The Internet Society likes my work. I aim to please…
One of the things I did in the process of importing LDNS and Unbound into FreeBSD 10 was to change the default value for VerifyHostKeyDNS
from “no” to “yes” in our OpenSSH when compiled with LDNS support (which can be turned off by adding WITHOUT_LDNS=YES
to /etc/src.conf
before buildworld).
The announcement the ISOC blog post refers to briefly explains my reasons for doing so:
I consider this a lesser evil than “ask” (aka “train the user to type ‘yes’ and hit enter”) and “no” (aka “train the user to type ‘yes’ and hit enter without even the benefit of a second opinion”).
There were objections to this (which I’m too lazy to dig up and quote) along the lines of:
- Shouldn’t OpenSSH tell you that it found and used an SSHFP record?
- Shouldn’t
known_hosts
entries take precedence over SSHFP records? - Shouldn’t OpenSSH store the key in
known_hosts
after verifying it against an SSHFP record?
The answer to all of the above is “yes, but…”
Here is how host key verification should work, ideally:
- Obtain host key from server
- Gather cached host keys from various sources (
known_hosts
, SSHFP, LDAP…) - If we found one or more cached keys:
- Check for and warn about inconsistencies between these sources
- Check for and warn about inconsistencies between the cached key and what the server sent us
- If we got a match from a trusted source, continue connecting
- Inform the user of any matches from untrusted sources
- Display the key’s fingerprint
- Ask the user whether to:
- Store the server’s key for future reference and continue connecting
- Continue connecting without storing the key
- Disconnect
The only configuration required here is a list of trusted and untrusted sources, the difference being that a match or mismatch from a trusted source is normative while a match or mismatch from an untrusted source is merely informative.
Unfortunately, in OpenSSH, SSHFP support seems to have been grafted onto the existing logic rather than integrated into it. Here’s how it actually works:
- Obtain host key from server
- If
VerifyHostKeyDNS
is “yes” or “ask”, look for SSHFP records in DNS - If an SSHFP record was found:
- If it matches the server’s key:
- If it has a valid DNSSEC signature and
VerifyHostKeyDNS
is “yes”, continue connecting - Otherwise, set a flag to indicate that a matching SSHFP record was found
- If it has a valid DNSSEC signature and
- Otherwise, warn about the mismatch
- If it matches the server’s key:
- Look for cached keys in the user and system host key files
- If we got a match from the host key files, continue connecting
- If we did not find anything in the host key files:
- If we found a matching SSHFP record, tell the user
- Ask the user whether to:
- Store the server’s key for future reference and continue connecting
- Disconnect
- If we found a matching revoked key in the host key files, warn the user and terminate
- If we found a different key in the host key files, warn the user and terminate
Part of the problem is that at the point where we tell the user that we found a matching SSHFP record, we no longer know whether it was signed. By switching the default for VerifyHostKeyDNS
to “yes”, I’m basically saying that I trust DNSSEC more than I trust the average user’s ability to understand the information they’re given and make an informed decision.