Update on 07-07-2020:

Thanks to the work of Anders Ingemann, the setup process has been simplified.

Using a smartcard to hold your GPG key can be great. Personnaly, I use a Yubikey. Although, by default, you cannot use any device from WSL2, including your smartcard.

To circumvent this issue, I came across several articles treating this topic. Here is what worked for me and a summarized how-to.

We will consider you have your yubikey with gpg keys setup on it, we also consider you use Gpg4Win.

Pre-requisites

npiperelay

As explained in justyn blog article, we will use npiperelay to use gpg4win gpg-agent from WSL2 environment.

  • First, download NZSmartie’s version of npiperelay. Place it in %appdata%\npiperelay\npiperelay.exe.

Enable windows gpg ssh support and prepare pageant

If you use the GPG keys in your smartcard to authenticate through SSH, you will have to make changes too. Several approaches exists, this one uses https://github.com/benpye/wsl-ssh-pageant and npiperelay (as seen previously) to use windows GnuPG’s ssh pageant.

First, enable SSH support by:

  • Locating your gpg-agent.conf (probably in %userprofile%\AppData\Roaming\gnupg\gpg-agent.conf, you may have to create it)
  • Add these two lines
enable-putty-support

Then, visit wsl-ssh-pageant, download the latest version to

  • Visit release, download the latest version in %appdata%\wsl-ssh-pageant\wsl-ssh-pageant-amd64-gui.exe

The name of this file is dependant of your architecture and support of GUI. Adapt accordingly if you choose something different than amd64-gui variant

Setup the daemon in the WSL2’s side

Thanks to Anders Ingemann’s work, you’ll have a scipt in charge of launching the required tools as a daemon.

Tested on Ubuntu 20.04 with socat utility installed. You’ll need to adapt the script yourself for older version. Also, you will want to double check on the provided paths

Visit Anders Ingemann’s gist for the most up to date version:

#!/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
  case $1 in
  start)
    if ! start-stop-daemon --pidfile "$PIDFILE" --background --notify-await --notify-timeout 5 --make-pidfile --exec "$0" --start -- foreground; then
      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 winuser
  winuser=$(/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe '$env:UserName')
  winuser=${winuser//$'\r'}
  local winhome="/mnt/c/Users/$winuser"
  local wingnupghome="C:/Users/$winuser/AppData/Roaming/gnupg"
  local npiperelay="$winhome/AppData/Roaming/npiperelay/npiperelay.exe"
  local wslsshpageant="$winhome/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"

  killsocket "$gpgagentsocket"
  killsocket "$sshagentsocket"

  "$gpgconnectagent" /bye

  "$wslsshpageant" --systray --winssh ssh-pageant 2>/dev/null &
  WSPPID=$!

  socat UNIX-LISTEN:"$gpgagentsocket,unlink-close,fork,umask=177" EXEC:"$npiperelay -ep -ei -s -a '${wingnupghome}/S.gpg-agent'",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=(socat start-stop-daemon lsof timeout)
  local dep
  local out
  for dep in "${deps[@]}"; do
    if ! out=$(type "$dep" 2>&1); then
      printf -- "Dependency %s not found:\n%s\n" "$dep" "$out"
      return 1
    fi
  done
}

main "$@"

Then, update your .bashrc to start the script, considering your script location to ~/.local/bin/gpg-relay-agent:

$HOME/.local/bin/gpg-agent-relay start
export SSH_AUTH_SOCK=$HOME/.gnupg/S.gpg-agent.ssh

Now, on computer boot, from WSL2, you should be able to see the SSH keys of you yubikey when you type

ssh-add -L

Troubleshooting

  • Check relay is running with:
    ~/.local/bin/gpg-agent-relay status
    
  • Check error of relay script launching it foreground:
    ~/.local/bin/gpg-agent-relay foreground
    
  • Double check the path you put npiperelay and wsh-ssh-pageant on windows, make sure it corresponds to the ones specified in gpg-agent-relay

  • Don’t forget to update gpg-agent.conf (you may have to restart gpg4win)

Resources