Using YubiKey and SmartCards through ssh tunnel remotely with p11-kit

Sat 18 April 2020
By mute

I don't run my DNS server on my laptop and I don't plug in my smartcard at the datacenter (yet). So to keep the KSK offline, I have my YubiKey plugged into my home router, I ssh into that, and then ssh to the DNS server with the key shared over an ssh channel.

p11-kit-server creates a unix socket which then serializes the communication to the smartcard. p11-kit-remote is more like the xinetd way of doing things. It isn't what I want for this, but can be used in the reverse -- such that the dns server starts the communication to the host with the smartcard (hopefully behind nginx with acl and more certs, or ssh tunnel again). but anyway...

The basic steps:

  1. Start p11-kit-server (first in foreground for testing):

    mute@local:~$ p11-kit server pkcs11:token=ksk 'pkcs11:token=YubiKey%20PIV%20%237792984' -f -n /tmp/pkcs11.socket
    P11_KIT_SERVER_ADDRESS=unix:path=/tmp/pkcs11.socket; export P11_KIT_SERVER_ADDRESS;
    P11_KIT_SERVER_PID=29446; export P11_KIT_SERVER_PID;
    

    This shares two tokens (well, the same token but different drivers), in the foreground, and drops the socket in /tmp. A long term solution would drop the last 2 parts and run it similar to ssh-agent: eval $(p11-kit-server pkcs11:token=ksk) and the socket goes in /run/user/<id>/pkcs11/ (or whatever your system is configured for).

  2. Find path to put socket on remote machine. You can also use systemd-path user-runtime but sources show it searches $P11_KIT_SERVER_ADDRESS, and then <runtime dir>/p11-kit/pkcs11. <runtime dir> is the first of any that exist: $XDG_RUNTIME_DIR, /run/user/<id>, /var/run/user/<id>, $XDG_CACHE_HOME, $HOME/.cache/p11-kit/pkcs11.

    mute@local:~$ ssh remote
    [mute@remote ~]$ echo $XDG_RUNTIME_DIR
    /run/user/1000
    
  3. Register the p11-kit-client module on the remote machine so you don't have to specify provider/module.

    [mute@remote ~]$ mkdir -p ~/.config/pkcs11/modules/
    [mute@remote ~]$ echo 'module: p11-kit-client.so' > ~/.config/pkcs11/modules/p11-kit-client
    
  4. You're ready to ssh with the unix socket forwarded. Start ssh with -R to forward a unix socket. I am going with a different path to show P11_KIT_SERVER_ADDRESS usage, and so I don't need to make sure p11-kit directory is made for me each reboot since /run/user isn't saved to disk.

    mute@local:~$ eval $(p11-kit server pkcs11:token=ksk)
    mute@local:~$ ssh -R "/home/mute/.pkcs11.socket:${P11_KIT_SERVER_ADDRESS#*=}" remote
    [mute@remote ~]$ export P11_KIT_SERVER_ADDRESS='unix:path=/home/mute/.pkcs11.socket'
    [mute@remote ~]$ p11tool --list-token-urls
    pkcs11:model=p11-kit-trust;manufacturer=PKCS%2311%20Kit;serial=1;token=System%20Trust
    pkcs11:model=p11-kit-trust;manufacturer=PKCS%2311%20Kit;serial=1;token=Default%20Trust
    pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=dc6e8bb5b47e7aa8;token=ksk
    
  5. (optional) You may get warnings on subsequent connections because the old socket still exists. You can edit /etc/ssh/sshd_config on the remote to include StreamLocalBindUnlink yes so that it's deleted during connection and save yourself from restarting the connection.

    Warning: remote port forwarding failed for listen path /home/mute/.pkcs11.socket
    

Comments