Using SSH certificates

Pros and cons

In previous articles I’ve explained how to use traditional SSH keys and why connecting to a wrong server could expose your password. I was reminded of a newer form of authentication supported by OpenSSH; CA keys.

The CA key model is closer to how SSL certs work; you have an authority that is trusted by the servers and clients, and a set of signed keys.

Creating the CA key

Creating a certificate authority key is pretty much the same as creating any other key

$ mkdir ssh-ca
$ cd ssh-ca
$ ssh-keygen -f server_ca
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in server_ca.
Your public key has been saved in server_ca.pub.
The key fingerprint is:
04:f2:60:0a:89:c0:66:2d:1c:a9:da:ca:11:60:59:bf sweh@test1.spuddy.org
The key's randomart image is:
+--[ RSA 2048]----+
|Bo*.+ .          |
|+@ +.+ .         |
|* o  .. .        |
|..    ..         |
|...  E  S        |
|...              |
|...              |
|..               |
|                 |
+-----------------+
$ ls -l
total 8
-rw------- 1 sweh sweh 1766 Oct 30 09:30 server_ca
-rw-r--r-- 1 sweh sweh  403 Oct 30 09:30 server_ca.pub

Creating and using signed host keys

Now we can start using this key to sign our host’s key. This host is test1.spuddy.org. We use ssh-keygen (which has grown massively from its origins!) with the -h flag to indicate “host key”. Because it’s a certificate it can have an expiration date associated with it (-V) flag; in this case 52 weeks.

$ sudo ssh-keygen -s server_ca -I key_for_test1 -h -n test1.spuddy.org -V +52w /etc/ssh/ssh_host_rsa_key.pub
Enter passphrase: 
Signed host key /etc/ssh/ssh_host_rsa_key-cert.pub: id "key_for_test1" serial 0 for test1.spuddy.org valid from 2016-10-30T09:43:00 to 2017-10-29T09:44:13
$ ls -l /etc/ssh/ssh_host_rsa_key-cert*    
-rw-r--r-- 1 root root 1332 Oct 30 09:44 /etc/ssh/ssh_host_rsa_key-cert.pub

We now need to tell the server to offer this certificate. In your sshd_config file you need the line

HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub

Now the client also needs to be told about the CA. It does this my adding the CA’s public key into the known hosts file. This can be the clients’s global entry:

$ cat /etc/ssh/ssh_known_hosts
@cert-authority *.spuddy.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/jf9ZrH1qtVB6Gxo31bPA77WGuJ92jb9EaWNp+ht9j+s5+ZLLUm8RM1+T8vBow54IF0uavv/YLcchKIa6vmZdKmMJU5HF6SEdplGqDMDdvd+6U/cm/NQbmS0uTsLQBnCh4zptJJKeC1lc9IOCMwnz1oVA6rGdUc09rZMIDYyW2QpeFArgye5Yi57OWGJl+61QeqBMoAy/hytrYbGXYQsra5c5+PpYdPn2MbXzXY5dLf4H9usyhB+xzx8iAk5Sym4HBL6DnTnOzKAXL5fkI9XKiS3u7Hb1+7CSc0gOYoEFS+8L8kZ5iAqK5kkA0ekQO8DhzPEyZnecqx9n1C6UVb5D sweh@test1.spuddy.org

Now let’s see if this works. I have no known hosts file of my own:

$ cat .ssh/known*
cat: .ssh/known*: No such file or directory
$ ssh test1.spuddy.org
sweh@test1.spuddy.org's password: 

Hey, it didn’t ask me to accept the key!

Now I deliberately mis-signed the key for test2. I specified -n badkey.spuddy.org instead of -n test2.spuddy.org.

What happens now?

$ ssh test2.spuddy.org
Certificate invalid: name is not a listed principal
The authenticity of host 'test2.spuddy.org (10.0.0.159)' can't be established.
RSA key fingerprint is 58:45:95:16:c0:98:aa:5f:68:99:36:38:66:18:79:80.
Are you sure you want to continue connecting (yes/no)? 

Nice, it warns that the certificate doesn’t match and then falls back to letting you accept the key. Let’s fix the cert and try again:

$ ssh test2.spuddy.org
sweh@test2.spuddy.org's password: 

Better!

Client keys

Now how can we use CA keys to login to the server. This is very similar to signing host keys, but without the -h flag, and we specify the username this key is valid for.

First I’ll generate my ssh key and then sign it:

$ cd
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/sweh/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/sweh/.ssh/id_rsa.
Your public key has been saved in /home/sweh/.ssh/id_rsa.pub.
The key fingerprint is:
60:a7:fe:05:7a:7d:d6:ba:fe:2e:e8:05:66:36:fa:61 sweh@test1.spuddy.org
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|                 |
|      o .        |
|     . +         |
|      . S *      |
|     . . B o .   |
|      o o E.+ .  |
|       o +.=..   |
|        ..o.+=o  |
+-----------------+
$ ssh-keygen -s ssh-ca/server_ca -I user_sweh -n sweh -V +52w .ssh/id_rsa.pub
Enter passphrase:
Signed user key .ssh/id_rsa-cert.pub: id "user_sweh" serial 0 for sweh valid from 2016-10-30T10:14:00 to 2017-10-29T10:15:01

We need to configure the server to accept signed keys; the CA’s public key needs to be accessible and sshd_config needs an extra line:

TrustedUserCAKeys /etc/ssh/server_ca.pub

Now let’s try:

$ ssh test2.spuddy.org
Enter passphrase for key '/home/sweh/.ssh/id_rsa': 

test2$ cat .ssh/authorized_keys     
cat: .ssh/authorized_keys: No such file or directory

It let me in!

In /var/log/secure I can see:

Oct 30 10:19:17 test2 sshd[1967]: Accepted publickey for sweh from 10.0.0.158 port 38092 ssh2: RSA-CERT ID user_sweh (serial 0) CA RSA 04:f2:60:0a:89:c0:66:2d:1c:a9:da:ca:11:60:59:bf

This also works with ssh-agent

$ eval `ssh-agent`
Agent pid 2286
$ ssh-add
Enter passphrase for /home/sweh/.ssh/id_rsa: 
Identity added: /home/sweh/.ssh/id_rsa (/home/sweh/.ssh/id_rsa)
Certificate added: /home/sweh/.ssh/id_rsa-cert.pub (user_sweh)
$ ssh test2.spuddy.org echo hello
hello

Summary

Signed host keys can help in avoiding need to maintain a track of host keys, and so can solve the “initial trust” issue problem. It can also solve the “host rebuild” issue. However an automation system to sign these keys when a server is built is essential for this to work.

Similarly we don’t need to distribute authorized_keys files if the user can present a signed certificate. This requires a request system (preferably self-service) where the user can present their public key and get a signed result.

Certificate authentication isn’t supported by all clients; you can expect it in OpenSSH 6.2 onwards, and companies such as RedHat may have backported it to older versions. However desktop based ssh clients such as putty may not support it.

Certificate authentication isn’t as flexible as standard key based authentication in that we don’t have an authorized_keys entry to add restrictions (from=, command=, etc) but it can solve a number of problems, particularly around host key management and distributing of authorized_keys files for human accounts; build a new server, get the host key signed and automatically we have a trust and an authentication process working.

We just need the workflows to do the signing :-)

Update

Based on blog posts and emails (thank you!) there’s a follow up post which goes into some more detail on the advanced use of certificates, including multiple principals and additional restrictions.