Blog

A Better Windows 10+WSL SSH Experience

Dec 3, 2019 | 5 minutes read

Tags: Security, Howto

Windows 10 ships with two awesome features for users and developers who still work in Linux land. The first is the Windows Subsystem for Linux, which implements a subset of the Linux Kernel’s system calls to allow you to run native Linux userland utilities such as bash and friends, and ssh. Windows 10 also includes a native build of OpenSSH, which means you don’t even need to use WSL to SSH into your other machines! Unfortunately, neither option has any ssh-agent running by default, so if you have passphrase protected keys (which you should!), then you’ll need to enter the passphrase every time you want to use them. Let’s fix that!

First, you’ll need to decide whether you want to use Pageant or the native SSH Agent. The main reason to use Pageant is if you use putty-cac for interfacing with non-PKCS#11 smart cards and certificates that are only available via the Microsoft CAPI (including the TPM-backed certificates from my previous post)1 2. I find Pageant to be less stable and user-friendly than the traditional SSH agent, but you can use either.

Configuring the OpenSSH SSH Agent

Optional: Update Native OpenSSH

This step is optional. The version of OpenSSH shipped with current builds of Windows 10 (1909 as of this writing) is a few versions out of date, and more importantly was built before Microsoft enabled PKCS#11 support. As a result, if you’re using a Yubikey for SSH, you need to do this step.

First, you’ll need to remove parts of the preinstalled OpenSSH build as they’ll conflict with the new one. Remove the preinstalled OpenSSH build from your PATH by going to the Environment Variables editor under System Properties, selecting PATH under the machine variables, clicking Edit, and deleting the OpenSSH entry. Then, run sc.exe delete ssh-agent in an Administrator command prompt to delete the existing OpenSSH Agent service.

Now, you have two options for updating. First, you can go to the release page, download, and install it. Make sure you choose to install the SSH Agent if you are given the choice. Alternatively, you can use the Chocolatey package manager, which is my preferred way of doing it because it makes keeping up to date easier. If you go that route, make sure you install with the command choco install openssh -params "/SSHAgentFeature" to tell it to install the SSH Agent.

Enabling the SSH Agent and Adding Keys

Whether or not you choose to update, you need to enable the SSH Agent service as it ships disabled by default. Go to Services, find the ssh-agent (it may also show up as “OpenSSH Agent”) service, and change its startup setting to Automatic, then start it manually (or reboot). Then you can simply ssh-add your keys. You’ll be prompted for the passphrase for them, and then the agent will store the key data in the Windows Registry encrypted with a key derived from your account credentials. After that, you won’t need to enter your passphrase anymore!

Passing the SSH Agent to WSL

But what if you want to use WSL’s ssh instead of Windows’s? The Win32 OpenSSH Agent uses Windows named pipes to talk to ssh.exe, which no WSL process can interact with. You’ll need a program to act as a shim between Unix-style sockets and Windows named pipes, and thankfully one exists! Download the latest release, unpack it somewhere you won’t accidentally delete it, and create a shortcut to it in SHELL:STARTUP (you can just type that into an Explorer address bar). Then modify the shortcut to include the following command line arguments (at the end of the Target field): -setenv -envname WSL_AUTH_SOCK and run it. This will tell it to export an environment variable named WSL_AUTH_SOCK3 to all new process containing the location of a Unix domain socket it creates; the program acts as a translator between that socket and the Windows OpenSSH Agent’s named pipe. Then, add the following line to your .bashrc (or your shell’s equivalent):

1
[ -n ${WSL_AUTH_SOCK} ] && export SSH_AUTH_SOCK=${WSL_AUTH_SOCK}

This will cause the shell to set the $SSH_AUTH_SOCK variable with the location of the translator socket. Once that happens, all the WSL ssh binaries will automatically talk to the Windows OpenSSH Agent. Tada!

Using Pageant Instead

If you’re here, I’m going to assume you already have Pageant (putty-cac’s or regular) installed and configured how you like, including starting up automatically. This will just explain how to get it to talk to ssh.exe and WSL. For that, you need to use wsl-ssh-pageant. Download the latest release and put it somewhere you won’t accidentally delete it, then create a shortcut to it in SHELL:STARTUP (you can just type that into an Explorer address bar). After that, modify the shortcut to include the following command line arguments (at the end of the Target field): --systray --wsl C:\ssh-agent.sock --winssh ssh-pageant (you can use any path less than 100 characters for the socket file) and run it.

Now we need to tell ssh.exe and WSL’s ssh how to find the Pageant agent shim. For ssh.exe, go to the the Environment Variables editor under System Properties and add a new variable to your user named SSH_AUTH_SOCK, and give it the value \\.\pipe\ssh-pageant. For WSL ssh, add the following to your .bashrc (or the equivalent for your shell):

1
export SSH_AUTH_SOCK=/mnt/c/ssh-agent.sock

If you put the socket in a different location, change the line to point there (remember to convert the path to one WSL will understand).

After that, you’ll be able to use both the native SSH client and the one in WSL with Pageant as your SSH agent.


  1. The Microsoft team responsible for handling the Windows port of OpenSSH have indicated they intend to support this, but it was supposed to come “by years end” in 2016, so ¯\_(ツ)_/¯ ↩︎

  2. There are at least two (one; two) projects to create PKCS#11-to-CAPI translation layers, but they are both rather dead and I haven’t tested them. ↩︎

  3. We do not export SSH_AUTH_SOCK directly so as to not clobber the Windows one — ssh.exe doesn’t understand Unix-style sockets and wouldn’t be able to talk to the SSH Agent if we did. ↩︎