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.
NOTE: I have started rewriting this post over on my wiki, so that anyone can update it. Newer instructions will be added there; this post will not see further updates. (2017-10-25)
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.
[toc]
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
Long version:
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 /etc/X11/Xsession.options
(as 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.
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 %appdata%\gnupg
(or 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.
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.
Setting up public keys / authorized_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).
SSH Agent within screen
sessions
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 .profile
:
# 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 .screenrc
:
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.
SSH Agent through sudo
By default, 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 sudo
?
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)
You seem to be having the exact same problems I’m having re: launchd and ssh. I get the same freezes when I unplug my reader (Gemalto shell token, same as yours). I’ve found that killing scdaemon is enough to stop the freezes from happening, but like you say, it’s necessary to start a whole new instance of gpg-agent to get it all working again.
If you come up with a good solution to the problem I’ll be very interested to hear.
If you are the Chris Boot who was at Village Camps in Leysin, Switzerland — I would love to hear from you.