[example] Sign OpenSSH certificates from HSM

Did not see a sample command here in the forum (maybe because it is documented in the most obscure location – called man-page :wink: )

I am exploring SSH certificates (introduced in OpenSSH 5.4 in 2010, btw).
OpenSSH server is already set up in ssh_config with TrustedUserCAKeys /etc/ssh/my-exported-public-key-for-use-as-SSH-CA.pub

Objectives

  • use the HSM as “SSH certificate authority”
  • sign non-HSM based SSH keys with short validity duration

Indeed ssh-keygen can somehow “point” to the HSM by supplying the public part of the key (an RSA key in my tests) and the -D option:

ssh-keygen -D /usr/lib/x86_64-linux-gnu/pkcs11/opensc-pkcs11.so -s ~/.ssh/id-my-exported-public-key-for-use-as-SSH-CA.pub -I "SSH HSM CA-signed user thru 2024-11-30" -V "-1d:20241130" ~/.ssh/id-my-exported-user-EC-public-key-to-be-signed.pub

All tutorials out there typically show -spointing to a private key file, e.g. generated with easy-rsa. But the manpage clearly says:

It is possible to sign using a CA key stored in a PKCS#11 token by providing the token library using -D and identifying the CA key by providing its public half as an argument to -s:

$ ssh-keygen -s ca_key.pub -D libpkcs11.so -I key_id user_key.pub

The command produces a file id-my-exported-user-EC-public-key-to-be-signed-cert.pub.
Despite the *pub file extension it is a certificate though, with a defined validity period. Documented in the manpage of ssh-keygen:

certificates may be defined with a validity lifetime. The -V option allows specification of certificate start and end times. A certificate that is presented at a time outside this range will not be considered valid. By default, certificates are valid from the UNIX Epoch to the distant future.

And with the possibility to re-import into a Nitrokey HSM2 by use of a DKEK I feel confident to finally explore the removal of SSH password (fallback) logins. I use SSH keys purely on a personal basis for hobby stuff:

  • if my certificate expired I sign a new one (there can even be serial numbers and CRL)
  • if I lost/destroyed/re-initialized the HSM I re-import from a rocksolid backup (already happed to me, the rather long form factor of the HSM and a laptop are ripe for such desasters)
  • if I am not at my primary PC/location I can have a duplicate HSM travelling with me

ssh-keygen can also do, btw:

Certificates may be limited to be valid for a set of principal (user/host) names. By default, generated certificates are valid for all users or hosts. To generate a certificate for a specified set of principals:

$ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub

2 Likes

I should add that the objective

  • sign non-HSM based SSH keys with short validity duration

is indeed my intended usecase: I do not want to carry the HSM with me as if it was a 2FA device. The HSM will be kept “less mobile” and “more restricted”.
Rather I want to have a standard file-based private key that I copy to all machines where I want to ssh from.

However, it is of course possible to use the private key on a Nitrokey HSM together with a signed certificate, the options in~./ssh/config are just slightly different (mind the intendation):

Host test
User root
HostName 10.47.11.1
PKCS11Provider /usr/lib/x86_64-linux-gnu/pkcs11/opensc-pkcs11.so
CertificateFile id-my-exported-user-EC-public-key-to-be-signed-cert.pub

Mind the CertificateFile instead of IdentityFile.

ssh -vv test will then result in the usual

Enter PIN for ‘myLabel (UserPIN)’:

In the OpenSSH server’s sshd_config the AuthorizedKeysFile could even be altered, I plan to take ~./ssh/authorized_keys away for root (to prevent future me falling back to ssh-copy-id out of habit)

Match User root
AuthorizedKeysFile /dev/null

When the OpenSSH server is trusting the CA in ssh_config with TrustedUserCAKeys, this means who has access to the HSM and its UserPIN can (re-)sign any public SSH key, specifying root as (one of the) principals -n root,joe, which turns it into a certificate and permits to log in as root. Just to spell this out!
Users of this forum understand how restrictive “HSM + UserPIN” is, whereas out-there-on-reddit kind of flamewars may erupt related to all things SSH certificates.


So the gist of these posts is

  • sign with a path to a public key
  • connect with option CertificateFile