Using Your SSH Keys in WSL in 2022

The Windows Subsystem for Linux (a.k.a. WSL) is, I have to say, the best thing to come to Windows in ages. A first-class way to run a real Linux distribution with solid integration? Yes, please. But there’s one problem with the out-of-the-box config. My SSH keys aren’t available, which makes Git and the like extremely annoying to use. So I set out to fix that.

SSH is not just a way to get access to the shell on a remote system. It’s a building block for all sorts of automations, such as Git, that can ride along on that connection.

To use SSH, you need to authenticate to the remote system. SSH keys—actually public/private key pairs — automate this. SSH uses your private key, stored on the client side, to authenticate to an SSH server that trusts your public key.

It’s such an important part of the Unix ecosystem that macOS and Linux distributions alike have first-class keychain support for SSH keys. When you log in, your keychain is unlocked, and your private keys stored there are then available to SSH sessions, securely.

On Windows, the SSH key picture has long been a bit more complex, requiring third-party software. But in 2022, that’s no longer the case for core Windows.

Using Windows’ Built-in SSH Support

Modern Windows ships with OpenSSH as an optional feature. It may even already be turned on. Search in Settings for “Optional features” and turn on “OpenSSH Client” if it isn’t already.

Keys are next. If you don’t already have SSH keys, you can generate new ones.

Even if you do have existing keys, it might be worth generating new ones, using the superior ed25519 algorithm. Or, you could do what I do, and create keys for every system, so you don’t have to copy private keys around and can remove individual system keys when necessary.

You can do this at the command line. I recommend setting a passphrase to avoid writing your private key unencrypted to a file, but don’t worry — you won’t need it often.

ssh-keygen -t ed25519

This will generate keys in .ssh in your home directory. The file ending in .pub is your public key, which you can set up with servers you want to access, such as GitHub.

The file that doesn’t end in .pub (e.g. id_ed25519) is your private key — don’t share that one.

At this point, you can use your new key. If you don’t have a specific SSH host to connect to, try this:

ssh [email protected]

If you used a passphrase, you’ll be prompted for it. (Of course, you don’t get a shell at GitHub. But if it takes your passphrase, you know you’ve set everything up right.)

But we don’t want to type passphrases all the time. That’s where an agent comes in.

The OpenSSH Authentication Agent

When we used SSH in the dark days of terminal-first Unix, we set up our sessions with the ssh-agent program. This background program was responsible for holding our SSH keys and doing the authentication for us, so we didn’t have to unlock them every time we wanted to use SSH. This required some fancy scripting in our shell startup scripts.

Windows has its own version of ssh-agent, now, too! And it’s much more convenient to use. In an elevated PowerShell, run this:

Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent

You can now use ssh-add to add your private key to the agent:

ssh-add .ssh\id_ed25519

If you had a passphrase, you’ll be prompted for it one last time as your key is installed into the agent.

Now try your SSH command from the last section again. You’ll get right in.

(If it doesn’t work, check that you’re using the correct SSH — Git for Windows includes its own, and you want to use the one from Windows. It’s in System32, OpenSSH in your Windows directory. If you have this problem, consider setting the environment variable GIT_SSH to %windir%\System32\OpenSSH\ssh.exe.)

You’ll never have to unlock that key again, even after you reboot. In fact, you can even delete the private key from your .ssh directory now, as the agent is keeping it.

A Little Help from Friends

If you hop into your WSL session now, you’ll find that while you probably have SSH tools available, they don’t know about the key you put into the agent.

This is because, in Unix land, ssh-agent uses a Unix socket for communicating with the agent. And on the Windows side, ssh-agent is using a named pipe.

Connecting the two gets us access to ssh-agent inside WSL. We can do that with the help of two tools working in concert: npiperelay running in Windows, and socat in Linux.

Installing npiperelay takes just a little bit, since it’s only distributed in source form. Thankfully, Go makes it easy to build.

winget install Go.GoLang.Go.1.19

Restart your shell to pick up the path change, then:

go install github.com/jstarks/npiperelay@latest

By default, this will build and install npiperelay into $env:HOMEPATH\go\bin.

Now, over in WSL, install socat. On Ubuntu, for example:

sudo apt install socat

You can now test out building a relay with this command, in WSL. (Make sure you’ve first started a new shell to pick up the PATH change for Go binaries, so that which will work properly.)

socat \
    UNIX-LISTEN:"$HOME/.ssh/wsl-ssh-agent.sock",fork \
    EXEC:"$(which npiperelay.exe) \
              -ei -s //./pipe/openssh-ssh-agent",nofork 2>&1 &

If all goes well, you can try to list your keys from WSL:

SSH_AUTH_SOCK=$HOME/.ssh/wsl-ssh-agent.sock ssh-add -L

(If it doesn’t go well, you may be running into an issue with SSH versions, particularly if you’re running on Windows 10. Try a newer release on the Windows side. You’ll have to restart services after installing; double-check the agent service in the Services app to make sure it’s pointing to the correct path afterward.)

Congratulations! You’ve started a relay to make your Windows SSH agent available in WSL.

You can make this start up with your WSL shells by using this script, which wraps the socat invocation with service management. Follow the directions at the top of the script, and set RELAY_BIN like this:

RELAY_BIN="$(which npiperelay.exe)"
 
Conversation

Join the conversation

Your email address will not be published. Required fields are marked *