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 email@example.com 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 (
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
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 firstname.lastname@example.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 email@example.com'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
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 firstname.lastname@example.org's password:
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 email@example.com 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:
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!
/var/log/secure I can see:
Oct 30 10:19:17 test2 sshd: 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
$ 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
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
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
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
We just need the workflows to do the signing :-)