DEV Community

Cover image for Using Windows SSH Agent in WSL2: A Complete Guide
Andrew Johnson
Andrew Johnson

Posted on

Using Windows SSH Agent in WSL2: A Complete Guide

If you're using Windows Subsystem for Linux (WSL2) for development, you've probably encountered the challenge of managing SSH keys across your Windows host and Linux environment. I used to have separate keys on all of my hosts which quickly became a nightmare to manage. That is until I discovered 1Password, by far my favorite password manager that I share with my entire family and have been doing so for almost five years. 1Password provides a native SSH Agent that makes all of this seamless, secure, and portable on your native systems. While you could maintain separate SSH keys in both systems, there'a a more elegant solution: passing your Windows SSH agent into WSL2.

This guide will show you how to use your Windows SSH agent (whether it's 1Password, Windows native OpenSSH Agent, or another solution) within your WSL2 environment. This approach keeps your SSH keys secure on your host system while making them available to your WSL2 instance.

Prerequisites

  • Windows 10/11 with WSL2 installed
  • An SSH agent running on Windows (this guide uses 1Password, but the process is similar for other agents)
  • Basic familiarity with the command line

Step-by-Step Guide

1. Configure Your Windows SSH Agent

First, ensure your SSH agent is properly configured on your Windows host system. If you're using 1Password (like I do), make sure you have:

  • 1Password installed and running
  • SSH keys added to 1Password
  • 1Password SSH agent enabled

For the Windows native SSH agent, ensure the service is running and your keys are added.

The 1Password Settings Developer Menu

2. Clone npiperelay

npiperelay is a crucial tool that helps bridge the Windows named pipe to WSL2.
In your WSL2 terminal:

git clone https://github.com/jstarks/npiperelay
Enter fullscreen mode Exit fullscreen mode

3. Install Go

npiperelay is written in Go, so we'll need to install the Go language to compile the binary from source:

# As of writing, Go 1.24.0 is the latest version, so we will use that.
wget https://go.dev/dl/go1.24.0.linux-amd64.tar.gz
# Remove any previous version of Go and extract the tarball into /usr/local, you may need sudo for this.
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.24.0.linux-amd64.tar.gz
# Add Go to your PATH
export PATH=$PATH:/usr/local/go/bin
# This should print out the Go version if successful
go version
Enter fullscreen mode Exit fullscreen mode

4. Build npiperelay and symlink

Now let's build the npiperelay binary and symlink it from our Windows filesystem to our WSL2 filesystem, this is the same way detailed in the project's README.

cd npiperelay
# Build the binary and store it in our Windows C: drive
go get -d github.com/jstarks/npiperelay
GOOS=windows go build -o /mnt/c/Users/<myuser>/go/bin/npiperelay.exe github.com/jstarks/npiperelay
# Create a symlink to a directory in our PATH
sudo ln -s /mnt/c/Users/<myuser>/go/bin/npiperelay.exe /usr/local/bin/npiperelay.exe
Enter fullscreen mode Exit fullscreen mode

5. Install socat

Socket Cat, socat, is a command line tool that establishes the connection between our WSL2 system and the Windows named pipe the SSH Agent listens on.

sudo apt install socat
Enter fullscreen mode Exit fullscreen mode

6. Set Up the Relay Script

Create a new script file somewhere you'll remember, I personally like to store these kinds of scripts in my ~/scripts directory. This script establishes the socket connection with your SSH Agent pipe and shoves it in the background.

#!/bin/bash
export SSH_AUTH_SOCK=$HOME/.ssh/agent.sock

# Check if the socket already exists
ss -a | grep -q $SSH_AUTH_SOCK
if [ $? -ne 0 ]; then
    # Start a new socat process
    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
Enter fullscreen mode Exit fullscreen mode

Let's break this script down so we know what we're getting into:
export SSH_AUTH_SOCK=$HOME/.ssh/agent.sock

This sets the SSH_AUTH_SOCK environment variable to point to a socket file in your home directory. This is where SSH clients will look to communicate with the SSH agent.

ss -a | grep -q $SSH_AUTH_SOCK
Enter fullscreen mode Exit fullscreen mode

ss -a: lists all sockets on the system
grep -q: quietly searches for the socket path (it exits with 0 if found, 1 if not)
This line checks if the socket is already active, say you spin up more shells after your first one.

If the grep search we did does not find an existing active socket, then we execute the steps for starting a new socat process.

rm -f $SSH_AUTH_SOCK

This will remove any existing socket file that might be left over from a previous session.

(setsid socat UNIX-LISTEN:$SSH_AUTH_SOCK,fork EXEC:"npiperelay.exe -ei -s //./pipe/openssh-ssh-agent",nofork &) >/dev/null 2>&1
Enter fullscreen mode Exit fullscreen mode

This is where things get interesting with a complex command that sets up the relay:

  • setsid: starts the process in a new session, making it independent of the terminal
  • socat: creates a bidirectional data flow
    • UNIX-LISTEN:$SSH_AGENT_SOCK,fork: creates a Unix domain socket that can handle multiple connections
    • EXEC:"npiperelay.exe -ei -s //./pipe/openssh-ssh-agent: executes npiperelay to connect to the Windows SSH agent
      • -ei terminates on EOF when reading from stdin, even when there is more data to write
      • -s specifies the Windows named pipe to connect to
      • //./pipe/openssh-ssh-agent is the named pipe where the Windows SSH agent listens
    • nofork prevents socat from forking this connection
  • & runs the process in the background
  • >/dev/null 2>&1 redirects both standard output and errors to /dev/null

The -ei flag is crucial for proper process management in this setup. Here's why:
In a normal pipe relay scenario, npiperelay would continue running until it receives an EOF on its inputs and it finished writing all data in its buffers to the output.
However, with SSH agent communication, connections are often short-lived, the Windows named pipe remains open, and multiple processes might need to connect to the agent.
By using -ei we ensure that the relay process terminates when the SSH client disconnects, the resources are cleaned up, new connections are establishes cleanly, and we avoid accumulating zombie processes.

Without this flag you might end up with lingering npiperelay process that don't terminate properly, leading to resource leaks and potential connection issues.

7. Make it executable and add it to your RC

chmod +x ~/scripts/relay-ssh-agent.sh
Enter fullscreen mode Exit fullscreen mode
# Add this to your bashrc or zshrc
source ~/scripts/relay-ssh-agent.sh
Enter fullscreen mode Exit fullscreen mode

Testing the Setup

After completing these steps, restart your shell or source your shell RC. Try an SSH command like ssh-add -l to see all of your identities.
If everything is setup correctly, the SSH agent will list off all of your identities and their fingerprints.

SSH Command Showing Successful Agent Communication

Troubleshooting

If you encounter issues:

  • Ensure your Windows SSH agent is running
  • Check that the npiperelay binary is correctly built and linked
  • Verify the socat process is running: ps aux | grep socat
  • Check the socket exists: ls -l $HOME/.ssh/agent.sock

Why This Approach?

This solution offers several benefits:

  • Maintain SSH keys in one place (Windows)
  • No need to copy private keys to WSL
  • Works seamlessly with security tools like 1Password
  • Persists across WSL restarts

Conclusion

With this setup, you can maintain your SSH keys securely on your Windows host while seamlessly using them in WSL2. This approach is particularly useful for engineers who want to keep their key management simple and secure while working across both Windows and Linux environments.
Remember to replace any placeholder paths or commands with the actual values for your system. Now get out there and manage those keys!

Top comments (0)