Now that I’m using OpenPGP cards for GnuPG, I may as well start using them for their other bells and whistles too. The first and most useful such extra feature of those cards is using the authentication key for SSH.
Update 2017-06-28: You should also look at Lars Wirzenius’s post on Using a Yubikey 4 for ensafening one’s encryption.
Getting this working is actually surprisingly simple. You simply need to make sure you have an authentication-only RSA key loaded in the card’s 3rd (Authentication key) slot, and use
gpg-agent‘s SSH agent:
bootc@ripley ~ $ gpg2 --card-status Application ID ...: D276000124010200000500001BDE0000 Version ..........: 2.0 Manufacturer .....: ZeitControl Serial number ....: 00001BDE [...] Authentication key: 087A F7A1 0BA2 A8E2 7419 5FB6 3F19 5C4B 5DC6 DEA7 created ....: 2013-06-03 22:56:41 [...] bootc@ripley ~ $ killall ssh-agent gpg-agent bootc@ripley ~ $ unset GPG_AGENT_INFO SSH_AGENT_PID SSH_AUTH_SOCK bootc@ripley ~ $ eval $(gpg-agent --daemon --enable-ssh-support) bootc@ripley ~ $ ssh-add -l 4096 4b:92:1e:c9:3f:b2:72:74:13:78:94:43:48:d6:66:2b cardno:000500001BDE (RSA) bootc@ripley ~ $
If you followed my previous guide to setting up an OpenPGP card, you’ll already have the right key in the correct slot, so you’re all set.
The hard part isn’t getting the agent to talk to the card or anything like that, but using the correct agent when you login to your session. On Linux you’ll generally either get the normal OpenSSH
ssh-agent running, or maybe GNOME Keyring’s SSH agent, or some other agent from a variety of other packages; so we’ll need to work out how to disable those agents and use the GnuPG agent instead.
Table Of Contents
1. Linux: Starting the correct agent at login
TL;DR (quick setup):
$ echo enable-ssh-support >> ~/.gnupg/gpg-agent.conf # editor /etc/X11/Xsession.options < comment out "use-ssh-agent" > # aptitude purge libpam-gnome-keyring
When you installed the
gpg-agent package, it installed an Xsession script into
/etc/X11/Xsession.d/90gpg-agent that automatically starts the GnuPG agent for you. You probably already have the
gpg-agent running without knowing, so part one of the equation has already been sorted out for you.
Xfce4 also starts the agent for you in
/etc/xdg/xfce4/xinitrc in a slightly different way to the above, as long as you don’t already have an agent running. This is perfectly adequate for us as well.
By default, the gpg-agent doesn’t enable its SSH agent. You have to explicitly enable it yourself, either by passing
--enable-ssh-support on the command-line (which we could do by editing the Xsession script) or by adding a line to a configuration file. The latter is a much tidier way of doing this, so here goes:
bootc@ripley ~ $ echo enable-ssh-support >> .gnupg/gpg-agent.conf bootc@ripley ~ $
Next time we login and start the
gpg-agent, it will enable its SSH mode for us. Great!
Finally, we have the problem of running other SSH agents. The OpenSSH agent starts by default, set up by
/etc/X11/Xsession.d/90x11-common_ssh-agent from the
x11-common package. If you’re running GNOME or any of its services, it’s likely you’ll be running the GNOME Keyring agent as well. Both of those will interfere with our GnuPG agent.
Fortunately, disabling the OpenSSH agent is very simple: edit
root) and comment out the
use-ssh-agent line. Job done.
Disabling the GNOME Keyring agent is potentially a bit more complicated. I don’t know what the implications are of doing this if you’re actually running full-fat GNOME on your system, but as I’m using Xfce I can easily get away without. In my case, I had to un-install the
libpam-gnome-keyring package (
aptitude purge libpam-gnome-keyring as
root) and make sure that Xfce didn’t start GNOME services (Settings → Session and Startup → Advanced → Launch GNOME services on startup – untick if ticked).
Now you should be able to logout of your desktop session, login again, and immediately see your key loaded in your agent as long as your card is plugged-in:
bootc@ripley ~ $ ssh-add -l 4096 4b:92:1e:c9:3f:b2:72:74:13:78:94:43:48:d6:66:2b cardno:000500001BDE (RSA)
If that isn’t the case, first make sure your
SSH_AUTH_SOCK environment variable is pointing at your gpg-agent. You should see something along the lines of this:
bootc@ripley ~ $ echo $SSH_AUTH_SOCK /tmp/gpg-QDGtCE/S.gpg-agent.ssh
The important part is the last path element,
S.gpg-agent.ssh. If it doesn’t look like that, you may have another agent interfering with your GnuPG agent. The
SSH_AGENT_PID variable should contain the PID of the process acting as the agent, which may help pinpoint the culprit:
bootc@ripley ~ $ ps u -p $SSH_AGENT_PID USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND bootc 12731 0.0 0.0 19148 1144 ? Ss 11:04 0:00 gpg-agent --daemon --enable-ssh-support --write-env-file /home/bootc/.cache/gpg-agent-info
That’s it, your agent is running correctly.
2. Windows: Using PuTTY and Pageant (Updated 2013-09-28)
PuTTY and Pageant know nothing about smart cards at all. Since Gpg4win 2.2.0 (GnuPG 2.0.21), the gpg-agent can natively interact with PuTTY; you just need to explicitly enable PuTTY agent support and start the agent at login.
To enable PuTTY agent support in gpg-agent, you need to explicitly enable it. You can do this with GPA (GNU Privacy Assistant, part of Gpg4win): look in Backend Preferences in the Edit menu. Alternatively, you can manually edit the
gpg-agent.conf file. It usually resides in
C:\Users\%username%\AppData\Roaming); make sure the
gpg-agent.conf file has the line
enable-putty-support in it somewhere. Then, you have to restart the agent for this to take effect: open a command prompt and run
gpg-connect-agent killagent /bye to stop the agent, then
gpg-connect-agent /bye should start it again.
The final step in the puzzle is to get the
gpg-agent to start when you login to Windows, otherwise you need to do something GPG-related to get the agent to auto-start before you can SSH anywhere. You can do this by creating a shortcut to
"C:\Program Files (x86)\GNU\GnuPG\gpg-connect-agent.exe" /bye and placing it in your Startup program group in your Start menu. Changing the Run: setting from Normal window to Minimised makes it slightly less obtrusive at login.
Thanks to Stephanie Daugherty, who emailed me to tell me about PuTTY agent support having been added in GnuPG 2.0.21. Discussion ensued, and I updated this post in response (as well as my own SSH agent setup in Windows).
Below is older information that has been superseded by the above:
There is a fork of PuTTY known as KiTTY that includes some support, but requires additional PKCS#11 modules to function correctly, and I never really got on with it. By far the simplest method I found is to replace PuTTY’s Pageant with a custom version with smart card support. This has the advantage of working with other tools that can speak to Pageant like WinSCP. You’ll want the first of the pageant.exe downloads.
Note that there appears to be some issue with running both the custom Pageant program and GnuPG; you essentially have to choose between running one or the other. If you want to use GnuPG (via Enigmail in Thunderbird for example), you’ll need to close the Pageant program. If you then want to use your SSH key again, unplug your card reader, start Pageant again and plug the reader back in.
3. Mac OS X: GPGTools and SSH
The standard GPGTools distribution of GPG for Mac OS X, including the GPGMail plugin, works fine with the smart cards. Unfortunately, while the
gpg-agent has support for running from
launchd, the most recent GPGTools doesn’t install a LaunchAgent for it. I’m still trying to come up with a good way of getting this working properly; once I settle on a good solution I’ll post an update.
So far, I have written a custom LaunchAgent that runs
gpg-agent at login, but the agent appears to have some bugs related to USB and sleeping. When my laptop has been asleep for some time, the
gpg-agent seems to cause USB to lock up for seconds at a time, during which I can’t use the keyboard or touchpad. The only way to get thing to behave again is to kill
gpg-agent and re-launch it, but because of its
launchd integration you then lose access to the agent for SSH.
More to come once I’ve worked out a good way around these issues.
4. Setting up public keys /
You may have noticed that the output of
ssh-add -l isn’t useful for putting into
.ssh/authorized_keys. There is a slightly different option you can pass to
ssh-add to make it spit out the public key in a useful format:
bootc@ripley ~ $ ssh-add -L ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDxAZn1IQ2cBxIbgwksWOfkAMKKLa3cUYMkbQBaR9Nw4CfoLs8xiu0Kb8oN4JH6p+E4C1MrlmFQuMZbVzs9JseV2pe6kw0xKQgLINopyF6letzCOEzPH7THicxyQc35vMIa8JTAMU6X3hpzzSUVSQGKDljj+c4XayTZCVQVg2Yqc67Vdm+4q4OQCU7Fns73KWmqwsdYtuyk74yPWjAvKkEaW7I9d3TLKVI8LLdzC6FoP2jJyGEoqxWEf2yL0eWelmJi/ikLJFSdXvdVCzvyI3dTeNqEdaisKQ0SJ7W0ysH1Os2hYyxBazWonMYI/T8Sh9J21xcWGmBumFTIcsbLEP17tojR4ttFq69ebtJIMkbPo0e+u4gWdvM44MyWsDm8jkKDuqNcduGIhF0dFY57niq4TEv5+Yvya2gwqBS4ttq/NlUAseL4zAcaP+kpDae4GMiRXwpFAiKA3ctn6/gf5QLvcAHMz62ASHeo9gG9t6n0eGUzBD/lv0qMsaYgmxfgIpqoU6Sr1w2EVp8TYjIVAaO/96Kljb2v9mB+0/BTO7gxJicxUNYQLOhEYdMnbr0bFNAG93hlUiq5eGTTG7nn1mre2OHWyGB8fZN9EukbMeFicgFTxgl3ddQawjn1Qb6u//ZpSCD++IH4HQCjz1fI9r+yZ+6CqfUrM0PI+dwAfcL4pw== cardno:000500001BDE bootc@ripley ~ $
You can then put that into your
authorized_keys file and start using your OpenPGP smart card for password-less logins (you will be asked for your card PIN the first time you use your key in a session).
5. SSH Agent within
If you use
screen, you’ll know that
screen sessions will often out-live logins on your desktop.
screen will inherit variables passed to it at login so you can access your SSH agent within your
screen, but if you re-attach an old
screen to a new desktop session you’ll find your SSH agent no longer works because the
SSH_AUTH_SOCK is pointing to a long-gone socket.
The way I fix this problem is via some minor symlink trickery. I have configured my
.profile to create/update a symlink in
/tmp that always has the same name and points at the current SSH agent socket.
screen can then use the symlink in its
SSH_AUTH_SOCK variable, and always have access to the current agent.
Note that recent Linux kernels (3.6 onwards) have enabled certain protections for symlinks within world-writeable sticky directories (exactly like
/tmp), so we need to create a directory within
/tmp to put our symlink in instead. This is what my code does.
I added the following to my
# set up SSH agent socket symlink export SSH_AUTH_SOCK_LINK="/tmp/ssh-$USER/agent" if ! [ -r $(readlink -m $SSH_AUTH_SOCK_LINK) ] && [ -r $SSH_AUTH_SOCK ]; then mkdir -p "$(dirname $SSH_AUTH_SOCK_LINK)" && chmod go= "$(dirname $SSH_AUTH_SOCK_LINK)" && ln -sfn $SSH_AUTH_SOCK $SSH_AUTH_SOCK_LINK fi
And the following to my
setenv SSH_AUTH_SOCK "$SSH_AUTH_SOCK_LINK"
Now my screen sessions always use the same path, but the symlink changes when I login:
bootc@jiji ~ $ echo $SSH_AUTH_SOCK /tmp/ssh-bootc/agent bootc@jiji ~ $ ls -l $SSH_AUTH_SOCK lrwxrwxrwx 1 bootc bootc 30 Jun 9 12:08 /tmp/ssh-bootc/agent -> /tmp/ssh-xCF06knRMQ/agent.5587
Note that this also works for remote
screen sessions with SSH agent forwarding, not just local sessions.
6. SSH Agent through
sudo doesn’t pass through environment variables. Generally, this is a very good security measure, so you shouldn’t just go and disable this functionality. It protects against attacks using
LD_PRELOAD for example. That being said, wouldn’t it be great if our SSH agent worked from within
The good news is that it’s very easy to enable passing through of the
SSH_AUTH_SOCK variable only, and we can do it per-user or per-command as well rather than allowing just everyone to do this. My example (below) allows a single user,
bootc (me!) to pass-through the environment variable for all commands I run.
Simply create a file in
/etc/sudoers.d (I called mine
/etc/sudoers.d/bootc-ssh_auth_sock) that reads:
Defaults:bootc env_keep += "SSH_AUTH_SOCK"
You then need to make sure it has the correct permissions:
jiji bootc # chown root: /etc/sudoers.d/bootc-ssh_auth_sock jiji bootc # chmod ug=r,o= /etc/sudoers.d/bootc-ssh_auth_sock jiji bootc # ll /etc/sudoers.d/bootc-ssh_auth_sock -r--r----- 1 root root 43 Jun 9 12:06 /etc/sudoers.d/bootc-ssh_auth_sock
You can then see that everything works as expected:
bootc@jiji ~ $ echo $SSH_AUTH_SOCK /tmp/ssh-bootc/agent bootc@jiji ~ $ sudo -s jiji bootc # echo $SSH_AUTH_SOCK /tmp/ssh-bootc/agent jiji bootc # ssh-add -l 4096 4b:92:1e:c9:3f:b2:72:74:13:78:94:43:48:d6:66:2b cardno:000500001BDE (RSA)