Skip to content

Instantly share code, notes, and snippets.

@sdktr
Forked from Jaykul/Agent Passthru.md
Last active April 10, 2025 08:08
Show Gist options
  • Save sdktr/dd10b0a187225b47c208c034190a7611 to your computer and use it in GitHub Desktop.
Save sdktr/dd10b0a187225b47c208c034190a7611 to your computer and use it in GitHub Desktop.
SSH Agent passthru to WSL 2 (working, in Windows 11, in April 2025)

SSH ssh agent /w WSL2

  • SSH agent running in Windows (e.g. Keeper or Keeper Commander)
  • The resulting PIPE in windows is relayed to WSL2 and made available as an SSH socket under the well known environment variable SSH_AUTH_SOCK
  • Additional filtering of the offered keys is applied on the SSH client side using ssh.conf

Installation

With Chocolatey, you must use an elevated PowerShell session. If there's no choco command found, it will fall back to winget for the npiperelay install. To force using Winget even if you have choco installed, you need to download it, so you can pass parameters to it.

Easy mode: just run this in PowerShell:

iex (irm https://gist.githubusercontent.com/sdktr/dd10b0a187225b47c208c034190a7611/raw/Install.ps1)

To be more cautious, click the Download zip button in the top right, unzip everything, read it through carefully, to make sure I'm not tricking you. Then run the Install.ps1 script from the unzipped files.

What does it do?

It's all designed to be run from PowerShell (5+) on Windows. When commands need to be run in WSL, it will use the wsl command to do so (as root and as you). Before you start, make sure you have wsl 2 and a distro installed (run wsl --list -v and pick a distro that has VERSION 2).

  1. Install npiperelay in Windows using chocolatey or winget
  2. Install socat in WSL using apt (sorry, if your distro isn't apt, please fork and comment 😉)
  3. Copy an ssh-agent-pipe script that starts npiperelay for you whenever you open bash
  4. Make that script executable
  5. Add that script to your .bashrc (note: better not to run this install multiple times 😝)
<#
.SYNOPSIS
Install npiperelay and socat and configure SSH_AUTH_SOCK forwarding
#>
[CmdletBinding()]
param(
# The distribution to connect the pipe to
[Parameter(Position = 0)]
$Distribution = "Ubuntu",
# The user for whom .bashrc should be modified
# Defaults to your username all lowercase
[Parameter(ParameterSetName = "Insecure")]
$Username = $Env:USERNAME.ToLower(),
# Ingore chocolatey for install (winget must be available).
[switch]$NoChocolate
)
# Install npiperelay
if (!(Get-Command npiperelay.exe -ErrorAction Ignore)) {
if (-not $NoChocolate -and (Get-Command choco -ErrorAction Ignore)) {
choco upgrade npiperelay -y
} elseif (Get-Command winget -ErrorAction Ignore) {
winget install --id=jstarks.npiperelay -e --accept-source-agreements
} else {
throw "Unable to install. Please download https://github.com/jstarks/npiperelay/releases/latest/download/npiperelay_windows_amd64.zip and extract it somewhere in your PATH"
}
}
# install socat in WSL
wsl -d $Distribution -u root apt install socat
if ($LASTEXITCODE) {
throw "Unable to install socat. I give up."
}
# create the ssh-agent-pipe script in WSL
# Ensure the carriage returns are correct (and fetch the script, if necessary):
$script = if (Test-Path $PSScriptRoot\ssh-agent-pipe.sh) {
(Get-Content $PSScriptRoot\ssh-agent-pipe.sh) -join "`n"
} else {
Invoke-RestMethod https://gist.githubusercontent.com/sdktr/dd10b0a187225b47c208c034190a7611/raw/ssh-agent-pipe.sh
}
# escape $ and " so we can pass this through bash
$script = $script -replace "\$", "\$" -replace '"', '\"'
wsl -d $Distribution -u root -- bash -c "cat > /usr/local/bin/ssh-agent-pipe <<'EOF'`n${script}`nEOF"
# Make it executable
wsl -d $Distribution -u root chmod +x /usr/local/bin/ssh-agent-pipe
# Add to .bashrc for the specified user
wsl -d $Distribution -u $Username -- bash -c "echo \`"source /usr/local/bin/ssh-agent-pipe\`" >> ~/.bashrc"
sdktr@WSL:~$ /usr/local/bin/ssh-agent-pipe -k
sdktr@WSL:~$ cat $HOME/.ssh-agent-pipe.log
2025-01-09 11:02:13 - Script started. SSH_AUTH_SOCK=/home/sdktr/.ssh/agent.sock
2025-01-09 11:02:13 - Raw sshpid: 5123
2025-01-09 11:02:13 - Killing existing socat process with PID: 5123
2025/01/09 11:02:13 socat[5123] W exiting on signal 15
2025-01-09 11:02:13 - Exiting due to -k flag.
sdktr@WSL:~$ /usr/local/bin/ssh-agent-pipe
sdktr@WSL:~$ cat $HOME/.ssh-agent-pipe.log
2025-01-09 11:02:33 - Script started. SSH_AUTH_SOCK=/home/sdktr/.ssh/agent.sock
2025-01-09 11:02:33 - Raw sshpid:
2025-01-09 11:02:33 - Starting socat process for SSH_AUTH_SOCK=/home/sdktr/.ssh/agent.sock.
2025-01-09 11:02:33 - Socat started successfully.
2025-01-09 11:02:33 - Script completed.
sdktr@WSL:~$ /usr/local/bin/ssh-agent-pipe -r
sdktr@WSL:~$ cat $HOME/.ssh-agent-pipe.log
2025-01-09 11:02:40 - Script started. SSH_AUTH_SOCK=/home/sdktr/.ssh/agent.sock
2025-01-09 11:02:40 - Raw sshpid: 6548
2025-01-09 11:02:40 - Killing existing socat process with PID: 6548
2025/01/09 11:02:40 socat[6548] W exiting on signal 15
2025-01-09 11:02:40 - Starting socat process for SSH_AUTH_SOCK=/home/sdktr/.ssh/agent.sock.
2025-01-09 11:02:40 - Socat started successfully.
2025-01-09 11:02:40 - Script completed.
#!/bin/bash
# ssh-agent-pipe: Manage an SSH agent socket relayed via socat and npiperelay.
# Usage: ssh-agent-pipe [ -k | -r ]
# Options:
# -k Kill the current process (if exists) and do not restart it.
# -r Kill the current process (if exists) and restart it.
# Default: Start the process only if it does not exist.
export SSH_AUTH_SOCK=$HOME/.ssh/agent.sock
LOG_FILE=$HOME/.ssh-agent-pipe.log
> "$LOG_FILE"
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
log_message "Script started. SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
if [ -z "$SSH_AUTH_SOCK" ]; then
log_message "Error: SSH_AUTH_SOCK is not set."
exit 1
fi
sshpid=$(ss -ap | grep -oP "(?<=users:\(\(\"socat\",pid=)\d+" | head -n 1)
log_message "Raw sshpid: $sshpid"
if [ "$1" = "-k" ] || [ "$1" = "-r" ]; then
if [ -n "$sshpid" ]; then
log_message "Killing existing socat process with PID: $sshpid"
kill "$sshpid"
else
log_message "No socat process found for $SSH_AUTH_SOCK."
fi
if [ "$1" = "-k" ]; then
log_message "Exiting due to -k flag."
exit
fi
unset sshpid
fi
if [ -z "$sshpid" ]; then
log_message "Starting socat process for SSH_AUTH_SOCK=$SSH_AUTH_SOCK."
rm -f "$SSH_AUTH_SOCK"
( setsid socat UNIX-LISTEN:$SSH_AUTH_SOCK,fork EXEC:"npiperelay.exe -ei -s //./pipe/openssh-ssh-agent",nofork & ) >> "$LOG_FILE" 2>&1
if [ $? -eq 0 ]; then
log_message "Socat started successfully."
else
log_message "Error: Failed to start socat."
fi
else
log_message "Socat process already running for $SSH_AUTH_SOCK."
fi
log_message "Script completed."
Additional method implemented to filter the keys that are offered as part of the SSH login, to avoid lockout due to too much keys offered.
```
# Windows (NON WORKING WITH ALL SSH clients, needs checking!)
PS C:\Users\myuser> cat .ssh/config
Host *
IdentityAgent \\.\pipe\ssh-agent
IdentitiesOnly yes
IdentityFile c:/Users/myuser/.ssh/mykey_key.pub
```
```
# WSL2:
myuser@WSL:~$ cat ~/.ssh/config
Host *
IdentityAgent $SSH_AUTH_SOCK
IdentitiesOnly yes
IdentityFile /mnt/c/Users/myuser/.ssh/mykey_key.pub
# points to my PUBLIC key in OpenSSH format, so that only that key is offered upon ssh negotiation
```
sdktr@PROX3-NB156637:~$ cat /mnt/c/Users/myuser/.ssh/mykey_key.pub
ssh-rsa AAAAB.....
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment