Skip to content

Instantly share code, notes, and snippets.

@Riebart
Last active September 18, 2023 16:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Riebart/16d957fb714263b32a0e9351bc9fa40e to your computer and use it in GitHub Desktop.
Save Riebart/16d957fb714263b32a0e9351bc9fa40e to your computer and use it in GitHub Desktop.
Proxy a Yubikey or smart card through from Windows to WSL2 with just socat.

HowTo Use

Windows

  • Install gnupg for Windows from gnupg.org (NOT GP4Win, the regular one).
  • Plug in your Yubikey, and run gpg --card-status, and make sure it emits some useful output about the card.

WSL1

We need WSL1 to act as the network proxy between the Windows TCP litening assuan socket, which is only on localhost, and a tcp socket listening on a host port that we can access from WSL2.

  • Open any WSL1 distro, you'll need socat (sudo apt install socat).
  • Stick both the above scripts in the same director. Make sure that all other instances of those scripts are dead.
    • If you get a prompt to permit access to socat from public networks, or any network, just say NO. You don't want randoms accessing this socket from your local network.

      $ bash assuan.sh
      /mnt/c/Users/Michael/AppData/Local/gnupg/S.gpg-agent:62192:S.gpg-agent
      /mnt/c/Users/Michael/AppData/Local/gnupg/S.gpg-agent.browser:62194:S.gpg-agent.browser
      /mnt/c/Users/Michael/AppData/Local/gnupg/S.gpg-agent.extra:62193:S.gpg-agent.extra
      /mnt/c/Users/Michael/AppData/Local/gnupg/S.gpg-agent.ssh:62195:S.gpg-agent.ssh
      /mnt/c/Users/Michael/AppData/Local/gnupg/S.scdaemon:62197:S.scdaemon\
      
      $ gpg --card-status
      gpg: WARNING: unsafe permissions on homedir '/home/Michael/.gnupg'
      gpg: keybox '/home/Michael/.gnupg/pubring.kbx' created
      Reader ...........: Yubico YubiKey OTP FIDO CCID 0
      Application ID ...: <snip>
      Application type .: OpenPGP
      Version ..........: 0.0
      Manufacturer .....: Yubico
      Serial number ....: <snip>
      Name of cardholder: <snip>
      Language prefs ...: en
      Salutation .......: <snip>
      URL of public key : [not set]
      Login data .......: [not set]
      Signature PIN ....: not forced
      Key attributes ...: ed25519 cv25519 ed25519
      Max. PIN lengths .: 127 127 127
      PIN retry counter : 3 3 3
      Signature counter : 1400
      KDF setting ......: off
      Signature key ....: <snip>
            created ....: <snip>
      Encryption key....: <snip>
            created ....: <snip>
      Authentication key: <snip>
            created ....: <snip>
      General key info..: [none]

WSL2

Do the exact same as WSL1, the script will auto-detect and use a local interface address.

#!/bin/bash
v1Distro="$1"
v2Distro="$2"
if [ "$v1Distro" == "" ] || [ "$v2Distro" == ""]
then
v1Distro=$(wsl.exe -l -v | iconv -f utf-16 -t utf-8 | tr -d '\r' | grep "1$" | tail -c+3 | cut -d ' ' -f1)
v2Distro=$(wsl.exe -l -v | iconv -f utf-16 -t utf-8 | tr -d '\r' | grep "2$" | tail -c+3 | cut -d ' ' -f1)
fi
echo "WSL1 Distro: $v1Distro"
echo "WSL2 Distro: $v2Distro"
# The common broadcast indicates a common network CIDR block shared.
# The two distros will have different addresses on the block, but will both be on at least one block.
wslCommonBroadcast=$(comm -12 \
<(wsl.exe -d "$v1Distro" --cd / ip a | grep -o "brd [0-9.]*255" | sort) \
<(wsl.exe -d "$v2Distro" --cd / ip a | grep -o "brd [0-9.]*255" | sort) | cut -d ' ' -f2)
# The common address we want is the host one, or rather that used by WSL1.
wslCommonAddress=$(wsl.exe -d "$v1Distro" --cd / ip a | grep "$wslCommonBroadcast" | grep -o "inet [0-9.]*" | cut -d ' ' -f2)
echo "WSL2 Common Broadcast: $wslCommonBroadcast"
echo "WSL2 CommonAddress: $wslCommonAddress"
ls $(wslpath `gpgconf.exe --list-dirs socketdir` | tr -d '\r\n')/S.* |
while read assuanFile
do
assuanPort=$(head -n1 $assuanFile)
gpgSocketDir=$(gpgconf --list-dirs socketdir)
socketName=$(echo "$assuanFile" | sed 's|^.*/\([^/]*\)$|\1|')
echo $assuanFile:$assuanPort:$socketName
if `uname -a | grep WSL2 >/dev/null`
then
echo "Running in WSL2, not starting the connections to localhost"
else
socat tcp-listen:${assuanPort},fork,bind=$wslCommonAddress tcp:127.0.0.1:${assuanPort} &
fi
socat \
unix-listen:${gpgSocketDir}/${socketName},unlink-early,fork \
"exec:bash assuan_sock.sh ${assuanFile} ${wslCommonAddress}" &
done
#$(gpgconf --list-dirs | grep "`echo "$socket" | sed 's|^.*/\([^/]*\)$|\1|'`$" | cut -d ':' -f2)
#ls /mnt/c/Users/Michael/AppData/Local/gnupg/S.* | while read socket; do echo $socket; socat -x -v unix-listen:$(gpgconf --list-dirs | grep "`echo "$socket" | sed 's|^.*/\([^/]*\)$|\1|'`$" | cut -d ':' -f2),unlink-early,fork "exec:bash ~/.gnupg/assuan.sh ${socket} 172.31.224.1" & done
#bash -c "(tail -n+2 "${assuanFile}"; cat -) | nc -vn "172.31.224.1" `head -n1 "$assuanFile"`
#!/bin/bash
assuanFile="$1"
assuanIpAddr="$2"
(
tail -n+2 "$assuanFile"
cat -
) | nc -n "$assuanIpAddr" `head -n1 "$assuanFile"`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment