SSH authentication with a YubiKey / FIDO2 hardware token – easy, portable & touch-free

The first thing I wanted to do when getting a YubiKey was to use it for SSH authentication with the servers I manage. My goal was to be able to login from any machine with an SSH client provided I had my YubiKey with me. When I searched for how to set up SSH login with a YubiKey, the instructions seemed quite daunting – I was hoping for a ‘native’ way to do this without installing new software or fiddling with your SSHD configuration. Thanks to the OpenSSH 8.2 release in February 2020, there is! The below instructions talk about using a YubiKey, but should work for any FIDO2 hardware token.

Check OpenSSH version on the client and server side, and upgrade if needed

You can check your SSH version by running ssh -V at the shell; for these instructions to work you’ll need version 8.2 or later.

If you’re running a recent Linux distro (e.g. Debian Bullseye, Ubuntu 20.04) you may be fine, but if not you’ll need to upgrade the OpenSSH packages and restart the ssh daemon.

On macOS Big Sur (11.5.2 at the time of writing), you’ll only have OpenSSH 8.1 so will need to install a newer version. Install Homebrew (if you haven’t already), and then run brew install openssh to get the latest version. You may want to close and open a new terminal and run ssh -V again to check that it runs the new version. If it’s still showing the old one, when following the below instructions you will need to specify the path to the new version installed by Homebrew which will be at /usr/local/bin/ssh for the SSH client and /usr/local/bin/ssh-keygen for the key generator.

Generate your key pair

Plug in your YubiKey and run the following command to generate a key pair using the hardware token:

ssh-keygen -t ed25519-sk -O resident -O no-touch-required

To explain the parameters:

  • -t ed25519-sk
    There are two key types supporting hardware tokens, both ending in -sk. ed25519-sk is the more robust one, but if this doesn’t work see the note at the bottom
  • -O resident (N.B. that’s the letter ‘O’ for option, not the number 0!)
    You may recall my goal was to need nothing more than the YubiKey to login from any machine, so for this we need to generate a ‘resident’ key, i.e. one that stays on the YubiKey and can be extracted/recreated on a new machine when needed.
  • -O no-touch-required
    This is for touch-free authentication, useful for any automation you want to run unimpeded when you’re not there. You may drop this parameter if you prefer to touch the YubiKey to confirm your login.

Setting a PIN

You’ll be prompted to touch your YubiKey to begin the key generation (regardless of the last parameter, as that only affects the key behaviour after generation) and will also be prompted for a PIN number. A YubiKey will work without a PIN (the default on a YubiKey is no PIN) – but having no PIN may prevent you from retrieving your resident key on another machine (I tried; ssh-keygen failed saying no PIN was provided). For now, you can simply press enter if you don’t have a PIN, and set one later. You will also be prompted to create a passphrase just like a regular SSH key – you can just press enter to generate a key without one.

I would recommend setting a PIN (you can do this after key generation without affecting your stored keys) by using the Yubikey Manager and going to the FIDO2 page under applications.

As we have not specified a file location, the key will be written to the default location which will be ~/.ssh/id_ed25519_sk for the private key and ~/.ssh/id_ed25519_sk.pub for the public key. You may add the -f parameter (in the format -f /path/to/file) to ssh-keygen to specify a location for the keys.

Install the public key

The public key file will contain a single line in the format you’re used to except that it’ll start with sk-ssh-ed25519@openssh.com instead of ssh-rsa. Login to your server (with your old SSH key?) and append the line to ~/.ssh/authorized_keys but insert the option no-touch-required at the start of the line – so the line will looks like this:

no-touch-required sk-ssh-ed25519@openssh.com AABCD...

Of course, if you dropped the no-touch-required parameter at the key generation stage, you won’t need this. If you generated a no-touch key but fail to specify this option in your authorized_keys file, login will fail – your auth.log will show “rejected: user presence (authenticator touch) requirement not met” – because SSHD by default expects user presence to be verified by touch. If you don’t control the server, this may also fail if your system administrator has set touch-required in the SSHD configuration, as this will override anything set at the user level.

You’re set!

Don’t remove your old public key from authorized_keys yet; to be on the safe side I would recommend staying logged in, and then opening another terminal session and attempting SSH login with your Yubikey. If your generated key was put in the default location, then all you need is:

ssh username@server.address

If it works, you can go ahead and remove the line with your old SSH key from authorized_keys, if you’re happy to rely on your YubiKey from now on. I’d worry about the single point of failure though, so I’d highly recommend generating a second key pair with a backup YubiKey and installing that on your server too.

Using your key on a different machine

Now, what if you actually need to login from a different machine? Simple! Plug in your YubiKey, and then you can either add your private key to ssh-agent by running this:

ssh-add -K

Or write a private key and public key file to your current directory by running this:

ssh-keygen -K

Things to watch out for

You will need a more recent YubiKey (5.2.3+ firmware) that supports FIDO2 to create an ed25519-sk key pair. For older YubiKeys you should be able to follow these instructions using the ecdsa-sk key type (change the parameter at the key generation stage).

If you don’t need the OTP feature and you’re getting annoyed by accidental touches triggering OTP entry, you can turn this off using the YubiKey Manager, or set this to only trigger on a long press instead of a short one.

Leave a comment