Windows Hello secured SSH keys in WSL2
I'm confident about the security for my server, it's on a separate network, it has firewalls, and all the sorts of things you'd expect. SSH is available internally for administration, and that's dialled up on the security. If some ne'er-do-weller connects to my network, they're not getting in. If somebody lifts my PC, that's encrypted, nobody is getting in. If some malware runs on my PC that can access my SSH keys? Ah well, that's a concern.
SSH keys allow for signing onto to a SSH host without any of this dirty mucking about with passwords. Microsoft and others are making a bit of a thing about passwordless logons recently, but as with many things, it's been an everyday thing with Linux for a long time. To secure these keys, we can make use of passphrases (a password for the passwordless logon), or a service known as an 'agent' that can be un/locked to access all keys. Well, I don't like typing in passwords more than once, it's a bore. I wanted to see if I could find a method of securing my keys without the use of a password and without being vulnerable to something nefarious running on my PC.
Windows Hello & YubiKey
I started with Windows Hello. Windows Hello is what Microsoft bundle all the advanced sign-in features with Windows (face unlock, pictures, biometrics etc). I figured that if Windows supports this sort of thing out the box, there might be a way of tying that together with SSH. First off, I needed to get some Windows Hello support for my aging Windows 10 PC, so for this I bought myself a Yubikey 5 (the nano version, it just sits in the front USB port of my PC under my desk in arms reach). This is a cryptographic key that supports a few technologies and works with Windows Hello. It has a touch-sensitive button on the front that is used to confirm you (somebody) is at the device. The idea is, you can put this on a keyring and take it around with you and act as the "something you have" factor in security (instead of phones which require charged batteries and what not).
OpenSSH keys
To leverage the Yubikey with an SSH key required a few steps. First off, we need a more recent version of the OpenSSH programs that Windows (10 at least) ships with. I'm not sure what version of OpenSSH Microsoft introduced this support, so on a bang up-to-date version of Windows 11, or whatever is out when you read this, it might not be a prerequisite step. To update to the latest beta, we can run this command from an Administrative command prompt:
winget install --id=Microsoft.OpenSSH.Beta
This will require a reboot after installing to take effect.
We then need to create a key pair for SSH in a command prompt or powershell:
ssh-keygen -t ed25519-sk
Here we're using ed25519 as the key type, which is the cutting edge for OpenSSH at the time of writing. The important bit is the "-sk" variant; -sk variants were introduced in OpenSSH 8.2 to suport U2F/FIDO security keys, which the Yubikey is. As per a normal key, there will be two files created, id_ed25519-sk and id_ed25519-sk.pub, with the latter being the public part that you can add to your server's authorized_keys file. The major difference is, the private key file doesn't work when the security key isn't present. You can copy the key elsewhere, but it doesn't do anything without your hardware key being present.
Once you've added the public part to your remote server, that's it. When you try to ssh from Windows to the server, you'll be prompted to touch the key to confirm your presence. You can still have a passphrase on it as well if you want to multi-factor your key, but that's kinda defeating the point.
Joining WSL2 to Windows' SSH-Agent
But I don't like powershell, or command line. I use WSL for most of my terminal work. So how do I join this all up to work with WSL2?
This is where we need to use an SSH-Agent; an agent is a process that runs in the background that has your keys and can be contacted to request private keys. What's really magical about this is, it can also be chained between sessions, so if you're using a jump box to get network access to another set of servers, your keys don't need to be stored on that remote box (which you can't easily plug a USB security key into).
The OpenSSH programs in Windows installs an agent service, make sure yours is running and set to start on boot in the services mmc, or with the command sc query ssh-agent. You can set the service to start on boot with sc config start= boot.
With your agent running, you can add your private key into the agent by just running "ssh-add", which will add all the available private keys into the agent. At this point, it's perfectly ok to delete the private keys from your %USERPROFILE%\.ssh directory, they're redundant now. Just as before, when connecting to a remote host, you'll be prompted to confirm your presence.
To wire up WSL2, you'll need a helper to forward requests back to Windows. For this, I used wsl2-ssh-agent, which provides a standalone binary to handle requests by creating a pipe in the .ssh directory of WSL which the ssh client can connect to. Installation is simple, from your home directory, simply download and mark as executable:
curl -L -O https://github.com/mame/wsl2-ssh-agent/releases/latest/download/wsl2-ssh-agent chmod +x wsl2-ssh-agent
And then edit your .bashrc (or relevant shell runcom file) to start it:
eval $($HOME/wsl2-ssh-agent)
Re-logon to your WSL2 shell and try it out. You should get your prompt to touch the key when connecting to your remote server.