KeePassXC in WSL2
Back in my native Linux days I got used to an SSH agent paired with a KeePassXC that manages my keys. I didn’t have to do much to get it to work, I just enabled SSH Agent integration in KeePassXC, enabled the SSH agent’s systemd service and things just worked.
In 2021 I decided to give Windows 10 and WSL2 a try. Replicating that setup proved to be quite a hassle.
I had KeePassXC running in Windows (because WSL2 can’t comfortably run GUIs just yet).
The SSH client I was meaning to use was in Ubuntu, running inside WSL2, and came from OpenSSH. Basically, the very same one I was using in Linux.
The overall setup is going to look as follows:
- KeePassXC for key management
- OpenSSH Authentication Agent service on Windows, updated to version 8
- WSL SSH Agent that “is a simple ‘notification tray’ applet which maintains AF_UNIX ssh-agent compatible socket on Windows end”
- npiperelay, “a tool that allows you to access a Windows named pipe in a way that is more compatible with a variety of command-line tools”
- socat that converts a named pipe to a Unix domain socket
ssh, OpenSSH client inside WSL
Because there was some rather unfortunate backtracking involved, I’ll be posting the shortest path that I eventually discovered.
So I started off by enabling the SSH agent integration, and checked the box for OpenSSH agent, since I was running an OpenSSH client, and thought I’d need a respective agent.
This is where I found that OpenSSH agent isn’t in Windows despite the rumors I heard. Well, okay I didn’t expect things to be that easy.
OpenSSH agent in Windows
I looked it up and found that OpenSSH has to be installed using “Manage optional features” in the system settings. Alright.
I opened that up and found two: “OpenSSH Client” and “OpenSSH Server”. That’s fair enough, OpenSSH implements both a server,
sshd, and a client,
ssh. You need “OpenSSH Server” at least because that’s what includes the agent.
Then I opened up “Services” settings in Windows 10:
- enabled and started “OpenSSH Authentication Agent” and set it up to start automatically
- disabled “OpenSSH SSH Server” (because I didn’t need it, and before enabling it I would at least review its configuration so make sure it’s secure; it’s a remote access server, not fun and games)
After a restart (and there have been a few throughout the process, I think changing the “optional features” needed this?) I could verify in
ssh-add -l was indeed listing my keys when KeePassXC is unlocked, and no keys when it’s locked. Half way there then!
Bridging the gap
So now we have a functioning SSH agent and an SSH client to connect it to. How hard can it be?
The hard work has already been done by the authors of WSL SSH Agent and npiperelay, so you mostly just have to install these. WSL SSH Agent already includes npiperelay in the release archive, so no, you don’t have to build npiperelay, although you could, to boost confidence that your keys are safe.
WSL SSH Agent has to be installed according to the README, which involves creating a shortcut to the executable with parameters and placing it somewhere where it will be auto-started
Now everything mostly comes down to putting this snippet somewhere that’s executed whenever you start a shell (
$WINTOOLS has to be replaced or defined with the Linux path where you put
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:"$WINTOOLS/npiperelay.exe -ei -s //./pipe/openssh-ssh-agent",nofork & \ ) >/dev/null 2>&1 fi
Here’s what it does:
$SSH_AUTH_SOCKis defined to the path where SSH agent’s socket is expected
ss -a’s output is inspected for presence of an active socket on that path
- If the socket is there, everything should be set!
- …but if it isn’t, whatever file is at that path currently is removed (not sure why, but
socat’s docs suggest that it might be left hanging even after the socket is closed) and to create it,
setsidis used to start a new session, where:
//./pipe/openssh-ssh-agent, the named pipe exposed by WSL SSH Agent, is converted into a more Linux-friendly format via
socatstarts, creating a Unix socket at
$SSH_AUTH_SOCKand connecting it to the aforementioned
npiperelayprocess (it actually starts that process via
EXECthen connects to it)
socatis started with
setsid(ok) and a
&at the end (not sure it’s necessary…), it does not block the starting shell and keeps on living even when that shell dies
- all the outputs of
setsidare discarded (redirected to
/dev/nullthe “black hole” of Unix): stderr is forwarded to stdout via
2>&1, a rather common idiom, and stdout is discarded directly
…and this is it. Try
ssh-add -l in
cmd and in WSL.
After everything was set up, I also updated OpenSSH to version 8.0, because a warning was popping up as I was testing the final setup:
warning: agent returned different signature type ... (expected ...)
I do not take credit for finding this solution, it’s an aggregation from the following places, in order of finding: