TLDR:
A guide on how to setup a shared ssh-agent and ssh keys between a Windows host machine and a linux WSL (Windows subsystem for linux) instance. Using socat, npiperelay and some bash scripting one can use the windows SSH agent inside the WSL container, automatically with no additional authentication.
Disclaimer:
This is heavily (entirely?) derived from blog-posts/instructions from the following sources:
pscheit.medium.com/use-an-ssh-agent-in-wsl-with-your-ssh-setup-in-windows-10
stuartleeks.com/posts/wsl-ssh-key-forward-to-windows/
More updated fork of original npiperelay:
github.com/albertony/npiperelay
How to update Windows’ OpenSSH client:
github.com/PowerShell/Win32-OpenSSH
Not used but potentially interesting:
Use the windows OpenSSH directly from linux
Background:
Recently, I acquired a new computer with a fresh installation of Windows. While setting up my development environment, I took the opportunity to reevaluate best practices and eliminate past workarounds.
My primary development workflow revolves around Linux, and my go-to tools consist of the following:
- VSCode
- WSL2 - Ubuntu LTS
- Docker Desktop
- SSH keys for github and remote servers
It’s that last topic that is the point of issue of this blog post. Namely in 2023 what is the current best practice methodology for setting up SSH keys on Windows?
On the one hand we have Windows with their build-in SSH agent, allowing one to generate and add keys once, then letting the Windows registry store them forevermore. This is nice because it puts the keys behind Microsoft’s user authentication barrier, allowing for technically password protected private keys, and only have to enter the password once, for all time.
This may not be the most secure, but for my personal github repositories, it is pretty convenient.
However, since I do most of my development in Linux, I need to mirror all of my GitHub/remote server keys to my WSL instance. The common and robust approach is to use keychain in Ubuntu and add the private keys on WSL startup. Although this requires entering the SSH private key password(s) at each startup, it’s manageable if you don’t restart the computer frequently.
But what if we could automate this process and have the Ubuntu WSL instance communicate with the Windows OpenSSH agent to obtain the keys automatically?
It turns out this is possible with some configuration.
How:
The Windows OpenSSH agent uses a named pipe in order to communicate with ssh.exe and provide authentication. What is needed is a way to allow our WSL instance to talk to that pipe. Luckily there is a cool project that does that:
This program takes a Windows named pipe and converts it to a unix socket that can be referenced from within WSL. Then one just needs to reference that socket and we should have all out Windows keys in our WSL instance.
Unfortunately, depending on the versions of OpenSSH in Windows on the host machine, and the version in the WSl container there can be incompatibilities. This ends up giving funky behavior where ssh-add -l
correctly lists all the Windows SSH keys, but when testing with ssh -T [email protected]
it just does not work.
After struggling with this for a couple hours I realized that the Windows ssh client was on version 8.6 while the Ubuntu was on 8.9. This is somehow a version mismatch, and can be fixed by updating the Windows client/agent to the newest (beta) version.
There is a concern is how reliable method will be going forward.
Step-by-Step Instructions:
On the Windows side:
- Install the beta version of OpenSSH via elevated powershell:
1
2
3
4
5
6
7
8
9
10
11
# Uninstall the OpenSSH Client/Server if currently installed (if previously installed):
Remove-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
Remove-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
# Use winget to install the OpenSSH beta From microsoft:
winget install "openssh beta"
# Setup the OpenSSH service to auto start as normal.
Get-Service ssh-agent
Get-Service -Name ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
If you have not added keys previously add them now via
ssh-add
, and confirm they are added withssh-add -L
You should see the keys that you are expect.Build and install npiperelay as described here:
github.com/albertony/npiperelay
For the rest of these instructions I placed the executable in:c:/Users/username/.ssh/bin/npiperelay.exe
On the WSL side (Ubuntu 22.04):
- Create a symbolic link to the npiperelay.exe on Windows:
1 2
mkdir -p ~/.local/bin ln -s /mnt/c/Users/username/.ssh/bin/npiperelay.exe ~/.local/bin/npiperelay.exe
- Install socat:
1
sudo apt-get -y install socat
- Add the following to your .bashrc. This adds the symbolic link to your path, along with a helper script to start the pipe-to-socket connection on startup (as per github.com/albertony/npiperelay):
1 2 3 4 5 6 7
PATH=$PATH:~/.local/bin export SSH_AUTH_SOCK=${HOME}/.ssh/agent.sock ss -a | grep -q $SSH_AUTH_SOCK if [ $? -ne 0 ]; then rm -f ${SSH_AUTH_SOCK} ( setsid socat UNIX-LISTEN:${SSH_AUTH_SOCK},fork EXEC:"npiperelay.exe -ei -s //./pipe/openssh-ssh-agent",nofork & ) >/dev/null 2>&1 fi
- Confirm that your connection works with
ssh-add -L
andssh -T [email protected]
A possible alternative:
One of the options I found when I started going down this path was not using the ssh.exe from wsl at all. Instead one could run the windows SSH executables directly from linux. I initially ran into issues with this, but that may have been caused by the SSH version incompatibility previously mentioned:
Use the Windows OpenSSH directly from linux