HOWTO
SSH
VERIFY SSH HOST KEYS WITH SSH CA

Published: 20221102

Tested on:

-

Just some very brief notes on how to verify SSH host keys with SSH CA.
I might extend this article later or write more on the subject.

In this HOWTO we have three machines:

ca1
A machine that will hold the CA (Certificate Authority), which is a private key that shouldn't leave this machine. Theoretically a machine holding a CA shouldn't be on a network, but in this case it's practical if this machine can reach any server it needs to create a certificate for.

server1
A server (which is also listening on SSH) whose SSH public host key is going to be signed by ca1.

client1
This machine wants to access server1 via SSH without any pre-existing knowledge of the SSH public host key. Normally this knowledge is transfered "by hand" by whoever setup server1 to the owner of client1. Commonly there's a bad practise of users just "connecting blindly" to the server and trust whatever SSH public host key their SSH client reports.

The plot
The idea is simple: ca1 manually verifies the host key of server1 and issues a certificate that server1 can configure into the SSH settings on server1. This way anyone who has the public CA certificate from ca1 can verify any SSH server that ca1 has verified. ca1 is thus a trustbroker between server1 and client1.

Something like this:

ssh_ca_the_plot

HOWTO
Pay attention to the prompt in the commands below as it indicates on which machine the commands are run.

First create the signing key on ca1 (step 03):

ca1:~$ mkdir ca
ca1:~$ cd cd
ca1:~/ca$ ssh-keygen -t rsa -b 4096 -f ca_signing.key -C ca_signing_key
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ca_signing.key
Your public key has been saved in ca_signing.key.pub
The key fingerprint is:
SHA256:C1QaVSCyz4XIl8vyIdSfmBi+NADtr4UShoxuYFaKJmo ca_signing_key
The key's randomart image is:
+---[RSA 4096]----+
|..  . o.+o.      |
| ..o = B         |
|=.+ * B .        |
|BB.+ O * .       |
|X. oB X S        |
|oE..o* o .       |
|o. o. . .        |
|  .              |
|                 |
+----[SHA256]-----+
ca1:~/ca$ 

You need to collect the public keys you're going to sign (step 04):

ca1:~/ca$ mkdir server1
ca1:~/ca1$ scp server1.example.com:/etc/ssh/ssh_host_*_key.pub ./server1/
ssh_host_dsa_key.pub                          100%  600   217.6KB/s   00:00
ssh_host_ecdsa_key.pub                        100%  172   105.4KB/s   00:00
ssh_host_ed25519_key.pub                      100%   92    63.3KB/s   00:00
ssh_host_rsa_key.pub                          100%  736   487.5KB/s   00:00
ca1:~/ca$ 

Then you sign each key (step 05):

ca1:~/ca$ ssh-keygen -s ca_signing.key -I server1_host_rsa -h -Z server1.example.com -V -5m:+365d -z 1 server1/ssh_host_rsa_key.pub
Signed host key server1/ssh_host_rsa_key-cert.pub: id "server1_host_rsa" serial 1 valid from 2022-11-01T22:44:04 to 2023-11-01T22:49:04
ca1:~/ca$ ssh-keygen -s ca_signing.key -I server1_host_dsa -h -Z server1.example.com -V -5m:+365d -z 2 server1/ssh_host_dsa_key.pub
Signed host key server1/ssh_host_dsa_key-cert.pub: id "server1_host_dsa" serial 2 valid from 2022-11-01T22:44:11 to 2023-11-01T22:49:11
ca1:~/ca$ ssh-keygen -s ca_signing.key -I server1_host_ecdsa -h -Z server1.example.com -V -5m:+365d -z 3 server1/ssh_host_ecdsa_key.pub
Signed host key server1/ssh_host_ecdsa_key-cert.pub: id "server1_host_ecdsa" serial 3 valid from 2022-11-01T22:44:21 to 2023-11-01T22:49:21
ca1:~/ca$ ssh-keygen -s ca_signing.key -I server1_host_ed25519 -h -Z server1.example.com -V -5m:+365d -z 4 server1/ssh_host_ed25519_key.pub
Signed host key server1/ssh_host_ed25519_key-cert.pub: id "server1_host_ed25519" serial 4 valid from 2022-11-01T22:44:31 to 2023-11-01T22:49:31
ca1:~/ca$ 

Here's a breakdown what the different flags do:

-s ca_signing.key      = Chooses which key to sign with
-I server1_host_rsa    = Identity of the certificate (like a label)
-h                     = We are signing a host public key
-Z server1.example.com = Sets/binds the certificate to this hostname
-V -5m:+365d           = The certificate starts to be valid 5 minutes ago and is valid up until 365 days from now
-z 1                   = Sets a serial number for the certificate, it is good custom to have this

Now transfer the certificate files back to server1 (step 06):

ca1:~/ca$ scp server1/ssh_host_*_key-cert.pub server1:
ssh_host_dsa_key-cert.pub                                                                                                          100% 2217   468.4KB/s   00:00    
ssh_host_ecdsa_key-cert.pub                                                                                                        100% 1789   957.7KB/s   00:00    
ssh_host_ed25519_key-cert.pub                                                                                                      100% 1713     1.0MB/s   00:00    
ssh_host_rsa_key-cert.pub                                                                                                          100% 2349     1.2MB/s   00:00    
ca1:~/ca$ 

Install the certificates (step 07):

server1:~$ sudo mv ssh_host_*_key-cert.pub /etc/ssh/
server1:~$ sudo chown root.root /etc/ssh/ssh_host_*_key-cert.pub
server1:~$ 

Add these four lines to /etc/ssh/sshd_config (step 07):

HostCertificate /etc/ssh/ssh_host_dsa_key-cert.pub
HostCertificate /etc/ssh/ssh_host_ecdsa_key-cert.pub
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub

Restart the SSH service with the new settings (step 07):

server1:~$ sudo systemctl restart ssh
server1:~$ 

Transfer the CA signing public key (ca_signing.key.pub) to client1 and put the file in ~/.ssh/ and add it as a CA to known_hosts on client1 (step 09):

client1:~$ cd .ssh
client1:~/.ssh$ echo "@cert-authority *.example.com $(cat ca_signing.key.pub)" >> ~/.ssh/known_hosts
client1:~/.ssh$ 

That's it!

Do note that it will only verify when you ssh to server1 with the full FQDN server1.example.com. You could add the cert-authority with * instead of *.example.com, but it's in theory kind of sloppy.

There's more fun to be learned about SSH CA, but that might be another article in the future.