SSH at Scale with OpenSSH Certificates - SmartCard Backed Keys
Many weeks ago, when I prematurely declared a final note on the SSH at Scale topic, I was left wondering if I could go further and loop my GPG Smartcard into the mix. This post details what I found hiding just below the surface of OpenSSH and OpenSC to let one maintain all necessary secrets on a SmartCard and still use OpenSSH Certificates for login.
First find your pkcs11 module (on Linux)
$ PKCS11_MODULE=$(pkcs11-tool | sed -n 's|.*(default:\(.*\))|\1|p;')
$ echo "${PKCS11_MODULE}"
/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
Using the PKCS15 interface, export the public parts of associated keys for signing
and authentication. In the case of my Librem Key, the relevant IDs are 01
and 03
:
Signature Pubkey:
$ pkcs15-tool --read-ssh-key 01 -o sig.pub
Using reader with a card: Purism, SPC Librem Key (000000000000000000009BB4) 00 00
$ cat sig.pub
ssh-rsa AAAAB3N...DVUhQ== Signature key
Authentication Pubkey:
$ pkcs15-tool --read-ssh-key 03 -o auth.pub
Using reader with a card: Purism, SPC Librem Key (000000000000000000009BB4) 00 00
$ cat auth.pub
ssh-rsa AAAAB3N...fuzQ== Authentication key
Now that both public keys are on disk, one can utilize the corresponding
SmartCard-backed private keys in a normal OpenSSH Certificate flow. The
first, and least intuitive, difference is in the call to ssh-keygen
. When
the private key is on disk one passes the path after -s
and this key
is used as the Certificate Authority. In the new case, the path to the
public key is given after -s
and the path to the pkcs11 module is
given after -D
. An example follows, signing the exported auth.pub
to
create a certificate:
$ ssh-keygen -D ${PKCS11_MODULE} -s sig.pub -n ${USER} -I EXAMPLE_CERT -z $(date +%s) -V +15m auth.pub
Enter PIN for 'OpenPGP card (User PIN (sig))':
Signed user key auth-cert.pub: id "EXAMPLE_CERT" serial 1662515192 for amartinez valid from 2022-09-06T20:45:00 to 2022-09-06T21:01:32
$ ssh-keygen -Lf auth-cert.pub
auth-cert.pub:
Type: ssh-rsa-cert-v01@openssh.com user certificate
Public key: RSA-CERT SHA256:V2KMVlJjPOn86z6a2srEcnMQj78OujEXJ597PJ6+wyY
Signing CA: RSA SHA256:HoXa4G9gmsln+8gOUPEeKNmcCA0cppiUlmUuEjt8joA (using rsa-sha2-512)
Key ID: "EXAMPLE_CERT"
Serial: 1662515192
Valid: from 2022-09-06T20:45:00 to 2022-09-06T21:01:32
Principals:
amartinez
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty.
permit-user-rc
Using the generated cert with a private key on the SmartCard requires that the target
host is configured to trust the key used as a CA above. An earlier post
details how such a configuration could be accomplished. Assuming your target host
is properly configured all that is left is telling ssh
where to find the pkcs11
module, -I
, and which certificate to utilize -o CertificateFile=...
. Another
example follows doing just that:
$ ssh -I ${PKCS11_MODULE} -o CertificateFile=auth-cert.pub beaglebone
Enter PIN for 'OpenPGP card (User PIN)':
Last login: Wed Sep 7 00:25:47 2022 from ...
amartinez@beaglebone:~$ sudo grep -i cert /var/log/auth.log | tail -3 | grep -v grep
Sep 7 01:52:21 beaglebone sshd[1344]: Accepted publickey for amartinez from ... port 40728 ssh2: RSA-CERT SHA256:V2KMVlJjPOn86z6a2srEcnMQj78OujEXJ597PJ6+wyY ID EXAMPLE_CERT (serial 1662515192) CA RSA SHA256:HoXa4G9gmsln+8gOUPEeKNmcCA0cppiUlmUuEjt8joA
The scenario used in these examples was exceedingly simple, and in production one
should use an entirely different smartcard (or other pkcs11 device like a TPM)
to act as the CA. If, like me, you tend to use your SmartCard with gpg
rather
than pcscd
it is worth noting that use of the certificate works just fine
without the -I
option to ssh
provided the associated key is available through
gpg-agent
serving as your ssh-agent
. Signing, as far as I can tell, does require
first stopping the local user's gpg-agent
and then starting pcscd
globally to
use pkcs11. As a final note, the above works exactly the same way in Windows
with the exception of the format of the paths given.