DNSSEC support in OpenWrt 15.05 Chaos Calmer

DNSSEC does not require any special support on the router, since the validation is typically done by the client itself. However, caching the DNSSEC records makes validation for clients faster, and a router in a trusted network can provide DNS replies which carry the ad flag (authenticated data).

Chaos Calmer comes with dnsmasq without DNSSEC validation support by default (DNSSEC is not enabled at compile time). However, one can use the dnsmasq-full package, which provides the DNSSEC features. Without DNSSEC compiled in or enabled, dnsmasq forwards the DNSSEC records properly, however it does not validate them and therefor the DNS replies do not contain the ad flag:

# dig debian.org. SOA +dnssec

; <<>> DiG 9.9.6-P1 <<>> debian.org. SOA +dnssec
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62577
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 6, ADDITIONAL: 11
   ^^^^^^^^^^^^^^^ (no ad flag, dnsmasq did not authenticated the DNS response)

Installing of dnsmasq-full and enabling of the dnssec feature can be archived with a few commands directly from the routers console:

# opkg update
...
# opkg remove dnsmasq
...
# opkg install dnsmasq-full
...
# uci set dhcp.@dnsmasq[0].dnssec=1
# uci commit dhcp
# /etc/init.d/dnsmasq restart

After installing dnsmasq-full, and enabling the dnssec feature, the replies should carry the ad flag:

# dig debian.org. +dnssec

; <<>> DiG 9.9.6-P1 <<>> debian.org. SOA +dnssec
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9960
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 6, ADDITIONAL: 8

In case the ad flag is not present at this time, your providers DNS server likely not DNSSEC-capable. In that case, you need to specify a specific DNS server to forward your requests to, for instance the Google public DNS service:

# uci add_list dhcp.@dnsmasq[0].server=8.8.8.8
# uci add_list dhcp.@dnsmasq[0].server=8.8.4.4
# uci commit dhcp
# /etc/init.d/dnsmasq restart

There is one problem thought: If an attacker forges an answer and removes its DNSSEC records, dnsmasq silently accepts them. The dnsseccheckunsigned option avoids that by asking the upstream servers whether the domain in question really does not support DNSSEC. Hence this leads to additional DNS requests for all non DNSSEC domains… Well, security comes not for free 🙂

# uci set dhcp.@dnsmasq[0].dnsseccheckunsigned=1
# uci commit dhcp
# /etc/init.d/dnsmasq restart

Enabling the dnssec option without dnsseccheckunsigned still has advantages since it improves performance and validates signatures if present…

The domain dnssec-failed.org can be used to verify the DNS system. My providers DNS system however, did not resolve that domain anyway, hence even with disabled DNSSEC, I was not able to browse that domain. By using a DNS server which does not validate DNSSEC by itself (e.g. Level 3 Communciations public DNS servers seem not to use DNSSEC by default), one can validate the DNSSEC feature.

Without dnssec option:

Sun May 24 21:17:00 2015 daemon.info dnsmasq[26699]: query[A] dnssec-failed.org from 127.0.0.1
Sun May 24 21:17:00 2015 daemon.info dnsmasq[26699]: forwarded dnssec-failed.org to 209.244.0.3
Sun May 24 21:17:01 2015 daemon.info dnsmasq[26699]: reply dnssec-failed.org is 69.252.80.75

With dnssec option:

Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: query[A] dnssec-failed.org from 127.0.0.1
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: forwarded dnssec-failed.org to 209.244.0.3
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: dnssec-query[DNSKEY] dnssec-failed.org to 209.244.0.3
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: dnssec-query[DS] dnssec-failed.org to 209.244.0.3
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: dnssec-query[DNSKEY] org to 209.244.0.3
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: dnssec-query[DS] org to 209.244.0.3
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: dnssec-query[DNSKEY] . to 209.244.0.3
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply . is DNSKEY keytag 48613
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply . is DNSKEY keytag 19036
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply org is DS keytag 21366
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply org is DS keytag 21366
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply org is DNSKEY keytag 34023
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply org is DNSKEY keytag 56198
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply org is DNSKEY keytag 21366
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply org is DNSKEY keytag 9795
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply dnssec-failed.org is DS keytag 106
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply dnssec-failed.org is DS keytag 106
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply dnssec-failed.org is BOGUS DNSKEY
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: validation dnssec-failed.org is BOGUS
Sun May 24 21:21:52 2015 daemon.info dnsmasq[27083]: reply dnssec-failed.org is 69.252.80.75

Since the DNS zone of dnssec-failed.org contain DNSSEC records, dnsmasq validates the domain even without the dnsseccheckunsigned option.

This domain, agner.ch, currently has DNSSEC entries but I could not configure the DS records in my providers DNS system yet. Hence the DNSSEC “chain of trust” is currently broken. I wondered how dnsmasq behaves in this condition. The logqueries option enables debug output to the system log. A query for agner.ch produced the following output. While the DNS entry is considered INSECURE, dnsmasq still considers the domain name as valid and replies to the query:

Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: query[A] agner.ch from 127.0.0.1
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: forwarded agner.ch to 62.12.130.66
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: forwarded agner.ch to 193.246.253.10
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: forwarded agner.ch to 213.144.148.44
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: dnssec-query[DNSKEY] agner.ch to 62.12.130.66
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: dnssec-query[DS] agner.ch to 62.12.130.66
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: dnssec-query[DNSKEY] ch to 62.12.130.66
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: dnssec-query[DS] ch to 62.12.130.66
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: dnssec-query[DNSKEY] . to 62.12.130.66
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: reply . is DNSKEY keytag 48613
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: reply . is DNSKEY keytag 19036
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: reply ch is DS keytag 46375
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: reply ch is DNSKEY keytag 46375
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: reply ch is DNSKEY keytag 36684
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: reply ch is DNSKEY keytag 6744
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: reply agner.ch is no DS
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: validation result is INSECURE
Sun May 24 19:44:11 2015 daemon.info dnsmasq[23690]: reply agner.ch is 178.209.48.105

This behavior helps during the transition phase, since the domain keeps working even when the DS records are not yet distributed. As long as the domain has no valid DS records in the DNS providers DNS system, a complete validation is anyway not possible. Hence in that condition, the domain won’t be safe from DNS spoofing anyway.

As soon as the DS records are in my providers DNS system, dnsmasq should be able to verify the domain completely. And when removing DNSSEC support then, dnsmasq should answer with SERVFAIL… I will update this post as soon as I could verify that.

Details about the options used above can be found on the OpenWrt DNS and DHCP wiki page and the dnsmasq man-page.

Btw, to perform a host side validation with dig, one can use the +sigchase option. This is very helpful when working on deploying DNSSEC.

# dig debian.org. +sigchase
;; WE HAVE MATERIAL, WE NOW DO VALIDATION
;; VERIFYING DS RRset for org. with DNSKEY:48613: success
;; OK We found DNSKEY (or more) to validate the RRset
;; Ok, find a Trusted Key in the DNSKEY RRset: 19036
;; VERIFYING DNSKEY RRset for . with DNSKEY:19036: success

;; Ok this DNSKEY is a Trusted Key, DNSSEC validation is ok: SUCCESS

Another helpful tool is DNSViz, which fetches DNS zones recursively, validates the DNS authentication chain and visualize it online.

Leave a Comment