I had a rather hefty argument with a colleague today, regarding the particular
merits of DNSSEC as far as the use of SSH fingerprints is concerned. I
violently argued that, though it would be nice to have, the feature isn’t yet
ready for the general public, because SSH client’s don’t correctly implement
it. In 2006, RFC 4255 – Using DNS to Securely Publish Secure Shell
(SSH) Key Fingerprints – was published. What this RFC proposes is to store
the fingerprint of host SSH keys in the DNS in the form of SSHFP
resource
records, so that a client, upon connecting to a particular server, can be
assured that the fingerprint is valid. Without DNSSEC this feature is …
well, not really very useful, right? Anyhow, to prove a point, I told said
colleague that whether DNSSEC is in effect or not has no effect whatsoever on
the client-side handling. And here is the proof. I have the appropriate SSHFP
records in my DNSSEC-secured zone
$ dig +dnssec ubu.jpmens.org any
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27616
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;ubu.jpmens.org. IN ANY
;; ANSWER SECTION:
ubu.jpmens.org. 120 IN RRSIG A 8 3 120 20110303000000 20110217000000 50853 jpmens.org. LfwN0...d Dic=
ubu.jpmens.org. 120 IN A 192.168.1.182
ubu.jpmens.org. 120 IN RRSIG SSHFP 8 3 120 20110303000000 20110217000000 50853 jpmens.org. xk3TCDBt...gew=
ubu.jpmens.org. 120 IN SSHFP 1 1 7E7A55CEA3B8E15528665A6781CA7C35190CF0EB
ubu.jpmens.org. 120 IN SSHFP 2 1 CC17F14DA60CF38E809FE58B10D0F22680D59D08
The domain has an Address record and two SSHFP records. Now, with all
SSH clients I tested on platforms available to me, the following expected
result occurred: SSH does a DNS lookup, verifies that it found SSHFP
fingerprint records which match the host key of the host I’m connecting to,
and informs me that it has, indeed, found the information to be correct.
That is it. Bob’s your uncle. Case closed. The client continues as it always
has, and asks me whether I want to continue connecting, stores the key in its
known_hosts
file, yada, yada, yada.
I confirmed the behavior by looking at
the OpenSSH source code, and verifying it on a number of platforms
(FreeBSD, several Linuxes, and Mac OS X). My colleague however, didn’t believe
me, and set up a validating recursor, etc. to prove me wrong. He proved me
wrong. Not completely, unfortunately, because of some issues he couldn’t
readily identify, but he said it “sometimes” worked for him on Fedora. I was
bitten by the bug, and, it turns out Fedora have put in the necessary code,
without OpenSSH upstream having done it. That is a bitch. Here goes, on Fedora
14: (what you can’t see in the screen shot is the rm known_hosts
I started
off with; sorry.) Just what the doctor ordered. Now, this is half a
public apology to said colleague (sorry Stefan!), and half a rant that a
vendor add such features quite silently. No mention in the man page, hardly a
word in the changelogs, etc. I had to dig quite deeply to find it: it’s an
interaction between Fedora patches and the Glibc.
Update This topic kept me awake. How does Fedora’s SSH client know that my recursing name server has the the AD (authenticated data bit) on the query? , so I sat down and looked at bit more closely at the source code. Make or break is the getrrsetbyname() function which isn’t documented on any of my Linux machines. The function was written by Jakob Schlyter, and he clearly writes:
if the EDNS0 option is activated in resolv.conf(5), getrrsetbyname() will request DNSSEC authentication using the EDNS0 DNSSEC OK (DO) bit.
Hah! Let me see if that works: It works. At least on an Ubuntu Linux as
shown in above screen shot. (Note how I’ve set the resolver option in the
environment before invoking
ssh
.) So what is different between, say,
Fedora’s and Ubuntu’s ssh client? Fedora’s code explicitly sets
RRSET_FORCE_EDNS0
in the flags before invoking getrrsetbyname
which,
although it has a flags parameter, is documented as the flags field is
currently unused and must be zero. The function is defined (in Fedora’s
patched source) in openbsd-compat/getrrsetbyname.c
. And the moral of this
story: read the bleedin’ source code properly!”.
Update: The RES_OPTIONS trick works on openSUSE 11.3 as well.
Continued in VerifyHostKeyDNS=yessssss!.