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.