#!/usr/bin/env bash | |
# Inspired by https://blog.nimamoh.net/yubi-key-gpg-wsl2/ | |
# Guide: | |
# Install GPG on windows & Unix | |
# Add "enable-putty-support" to gpg-agent.conf | |
# Download wsl-ssh-pageant and npiperelay and place the executables in "C:\Users\[USER]\AppData\Roaming\" under wsl-ssh-pageant & npiperelay | |
# https://github.com/benpye/wsl-ssh-pageant/releases/tag/20190513.14 | |
# https://github.com/NZSmartie/npiperelay/releases/tag/v0.1 | |
# Adjust relay() below if you alter those paths | |
# Place this script in WSL at ~/.local/bin/gpg-agent-relay | |
# Start it on login by calling it from your .bashrc: "$HOME/.local/bin/gpg-agent-relay start" | |
GNUPGHOME="$HOME/.gnupg" | |
PIDFILE="$GNUPGHOME/gpg-agent-relay.pid" | |
die() { | |
# shellcheck disable=SC2059 | |
printf "$1\n" >&2 | |
exit 1 | |
} | |
main() { | |
checkdeps socat start-stop-daemon lsof timeout | |
case $1 in | |
start) | |
if ! start-stop-daemon --pidfile "$PIDFILE" --background --notify-await --notify-timeout 5 --make-pidfile --exec "$0" --start -- foreground; then | |
# shellcheck disable=SC2016 | |
die 'Failed to start. Run `gpg-agent-relay foreground` to see output.' | |
fi | |
;; | |
stop) | |
start-stop-daemon --pidfile "$PIDFILE" --remove-pidfile --stop ;; | |
status) | |
start-stop-daemon --pidfile "$PIDFILE" --status | |
local result=$? | |
case $result in | |
0) printf "gpg-agent-relay is running\n" ;; | |
1 | 3) printf "gpg-agent-relay is not running\n" ;; | |
4) printf "unable to determine status\n" ;; | |
esac | |
return $result | |
;; | |
foreground) | |
relay ;; | |
*) | |
die "Usage:\n gpg-agent-relay start\n gpg-agent-relay stop\n gpg-agent-relay status\n gpg-agent-relay foreground" ;; | |
esac | |
} | |
relay() { | |
set -e | |
local winhome | |
local wslwinhome | |
winhome=$(cmd.exe /c "<nul set /p=%UserProfile%" 2>/dev/null || true) | |
wslwinhome="$(wslpath -u "$winhome")" | |
local npiperelay="$wslwinhome/AppData/Roaming/npiperelay/npiperelay.exe" | |
local wslsshpageant="$wslwinhome/AppData/Roaming/wsl-ssh-pageant/wsl-ssh-pageant-amd64-gui.exe" | |
local gpgconnectagent="/mnt/c/Program Files (x86)/GnuPG/bin/gpg-connect-agent.exe" | |
local gpgagentsocket="$GNUPGHOME/S.gpg-agent" | |
local sshagentsocket="$GNUPGHOME/S.gpg-agent.ssh" | |
# backslash escaping in socat EXEC doesn't seem to work very well, use forward slashes instead | |
# windows/npiperelay handle that just fine | |
local wingpgagentpath="${winhome//\\/\/}/AppData/Roaming/gnupg/S.gpg-agent" | |
killsocket "$gpgagentsocket" | |
killsocket "$sshagentsocket" | |
"$gpgconnectagent" /bye | |
"$wslsshpageant" --systray --winssh ssh-pageant 2>/dev/null & | |
# shellcheck disable=SC2034 | |
WSPPID=$! | |
socat UNIX-LISTEN:"$gpgagentsocket,unlink-close,fork,umask=177" EXEC:"$npiperelay -ep -ei -s -a '$wingpgagentpath'",nofork & | |
GNUPID=$! | |
# shellcheck disable=SC2064 | |
trap "kill -TERM $GNUPID" EXIT | |
socat UNIX-LISTEN:"$sshagentsocket,unlink-close,fork,umask=177" EXEC:"$npiperelay /\/\./\pipe/\ssh-pageant" & | |
SSHPID=$! | |
set +e | |
# shellcheck disable=SC2064 | |
trap "kill -TERM $GNUPID; kill -TERM $SSHPID" EXIT | |
systemd-notify --ready 2>/dev/null | |
wait $GNUPID $SSHPID | |
trap - EXIT | |
} | |
killsocket() { | |
local socketpath=$1 | |
if [[ -e $socketpath ]]; then | |
local socketpid | |
if socketpid=$(lsof +E -taU -- "$socketpath"); then | |
timeout .5s tail --pid=$socketpid -f /dev/null & | |
local timeoutpid=$! | |
kill "$socketpid" | |
if ! wait $timeoutpid; then | |
die "Timed out waiting for pid $socketpid listening at $socketpath" | |
fi | |
else | |
rm "$socketpath" | |
fi | |
fi | |
} | |
checkdeps() { | |
local deps=("$@") | |
local dep | |
local out | |
local ret=0 | |
for dep in "${deps[@]}"; do | |
if ! out=$(type "$dep" 2>&1); then | |
printf -- "Dependency %s not found:\n%s\n" "$dep" "$out" | |
ret=1 | |
fi | |
done | |
return $ret | |
} | |
main "$@" |
This comment has been minimized.
This comment has been minimized.
Not without a big chunk of workaround-code. Until recently there has not really been a way of notifying the parent process about service readiness in a clean manner
It will! :-) My system is running vanilla Ubuntu 20.04 with no systemd as pid 1 and it works just fine. I think it's because it simply uses sockets to do the notification, so the service manager is not really necessary.
Yup. I removed that initially because I didn't want to hide any errors while developing and forgot to add it again. However, I'm posting an update that uses |
This comment has been minimized.
This comment has been minimized.
@Nimamoh
|
This comment has been minimized.
This comment has been minimized.
Really nice, I tried your script and it works really well. Are you okay if I update my blog post by referencing your work? |
This comment has been minimized.
This comment has been minimized.
@Nimamoh, of course, go right ahead (assuming attribution of course). |
This comment has been minimized.
This comment has been minimized.
@andsens |
This comment has been minimized.
This comment has been minimized.
@Nimamoh
|
This comment has been minimized.
This comment has been minimized.
@Nimamoh, updated. All known bugs are fixed :-) |
This comment has been minimized.
This comment has been minimized.
Well done! I updated the blog post accordingly and used the last version of your script, so far it's good ^^ |
This comment has been minimized.
This comment has been minimized.
I've tried the guide by @Nimamoh with this script, and I can't seem to get past the My environment is Pengwin (Debian based, with fish shell) in WSL2, up-to-date Windows 10 (fresh install). Kleopatra has found my Yubikey properly and seems to be configured correctly. Also, the systray icon for
Any suggestions? |
This comment has been minimized.
This comment has been minimized.
Apparently I should have checked further. |
This comment has been minimized.
This comment has been minimized.
@strangnet try running One possibility, things just take longer than I anticipated. I have set the timeout to 5 seconds (line 27), try adjusting that. |
This comment has been minimized.
This comment has been minimized.
If I do let the agent relay My solution now, is to accept that it works, i.e. I get the result I'm after, and just send the output of |
This comment has been minimized.
This comment has been minimized.
This script is fantastic, thanks for putting it together! I ran into some issue b/c
|
This comment has been minimized.
This comment has been minimized.
@lekv thanks for the pointer. I have adjusted the script, I really like how your solution makes no assumption about the home dir location whatsoever :-) |
This comment has been minimized.
This comment has been minimized.
Hi @andsens, thank you for the awesome script. I'm having a bit of trouble getting it to work. I've reached a stage where I had to slightly modify the script because my username contains a space and the script doesn't seem to be able to handle that. Now I just get a whole bunch of npiperelay.exe help messages. Here's my output of the foreground task when ran with
The I'm running Ubuntu 20.04.1 on WSL, windows 10 as host and I'm trying to use a yubikey 5. If I try the card status in the power shell I can actually see the card. I have all the perquisites installed. Any suggestions on what's going on? |
This comment has been minimized.
This comment has been minimized.
@dannyverp, socat EXEC is garbage at escaping the arguments for its invocations. I suggest fiddling around with different escaping mechanism and see what sticks (i.e. just run the socat command seperately). |
This comment has been minimized.
This comment has been minimized.
Hey, so I've found out that somehow sometimes a socket is open by multiple processes and thus the |
This comment has been minimized.
This comment has been minimized.
@jmigual huh, curious. Of course, only on process can be listening on that socket. So the others must be clients. I'm not sure killing them is the best way to go, it would be better to have |
This comment has been minimized.
This comment has been minimized.
This isn't working for me as is. I moved a few files around, but other than that I'm running the script exactly as you have here. The only thing that fixed gpg for me was to symlink Is there any way I can help figure out why it doesn't work with stock gpg? P.S. I'm also going to work on modifying the script to only setup the GPG relay. I want to use the built in Windows OpenSSH client with its ssh-agent. |
This comment has been minimized.
This comment has been minimized.
I'm also not able to get it working. When I run any |
This comment has been minimized.
This comment has been minimized.
@alanivey Did you try the symlink method? That was the only thing that worked for me. |
This comment has been minimized.
This comment has been minimized.
@mew1033 yes, I symlinked the Gpg4Win exe in WSL2 Linux as $HOME/.local/bin/gpg with I'll caveat that I have not used this configuration for longer than a day so I might end up being wrong! |
This comment has been minimized.
Great work, thank you!
I had two issues trying your script, first are the options
--notify-await --notify-timeout 2
on the start command. It is relatively recent options to rely on, do you have an opinion on how to adapt it on older system?Second if the
systemd-notify
which will not work unless started as a systemd service, what would you think about a|| true
?Lastly, I noticed it did not really support consecutive start or
kill -9
then restarts. Here is what I did to adapt your work, I am no bash guru, it's mainly food for thoughts: