SSH Certificate Authorities
Accessing SSH servers with ease


Posted on 2017-01-14
Tags: ssh sshd CA

A common nuisance when first connecting with SSH to a server is to verify the fingerprint. Especially if you have many servers with multiple users, when everyone needs to know all the fingerprints of all the servers. This can easily be improved with SSH CA host certificates.

For example if we were to sign a server's public key when we provision it, everyone that already trusts the CA can then also connect to that server without having to manually verify the fingerprint.

User certificates on the other hand can help us authorize users to a server without manually managing each individual key. By deploying a SSH user CA to a remote host anyone with a valid certificate can connect to it.

Manpages

The manpages of ssh-keygen(1) and sshd(8) contains everything you need to known about SSH Certificates, of most interest is the section "CERTIFICATES" of ssh-keygen(1) and sections "AUTHORIZED_KEYS FILE FORMAT" and "SSH_KNOWN_HOSTS FILE FORMAT" of sshd(8). Use them as a reference and think of this post as a way to get started.

Host certificates

Creating

The CA itself is generated like any other SSH key.

ssh-keygen -f host_ca -t ed25519

Trusting

Users can then choose to trust this CA by adding the a line like toe following to their known_hosts file (defined in sshd(8) section "AUTHORIZED_KEYS FILE FORMAT"):

@cert-authority *.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIzHKhkOE4C58Zgg/7AO2xXVGKBSAt2iKs9vJgkCu8hh user@host

The second field is to limit the scope of the key, in this case we trust it to verify everything from *.example.com but not for example secret.example.test.

Signing

This is how we sign a server's public key, that is, generate a certificate for that server:

ssh-keygen -s "$HOST_CA" -I "$IDENTIFIER" -h -n "$SERVER_DOMAIN" "$PUBLIC_KEY"

A quick explanation to the arguments:

Argument Description
-s The CA key to sign with
-I An identifier for the certificate, used for logging and revocation
-h Create a host certificate
-n Principals, in practice which domain this certificate will be valid for

Its also possible to limit when a certificate is valid with -V.

In order to sign the public key ssh_host_ed25519_key.pub of server server.example.com with the host_ca CA we can run:

ssh-keygen -s "host_ca" -I "user_1@host_ca" -h -n "server.example.com" "ssh_host_ed25519_key.pub"

This will create a file called ssh_host_ed25519_key-cert.pub that we need to configure the server to use. This is done with the HostCertificate directive from sshd_config(5).

HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub

Demoing

Lets put everything together into a working example.

ssh-keygen -f host_ca -t ed25519
echo "@cert-authority *.example.com $(cat host_ca.pub)" >> ~/.ssh/known_hosts
scp server.example.com:/etc/ssh/ssh_host_ed25519_key.pub .
ssh-keygen -s host_ca -I "user_1@host_ca" -h -n server.example.com \
    ssh_host_ed25519_key.pub
scp ssh_host_ed25519_key-cert.pub server.example.com:
ssh -t server.example.com 'echo "HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub" | sudo tee --append /etc/ssh/sshd_config'
ssh -t server.example.com sudo systemctl reload ssh

Lets test it out

echo "@cert-authority *.example.com $(cat host_ca.pub)" \
    | sudo -u newuser -i bash -c "cat > ~/.ssh/known_hosts"
sudo -u newuser ssh server.example.com

Notice how it does not ask you to verify the fingerprint, as we already trusted the CA.

User certificates

By installing a CA's public key into a remote users authorized_keys file (or globally on the server) everyone who has their public key signed by the CA (i.e. a certificate) will have access to the server.

Creating

We create user CA's in the exact same manner as host CA's, or ordinary ssh keys for that matter.

ssh-keygen -f "user_ca" -t "ed25519"

Trusting

By adding the user CA's public key to a remote user's authorized_keys file we will grant access to anyone who has a valid certificate from the CA. For this to work we also need to give the key the cert-authority option. The format is specified by section "AUTHORIZED_KEYS FILE FORMAT" of sshd(8).

For example:

cert-authority ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK624pJT4N/5RrU9AE4I5U1fZCVGwlyqM4nylreB15oB user@host

It is also possible to globally install a CA key by using the TrustedUserCAKeys of sshd_config(5). In that case it is however very important to limit the scope of the certificates as a certificate will otherwise give access to any account on the server.

Signing

A user certificate is generated in much the same way as a host certificate, the big difference is that the -h argument is missing.

ssh-keygen -s "$USER_CA" -I "$IDENTIFIER" "$PUBLIC_KEY"

The other difference is the principals defined with -n. With user certificates they define which users the certificate is valid for, instead of which hosts. If you have installed the CA globally (with TrustedUserCAKeys in sshd_config) it is strongly recommended you use this, as the certificate otherwise will grant access to any account.

ssh-keygen -s "$USER_CA" -I "$IDENTIFIER" -n "$USER" "$PUBLIC_KEY"

Demoing

Lets try out a working example of user certificates:

ssh-keygen -f user_ca -t ed25519
echo "cert-authority $(cat user_ca.pub)" \
    | ssh server.example.com "cat >> .ssh/authorized_keys"

And test if it works:

# We use sudo to be able to write to newuser's homedir in order to simplify
# this demo
sudo ssh-keygen -s user_ca -I "newuser@user_ca" ~newuser/.ssh/id_ed25519.pub
sudo -u newuser -i ssh user@server.example.com

Notice that no password is required, in fact, if you followed this post in order you should have been logged in without neither verifying a fingerprint nor providing a password.

Revocation

Revoking keys is also possible, however because of the length of this post and the fact that I have yet to configure any key revocation lists I have chosen not to discuss that in this post. You can however read about it in the section "KEY REVOCATION LISTS" of ssh-keygen(1).