Note: In an older version of this article, some of the things stated here were wrong. Sorry for that.
I recently played with my Yubikey to establish them as second factor for my ssh keys. The process is straight-forward, however it took me some time to go through Yubico’s documentation. Here I write the process down in my own words.
ssh public key authentication can be hardened to require a hardware token like the Yubikeys (series 5 onwards). My Yubikey 4 is not supported, it’s too old - FIDO2 is required. From OpenSSH 8.2 and 8.3 respectively (for resident keys) onwards, yubikeys are supported. I do only require them for Linux, although the documentaton mentions some not-officially-supported-but-should-work methods to get it also working on Windows and MacOS.
Discoverable (resident) and non-discoverable (non-resident) keys
Before we start, there are two different keys: Discoverable (or resident) and non-discoverable (or non-resident) keys. Resident keys are stored on the Yubikey, while non-resident key use the Yubikey’s master key, but do not store anything on it. For ssh
, non-resident keys should be preferred because of two reasons:
First, they do not consume any of the sparse certificate slot on the Yubikey.
Second, if the Yubikey gets lost or stolen, an attacker cannot possibly obtain the ssh private key based on the Yubikey alone.
In the case of resident keys, the Yubikey uses PIV slots to store the ssh keys (RSA or EC). The Yubikey 5 has 25 slots available, but they are shared with other PIV certificates, so be careful to not waste them. Firstyear wrote this might become a problem, because for every passkey you will need one of those slots. Therefore again, non-resident keys should be preferred.
TL;DR
Tested on my openSUSE Tumbleweed laptop against a Leap 15.5 and a MicroOS server
Non-Discoverable (non-resident) key: Require additional identity file, do not occupy storage on Yubikey
Discoverable (resident) key: Can in principle be used anywhere, occupy one slot on the Yubikey per ssh key
Recommended: non-resident keys
- Insert your Yubikey (Firmware 5.2.3 or higher)
- Set a PIN for FIDO2 on the Yubikey
- Generate the key pairs:
Non-discoverable keys (recommended, more secure, cannot be used without the credentials id file, do not occupy slot on Yubikey):
$ ssh-keygen -t ed25519-sk
Discoverable/resident keys (will occupy one of the sparse credential slots on the Yubikey, not recommended)
$ ssh-keygen -t ed25519-sk -O resident -O application=ssh:my-hostname -O verify-required
- Transfer the public key to the remote host
REMOTE
$ ssh-copy-id -i ~/.ssh/id_ed25519_sk REMOTE
- Check if the login works by enforcing the new identity.
ssh
should ask you for your PIN:
$ ssh -i ~/.ssh/id_ed25519_sk -o 'IdentitiesOnly yes' REMOTE
And that’s it!
Step-by-step guide
Remote system configuration
this was not needed in my case. I leave it here for you, if you need it. See the official documentation for more information.
OpenSSH supports FIDO2 authentication by default since somewhere in OpenSSH 8, but it might not check for the user verification via PIN.
In case you run into trouble, add the following to your sshd_config
on the OpenSSH server end:
PubkeyAuthOptions verify-required
On the client side, you can also append verify-required
to an authorized_keys
entry:
sk-ssh-ed25519@openssh.com ENTRY verify-required
I did not need to do those changes on openSUSE Leap 15.5 and MicroOS.
Create Non-discoverable keys (recommended)
- Insert your Yubikey (Firmware 5.2.3 or higher)
- You probably need to set a PIN for the Yubikey, otherwise it doesn’t work
- Generate a new key using
ed25519
(orecdsa
if you really want. Replacemy-hostname
with your identifier
Note: Prefer ed25519
over ecdsa
because of concerns over the trustworthiness of NIST produced curves used in ECDSA.
ssh-keygen -t ed25519-sk
A key pair, id_ed25519_sk
and id_ed25519_sk.pub
is generated. The private key is not the actual private key, but part of the key generation process. This secret is used in conjunction with the Yubikey to generate the private key. This makes it more secure than resident keys, because you always need this identity key AND the Yubikey.
The public key id_ed25519_sk.pub
is the actual public key.
You can (and should) add an additional passphrase to further harden the ssh key.
Use ssh-copy-id
to copy the public key to a remote host, e.g. REMOTE
(the -i ...
option is optional):
$ ssh-copy-id -i ~/.ssh/id_ed25519_sk REMOTE
And then you can check the key login via the following - ssh
should ask for your PIN
$ ssh -i ~/.ssh/id_ed25519_sk -o 'IdentitiesOnly yes' REMOTE
Create discoverable keys
- Insert your Yubikey (Firmware 5.2.3 or higher)
- You might need to set a PIN for the Yubikey for FIDO2, otherwise it’s refused or not secure. Do it.
- Generate a new key using
ed25519
(orecdsa
if you really really want). Replacemy-hostname
with your identifier
$ ssh-keygen -t ed25519-sk -O resident -O application=ssh:my-hostname -O verify-required
This generates a new keypair ~/.ssh/id_ed25519_sk
and ~/.ssh/id_ed25519_sk.pub
. Note that the private key is mostly just a reference to the secret stored on the Yubikey, although it does not appear that way. The public key id_ed25519_sk.pub
is the actual public key.
From here, the rest is the same as above. Copy the key using ssh-copy-id
and then test it using the above stated arguments for ssh
.
List and delete keys (resident keys only)
Reminder: For ssh, non-resident keys should be preferred to save the sparse credential slots on the Yubikey. See Firstyear’s blog post on why this is an issue.
The Yubikey uses it’s PIV module to store RSA and EC keys used for ssh. Mine (Yubikey 5) has 25 slots available. Per ssh key one slot is occupied and you might need those slots also for other applications. If you want to really use passkeys, then you can only store 25 passkeys on a Yubikey, so don’t be wasteful. Use non-resident keys for ssh.
However, in case you still need resident keys, you can list the currently used certificates via
ykman fido credentials list
And to delete a certain credential the delete
subcommand with the corresponding key id, e.g. for da7fdc
ykman fido credentials delete da7fdc
Cave: The private key does not get deleted unless it is overwritten.
Outlook
Once I figured the difference between discoverable and non-discoverable keys and how they interact with ssh, it’s pretty much straight-forward to use a Yubikey to secure your ssh keys. I have this now setup for my primary Yubikey and one device and will need to do the same for my secondary computer (I use non-discoverable keys).
I think that Yubikey-based ssh authentication can improve the security. But before I’m comfortable to limit ssh authentication to this method and remove my old keys, I need a secondary Yubikey. I currently have a older Yubikey 4, which is not supported. And I do not feel comfortable to have only one Yubikey that opens important ssh connections. It’s a single point of failure (or loss), that would lock me out otherwise.
Also I noticed that it’s less convenient because I need to insert the Yubikey PIN for every ssh connection. ssh-add
allows to unlock the key passphrase, but still for every connection I get a PIN prompt. Perhaps there is a way to avoid this and unlock the Yubikey for a certain time, but that’s something that I still need to figure out.
For now this works pretty well, and I look forward to play a bit more with my Yubikeys.
Minus points for Azure
And 100 minus points to Azure, who still do not support ed25519 keys. Sorry, this annoys me that much, that I just had to rant about it.