So it turns out there’s several RFCs for adding SSH fingerprints to DNS records. They would be RFCs 4255 and 6594
What is this useful for?
Well, if you’re sshing into a box from a new location and don’t remember your SSH fingerprints (Was it the RSA/DSA/ECDSA one!?), your SSHFP record(s) will let you verify them. This also prevents MITM attacks upon first connections by verifying “out of band” over DNS. Of course, someone can come along and also MITM your DNS lookups, but that’s what DNSSEC is for.
Openssh seems to have support for SSHFP records since forever (I couldn’t find what version) via the VerifyHostKeyDNS option. Versions of openssh 5.7p1+ have support for ECDSA keys, but only added RFC6594 support (ECDSA and SHA256 hashed fingerprints) in 6.1p1. So if you have ECDSA keys, you’ll need to upgrade to 6.1p1 if you want the ssh client to verify the ECDSA fingerprints. (Type 3)
In order to have openssh verify the SSHFP records, you need to use ssh -o verifyhostkeydns=yes
or add the VerifyHostKeyDNS option to your .ssh/config.
Adding SSHFP records are as simple as doing a “` ssh-keygen -r hostname ““ on the machine you want to generate the records for and copy/pasting the output into your zone file.
You’ll get output like:
1 2 3 4 5 6 |
|
The first 3 columns being self-explanatory.
The fourth column is the type of key (1 = RSA, 2 = DSA, 3 = ECDSA), fifth column is 1 = SHA1, 2 = SHA256 and the last column being the SHA1 or SHA256 hash of the public key. (un-based64ed, as it’s stored in ssh_host
You can check your SSHFP records by doing a dig +short SSHFP example.org
Now, annoyingly openssh doesn’t do any kind of DNSSEC validation on its own, so it’s only checking the AD (authenticated data) bit on the response from the DNS resolver. Unfortunatley if you’re not running a local or remote validating resolver that you trust the network path to, an attacker can still change/strip the AD bit from the response between you and the resolver.
They can do this because it’s the resolver that verifies the DNSSEC records and simply returns a bit saying “Yes, this record is signed and valid”.
Another hurdle is that doing DNSSEC lookups almost always results in DNS packets that are larger than 512 bytes. (If you run a firewall and you block TCP DNS or UDP DNS packets larger than 512 bytes, you’re an asshole. Stop it).
This is where EDNS-0 (Extension mechanisms for DNS) comes in. You have to set your resolver options (RES_OPTIONS) to explicitly enable edns0 support. In Linux, this is “export RES_OPTIONS=edns0”.
What if you don’t have a validating resolver? Well, you could install unbound + dnssec-trigger but that’s a pain in the arse, so lets use a trick @isomer told me. We’re going to use googles public DNS resolvers, since they validate DNSSEC records now.
1 2 3 4 5 |
|
(You can use test at kyhwana.org if you’d like to see some actual valid SSHFP records.)
What the above does is run a bash shell with the mount namespace “unshared” from the rest of the system, so we can go ahead and bind newresolv.conf with the google DNS resolver IPs to /etc/resolv.conf, this only affects the bash shell we’ve started.
We then tell our resolver to use EDNS0 and then ssh to the host with verbose mode turned on, where we should see: debug1: found 6 secure fingerprints in DNS
debug1: matching host key fingerprint found in DNS
indicating that it’s found the SSHFP fingerprints and that they’re signed correctly!
Huzzah, you can now securely connect to a host that you’ve connected to before and know that you’re not being MITMed! (Assuming our DNS packets aren’t being interferred with between us and googles DNS)