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
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
@sindastra There was a MitM security bug effecting this setup revealed in OpenSSH just a few days ago. Apparently it's been in OpenSSH for the last decade.
So make sure you're running a patched version of OpenSSH if you're going to do this (i actually set this up last week and then the security bug was revealed the day after!).
https://go.theregister.com/feed/www.theregister.com/2025/02/18/openssh_vulnerabilities_mitm_dos/