In this article I’ll guide you through verifying SSH host keys manually, then the crazy shell shenanigans way, and finally we’ll talk about SSH keys in SSHFP DNS(SEC) records!

So, let’s say you’re using OpenSSH on your server and you’re connecting for the first time. How do you actually verify that the key is correct?

On Ubuntu Server, running OpenSSH Server, the host keys will be stored in /etc/ssh/ with the names ssh_host_*_key.pub.

Verifying Manually

So, if you have a remote console to your server (like with a VPS) or physical access to your server, you’d run this:

find /etc/ssh/ -name 'ssh_host_*_key.pub' -exec ssh-keygen -E sha256 -lf {} \;

This will print out all the host keys as SHA256. You can write these down, and next time you’re asked if the key matches, you can manually compare them and confirm.

Verifying the shell shenanigans way (this is not for the faint of heart, jump to DNSSEC+DNS)

We can also manually do a scan to display all public keys from a server with:

ssh-keyscan -q server.example.net

Now, admittedly, that’s not very useful since it will output all raw keys. So, we can actually do this instead:

ssh-keyscan server.example.net | ssh-keygen -lf -

Now we should get the same or similar output – albeit in different order – as we got on our server (assuming all keys are enabled in the SSH server config)!

Let’s improve this a little to get the same output:

On the server:

find /etc/ssh/ -name 'ssh_host_*_key.pub' -exec ssh-keygen -E sha256 -lf {} \; | awk '{print $2$4}' | sort -u

On the client:

ssh-keyscan server.example.net | ssh-keygen -lf - | awk '{print $2$4}' | sort -u

Now we can compare the output in a diff, without needing to store things in a file:

diff <(echo '') <(echo '')

Just paste the contents of both outputs between the single quotes. (:

Or, if you’re crazy like me, you can do it in this one simple command:

diff <(ssh-keyscan server.example.net | ssh-keygen -lf - | awk '{print $2$4}' | sort -u) <(ssh -o StrictHostKeyChecking=no [email protected] "find /etc/ssh/ -name 'ssh_host_*_key.pub' -exec ssh-keygen -E sha256 -lf {} \;" 2>/dev/null | awk '{print $2$4}' | sort -u)

Simple! :D

SSH Fingerprint in DNS + DNSSEC

Ok, crazy shell shenanigans are fun to play with, but for actual production use, that’s not really scalable, right? So we can do better. If your hostname is in the DNS, and DNSSEC is enabled for your zone, we can simply store the fingerprint as a DNS record! Let’s do this as per RFC 4255.

The record type we want to create is an SSHFP record (this is e.g. supported by Cloudflare DNS); for this record to be effective, you need to make sure DNSSEC is enabled for your zone!

A SSHFP type looks something like this:

<hostname> IN SSHFP <algorithm> <fingerprint type> <fingerprint>

An example would be:

server.example.net. IN SSHFP 4 2 123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234

Where 4 means ED25519 and 2 means SHA256. It is important that the fingerprint be hex!

Now, manually converting records and things isn’t fun, so if you manually verified your host keys to be correct using the steps above, sign in to your server over SSH and run this:

ssh-keyscan -D localhost

And in theory, all you gotta do, is copy those DNS records into your DNS provider (adjusting hostnames, of course). However, I recommend only copying the SHA256 records, since SHA1 is insecure by today’s standards.

Dropbear

In a past article, I explained how to set up full volume encryption and using dropbear in the initramfs to unlock the disk.

Of course, we want to have that Dropbear SSH fingerprint in our DNS as well. Assuming the key is stored in /etc/dropbear/initramfs/dropbear_ed25519_host_key we can quickly convert it to an OpenSSH compatible key and then turning it into an SHA256 fingerprint in one go:

ssh-keygen -y -f <(dropbearconvert dropbear openssh /etc/dropbear/initramfs/dropbear_ed25519_host_key - 2>/dev/null) | awk '{print $2}' | base64 -d | sha256sum | awk '{print $1}'

Just copy and paste the output into a secondary SSHFP record (yes you can have multiple keys per hostname – but OpenSSH clients version 8.7 and newer won’t like that because the devs weren’t smart!)

OpenSSH Client Side

Of course, for all of this to work, your SSH client actually needs to check for those DNS records. We can easily enable this on the OpenSSH client by editing ~/.ssh/config and adding:

VerifyHostKeyDNS yes

Alternatively, you can pass -o VerifyHostKeyDNS=yes to ssh btw.

Conclusion

If you have access to the physical machine (or some other secure channel to access it), you simply extract the fingerprints from the host itself, then ideally, you will add them as SSHFP DNS(SEC) records, but a manual comparison will also do to be able to bootstrap yourself. (:

I run this blog in my free time, if this helped you out, or you enjoyed my shenanigans, consider donating a coffee. :3