This article describes how to secure your SSH host keys using hardware tokens.
Background
I have previously written numerous posts about strong user authentication using smart cards, yubikeys and/or OpenSSH certificates. I have also described how you can use OpenSSH certificates to authentcate hosts. But let say your server where compromised in some way just for a few hours and your SSH host keys (and certificates) where stolen. This would allow the attacker to perform MITM attacks or impersonate your server in several different ways. Depending on the application this could be more or less detrimental. If the server have a lot of users this could be used to steal passwords for example.
One way to mitigate this attack is to store your host keys on hardware tokens. This way they cannot be stolen by someone not in physical contact with the server, and you would easily find out if the token was missing.
Configure the token
I will use a Yubikey 4 for this. I have already described how to use yubikeys for client keys, so a more detailed description on how to configure and use Yubikeys for SSH can be found here. You can check that everyting is working like this:
# opensc-tool -l # Detected readers (pcsc) Nr. Card Features Name 0 Yes Yubico Yubikey 4 OTP+U2F+CCID 00 00
What we need is basically a pkcs11 capable device i.e a smart card or “similar” and create a RSA key pair on it.
Configure sshd and setting up a ssh-agent
Since it would be extremely impractical to enter the token PIN every time a client connects to your secure server, we will need to use a ssh-agent that will keep the pin in memory as long as the agent process is running. We also need to enter the PIN before we leave:
# ssh-agent -a /root/yubikey-agent setenv SSH_AUTH_SOCK /root/yubikey-agent; setenv SSH_AGENT_PID 10894; echo Agent pid 10894; # setenv SSH_AUTH_SOCK /root/yubikey-agent; # ssh-add -s /usr/local/lib/opensc-pkcs11.so Enter passphrase for PKCS#11: Card added: /usr/local/lib/opensc-pkcs11.so # ssh-add -l 2048 SHA256:qBbMpdbUeabLe4PnfjrjPbGPu8zfbkbK+ni4mXOnV24 /usr/local/lib/opensc-pkcs11.so (RSA)
Now you have your RSA key available to the system using the UNIX-domain socket at /root/yubikey-agent.
In a production situation I would put this in a rc-script and enter the PIN at boot-time.
Configure OpenSSH
We need to create a file containing the public key and also tell sshd to use this key and the newly created socket as a backend for its host keys.
# ssh-keygen -D /usr/local/lib/opensc-pkcs11.so > /etc/ssh/yubikey_host_key.pub # echo "HostKey /etc/ssh/yubikey_host_key.pub" >> /etc/ssh/sshd_config # echo "HostKeyAgent /root/yubikey-agent" >> /etc/ssh/sshd_config
Notice that if you have several keys on your token, ssh-keygen will output all of them. Make sure only the correct keys are added to the key file.
Verify the configuration
When you have configured sshd you will need to restart sshd and them we can verify that host keys are actually from the hardware token (in this case the yubikey). A very easy way to do this is to actually try to connect to the server and have a look at what keys it presents to you.
% ssh server The authenticity of host 'server (172.25.0.15)' can't be established. RSA key fingerprint is SHA256:qBbMpdbUeabLe4PnfjrjPbGPu8zfbkbK+ni4mXOnV24. No matching host key fingerprint found in DNS. Are you sure you want to continue connecting (yes/no)?
You can now easily verify that this fingerprint IS actually the same as the one ge got when we added the yubikey to the ssh-agent.
This is basically it. If you are going to use this in production you would probably want to add some rc-scripts to automate the setup process as much as possible.