[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

openssh, VerifyHostKeyDNS, DNSSEC and libunbound - a proposal



Dear SSH Maintainers,

Recently, DSA had the opportunity to address a DD's request that we
begin using SSH certificates to authenticate Debian Project machines to
ssh clients.  We provided a lengthy reply (see attached), the summary of
which is "we publish SSHFP records; use VerifyHostKeyDNS; set up a local
caching resolver to avoid MITM attacks".

It seems rather cumbersome to have users install a local chaching
resolver in order to secure the last mile of DNS queries, so we
postulated whether it would be possible to modify openssh such that the
ssh client could perform the queries itself.

Turns out that this is quite straight forward to implement (see patch,
attached).  The openssh developers have encapsulated the DNS query code
well, and the unbound developers have exposed the right amount of DNS
information while still giving access to the raw response.

Since we are quite concerned that DDs aren't managing their known_hosts
in a secure fashion, we are keen on using SSHFP records .. but only if
the DNSSEC last mile issue can be addressed in a relatively easy way for
users.

We propose that the openssh package be modified as follows:

(1) introduce a new ssh_config directive: UnboundConfiguration

(2) modify getrrsetbyname() such that if UnboundConfiguration is set,
the unbound resolver is used; if not, the libc resolver is used

In this way, the standard mode of operation for ssh remains unchanged by
default.  Users who would like to use SSHFP records in a secure manner
would need to set the configuration directive.

We would love to see this functionality in wheezy.  Our proposed approach is low risk in that it leaves the default mode of operation untouched.  From a package maintainer's perspective, it introduces a divergence from upstream (although we are keen to submit this upstream, also) and a new dependency on libunbound.

Please let us know your thoughts on the above.

Thanks very much,

Luca

-- 
Luca Filipozzi
Member, Debian System Administration Team
On Tue, May 01, 2012 at 07:14:48PM +0000, Debian Developer via RT wrote:
> OpenSSH allows to sign the host pubkeys with another ssh key, which
> then can be used in users knownhosts files as a certificate authority.
> 
> That way, Debian people can use a simple one line entry for all the
> hosts, rather than manually importing and updating a list deduced from
> the db.debian.org overview.

Thanks for submitting this request.

The introduction of an x509-like PKI for user-to-host (user certs) and
host-to-user (host certs) is interesting.  The biggest challenge is the
certificate revocation mechanism.

With traditional ssh-rsa host keys (the ones we publish in LDAP and in
/etc/ssh/ssh_known_hosts on every Debian hosts), if we need to revoke a
key (because a server is decommissioned or, worse, compromised), we need
only remove the public key from LDAP and /etc/ssh/ssh_known_hosts.

Unfortunately, this assumes that end users, such as yourself, are doing
two things: (1) that you're careful about which certificates you accept
(or are using StrictHostKeyChecking yes) and (2) that you're updating
your ~/.ssh/known_hosts from an authoritative source periodically.

These are fairly significant assumptions.  We strongly suspect that the
majority of users, even the majority of Debian Developers, are not as
careful about host keys as we'd like them to be.

Even if they are doing these things, the mechanism is pull-based rather
than pushed or triggered.  It would be very good if, upon revocation of
a compromised server's cert, users would not need be required to do
anything.

In x509 terms, this would be the equivalent of having a properly working
certificate revocation list (CRL) mechanism (which has its own set of
problems but let's assume it works for the purposes of this ticket).

The introduction of an x509-like PKI mechanism for ssh that does not
include a decent CRL mechanism strikes us as being just as weak as the
ssh-rsa host key mechanism.  We can publish a list of revoked certs but
users would have to import these into their known_hosts... so we're back
to where we started!

Perhaps the correct mechanism is SSHFP (SSH FingerPrint) DNS resource
records.  Users (rightfully so) would like a pain-free configuration of
their ssh clients and administrators would like an automated revocation
mechanism.  The SSHFP DNS resource records provide this and we're
already publishing SSHFP records for Debian machines!

Put these two lines in your .ssh/config (remember to delete debian hosts
from your .ssh/known_hosts) and you're good to go (sort of; read below):

  StrictHostKeyChecking yes
  VerifyHostKeyDNS yes

Why "sort of"?  The challenge with SSHFP records is authenticating
them... that is, preventing MITM attacks.  DNSSEC can help, here, but
only if you trust the DNS replies you're receiving (which means trusting
your resolver and having a secure connection to it).  The best way to
ensure this is to install a local caching resolver (such as unbound) and
pointing libc at it (by setting "nameserver 127.0.0.1" in your
resolv.conf).  Rather than having libc contacting some remote resolver
(like your ISP), it'll now contact your local resolver.

Having to set up a local resolver just to get secure SSHFP records is
rather cumbersome.  Perhaps the long-term solution is to link ssh
against libunbound or libldns so that it resolves secure SSHFP records
directly.

In conclusion, Debian won't be using the x509-like PKI infrastructure
for the above stated reasons.  If you trust your resolver (install
unbound!), you can use our SSHFP records to achieve what you want.

Let me know if you have questions or comments,

Luca

-- 
Luca Filipozzi
Member, Debian System Administration Team
diff -ru openssh-5.9p1/openbsd-compat/getrrsetbyname.c openssh-5.9p1-new/openbsd-compat/getrrsetbyname.c
--- openssh-5.9p1/openbsd-compat/getrrsetbyname.c	2009-07-12 18:38:23.000000000 -0700
+++ openssh-5.9p1-new/openbsd-compat/getrrsetbyname.c	2012-05-08 10:24:12.000000000 -0700
@@ -45,6 +45,7 @@
 
 /* OPENBSD ORIGINAL: lib/libc/net/getrrsetbyname.c */
 
+#include <unbound.h>
 #include "includes.h"
 
 #ifndef HAVE_GETRRSETBYNAME
@@ -195,7 +196,8 @@
 	struct rdatainfo *rdata;
 	int length;
 	unsigned int index_ans, index_sig;
-	u_char answer[ANSWER_BUFFER_SIZE];
+	struct ub_ctx *ub_ctx = NULL;
+	struct ub_result *ub_result;
 
 	/* check for invalid class and type */
 	if (rdclass > 0xffff || rdtype > 0xffff) {
@@ -215,41 +217,37 @@
 		goto fail;
 	}
 
-	/* initialize resolver */
-	if ((_resp->options & RES_INIT) == 0 && res_init() == -1) {
+	/* create context */
+	ub_ctx = ub_ctx_create();
+	if (!ub_ctx) {
 		result = ERRSET_FAIL;
 		goto fail;
 	}
 
-#ifdef DEBUG
-	_resp->options |= RES_DEBUG;
-#endif /* DEBUG */
-
-#ifdef RES_USE_DNSSEC
-	/* turn on DNSSEC if EDNS0 is configured */
-	if (_resp->options & RES_USE_EDNS0)
-		_resp->options |= RES_USE_DNSSEC;
-#endif /* RES_USE_DNSEC */
-
-	/* make query */
-	length = res_query(hostname, (signed int) rdclass, (signed int) rdtype,
-	    answer, sizeof(answer));
-	if (length < 0) {
-		switch(h_errno) {
-		case HOST_NOT_FOUND:
-			result = ERRSET_NONAME;
-			goto fail;
-		case NO_DATA:
-			result = ERRSET_NODATA;
-			goto fail;
-		default:
-			result = ERRSET_FAIL;
-			goto fail;
-		}
+	/* load unbound configuration : FIXME - filename */
+	if ((result = ub_ctx_config(ub_ctx, "/opt/local/etc/unbound/unbound.conf")) != 0) {
+		result = ERRSET_FAIL;
+		goto fail;
+	}
+
+	/* execute query */
+	if ((result = ub_resolve(ub_ctx, (char*)hostname, rdtype, rdclass, &ub_result)) != 0) {
+		result = ERRSET_FAIL;
+		goto fail;
+	}
+
+	if (ub_result->nxdomain) {
+		result = ERRSET_NONAME;
+		goto fail;
+	}
+
+	if (!ub_result->havedata) {
+		result = ERRSET_NODATA;
+		goto fail;
 	}
 
 	/* parse result */
-	response = parse_dns_response(answer, length);
+	response = parse_dns_response(ub_result->answer_packet, ub_result->answer_len);
 	if (response == NULL) {
 		result = ERRSET_FAIL;
 		goto fail;
@@ -333,6 +331,8 @@
 		}
 	}
 	free_dns_response(response);
+	ub_resolve_free(ub_result);
+	ub_ctx_delete(ub_ctx);
 
 	*res = rrset;
 	return (ERRSET_SUCCESS);
@@ -342,6 +342,10 @@
 		freerrset(rrset);
 	if (response != NULL)
 		free_dns_response(response);
+	if (ub_result != NULL)
+		ub_resolve_free(ub_result);
+	if (ub_ctx != NULL)
+		ub_ctx_delete(ub_ctx);
 	return (result);
 }
 

Attachment: signature.asc
Description: Digital signature


Reply to: