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.
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
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:
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:
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
OpenSSH in your Windows directory. If you have this problem, consider setting the environment variable
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.
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
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: