SSH is one of the most important tools I use. It has tons of cool features for the power user. First off any serious use needs SSH keys and ssh-agent to go with that. Socks proxy forwarding is super powerful, as is SSH mutlihop. GitHub works nicely with SSH, and Ansible builds on SSH. And when you use Ansible, you probably want to throw in the control master configurations. With all this use it would be interesting to have some statistics on how many SSH sessions I actually initiate daily.
If you're in a similar position as I am, the SSH keys you use are pretty much the keys to the kingdom, and they should be protected. Passphrase protected keys on a encrypted hard drive is quite a good start. However, you could do something cooler, and arguably safer.I got a Yubikey 4 to try putting my SSH keys on it.
There seems to be a few ways of setting up SSH keys on the Yubikey 4. The most popular seem to be using GPG keys and gpg-agent for this. As I'm not a huge fan of this approach, I was excited when I found this blog post. This sounded like a much nicer approach. Basically it "misuses" the certificate functionality to store SSH keys. I'm repeating half of that blog here, but for my own use-case.
These are the instructions for Ubuntu (15.10 at the time of writing).
If you're on a new enough Ubuntu (16.10?) just install it from the repos.
aptitude install yubikey-piv-manager opensc
If you're on an older Ubuntu, download the yubikey-piv-tool deb package from the newest Ubuntu, and install it (YMMV, worked in 15.10).
You'll want the new version to support all the options you need. The manual can be found here.
Let's start by personalizing the Yubikey. You'll need to change the PIN and PUK codes. Leaving them as the default would kind of make the whole exercise pointless. You might also want to change the management key. You should look at the previously linked Yubikey command line guide for the specifics.
The PIN and PUK can't be more than 8 characters, but they can have other characters than numbers (special characters too). Yes, you have to give the new secrets on the command line in plain text, so hit space one on your prompt before pasting this. This way it won't be saved in your history.
yubico-piv-tool -a change-pin -P 123456 -N newpin
yubico-piv-tool -a change-puk -P 12345678 -N newpuk
Creating RSA keys
Now to the fun part, key generation. There are two important options you'll need to be aware of before we start, pin-policy and touch-policy.
Pin-policy can be set to "never", "once",or "always". This determines if the PIN must be entered when using the key. You should NOT set it to never, otherwise anybody can grab the Yubikey and use it. The only realistic solution for me is "once" (when loading the key to ssh-agent) , since e.g. Ansible can use your SSH key 400 times in 10 minutes. I guess "always" can be used if you're really paranoid, but not for my use case.
Touch-policy can be set to "never", "always", "cached". This decides if you need to touch the Yubikey to use the key. "always" won't work for the tons of connections Ansible uses, "cached" only caches it for 15 seconds, so let's go with "never" here. If you have a SSH jump host, I do see benefit to use a separate SSH key (different certificate slot, see below) with the "always" touch-policy, and use SSH control master settings to avoid having to touch the Yubikey too often.
Let's DO IT already
Yeah, yeah, here are the commands for the impatient. Remember to add a space before any line with your PIN on it, so it doesn't get saved into history.
yubico-piv-tool -a generate --pin-policy=once --touch-policy=never -s 9a -o public.pem
yubico-piv-tool -a verify-pin -P my_pin_code -a selfsign-certificate -s 9a -S '/CN=touch SSH key/' -i public.pem -o cert.pem
yubico-piv-tool -a import-certificate -s 9a -i cert.pem
The "-s 9a" flags are which certificate slot we use on the key. More info in the manual. Please note that if you set the touch-policy to something else than "never", you'll need to touch the Yubikey after running the selfsign-certificate.
We generate private and public keys (RSA 2048 bit by default), then self sign a certificate and add the certificate to the Yubikey to make it happy. Yubikey needs a certificate so that it lets you use the associated key. The certificate validity doesn't matter here, since we don't use the certificate. The key will keep on working with an expired certificate.
We're almost there, now we just need make SSH use the RSA key. First we need to get the public key.
ssh-keygen -D /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so -e
Note, the library path might differ on different distributions. And yes, for some reason you use pkcs11 with SSH by pointing to library.
Now you can deploy the public key to the servers you want it on. Then you can test that it works with
ssh -I /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so <targethost>
Did it ask for the PIN? Did you get in? Congrats!
Next we'll set up ssh-agent. NB! Not all key managers that support ssh-agent like functionality work. E.g. gnome-keyring-daemon did not work. The ssh-agent that comes with openssh works. I recommend starting ssh-agent in your login startup scripts (and disabling ssh functionality in other key managers), so that all your terminals can use the ssh-agent.
When you have ssh-agent running, just run
ssh-add -s /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
and Voilá! Your Yubikey gets used by ssh-agent! If you had a touch policy on your keys, you'll need to touch the Yubikey when you use ssh.
In practice you'll probably have the Yubikey in your (physical) keychain, and grab it when you leave the computer. What happens when you reconnect it? Well, the key doesn't work any more. That's probably the way you want it to work. Each time you plug in the Yubikey you need to enter the PIN once to use it. To solve this you could for example alias "yubiadd" to something like this.
ssh-add -e /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so; ssh-add -s /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
To re-add the key you need to unload it first, but these two commands should do the trick. Now you only need to run "yubiadd" and enter the pin each time you get to your computer.
After testing Yubikey+SSH at home, I feel like I'd like to use it at work. There are still some questions that only real use will answer. E.g. is the key handling in the Yubikey fast enough to handle the tons of new SSH sessions which Ansible starts? Does ssh-agent annoyingly lose contact with the Yubikey constantly for some reason? Well, only one way to find out!