Implementing DNSSEC on OpenWrt


Updated: 2015-12-28

DNSSEC is a set of extensions to DNS. As Wikipedia states, it is

a set of extensions to DNS which provide to DNS clients (resolvers) origin authentication of DNS data, authenticated denial of existence, and data integrity, but not availability or confidentiality.

While it is by no means a perfect solution, it does address some (by now obvious) flaws in the DNS design. Other solutions exist, like e.g. DNSCrypt, but that takes a different approach.

Possible implementations

There are two ways to implement DNSSEC on your OpenWrt router, depending on your needs.

  • Replace the default dnsmasq package (which is a trimmed version) by its dnsmasq-full sibling, which supports DNSSEC validation on DNS replies from upstream (recursive) nameservers. That, however, still requires you to set DNS servers that support DNSSEC. If your ISP's nameservers don't, you need to go out looking for ones that do (and preferrably are geographically close enough so DNS isn't too slow).
  • Install a validating, recursive, and caching DNS resolver like Unbound. The advantage over dnsmasq is you don't pass on your DNS queries to your ISP or another third party; Unbound handles them. That also means you don't need to go looking for DNS servers with DNSSEC support. Unlike dnsmasq though, it does not integrate DHCP and DNS, so you need to keep dnsmasq around as the DHCP server.
    Unbound can be used in the following configurations. Each has its advantages and disadvantages.
    • Stand-alone
      Rely solely on Unbound. This is fine if you don't need to be able to match IPs to hostnames in your LAN (e.g. ping your server by its name, not its IP), or don't mind setting that all up manually through each client's (or your router's) /etc/hosts file.
    • Primary DNS resolver
      Have Unbound handle all DNS queries, and hand over the LAN queries to dnsmasq. You keep the advantages of dnsmasq for local queries (integration with DHCP). You'll also be able to see the 'ad' flag for queries on domains supporting DNSSEC (more on that further).
    • Secondary DNS resolver
      Have dnsmasq handle all LAN DNS queries (for which it will consult its DHCP leases), and hand over all other queries to Unbound. This setup has a few downsides, and you'll need to patch up things to get everything fully functional. If you prefer this scenario, read on here.

Unbound as a primary DNS resolver

Unbound configuration

You'll need the unbound and unbound-anchor packages installed - pull them in through opkg. After that, fetch the root anchor manually.

# unbound-anchor -a "/etc/unbound/root.key"

The default configuration works fine on OpenWrt but is heavily commented so it lacks some overview. You'll need to add the following, presuming your LAN is on 192.168.x.x. I'm assuming you do run a stock OpenWrt setup (e.g. no custom hostname for the router/DNS resolver). To hand off LAN queries to dnsmasq, Unbound requires a bit of modification.

# vim /etc/unbound/unbound.conf
interface: 192.168.1.1
interface: 127.0.0.1
do-not-query-localhost: no
access-control: 192.168.1.0/24 allow
local-data: "OpenWrt. 86400 IN A 192.168.1.1"
local-data: "OpenWrt.lan. 86400 IN A 192.168.1.1"
local-data: "1.1.168.192.in-addr.arpa 86400 IN PTR OpenWrt.lan."
local-zone: "1.168.192.in-addr.arpa" nodefault
domain-insecure: "lan."
domain-insecure: "1.168.192.in-addr.arpa."
private-domain: "lan."
prefetch: yes

A bit of explanation:

  • interface: the interface Unbound will listen on. By default this is set to 0.0.0.0 - wide open. If you happen to have a separate guest network, make sure to add the correct IP as well (e.g. 192.168.2.1). If you have services on the router querying the DNS server (like a dynamic DNS updater), you need to add localhost as well.
  • do-not-query-localhost: allow DNS queries on localhost - needed to interface with dnsmasq.
  • access-control: the client range allowed to query the DNS server.
  • local-data and local-zone: define OpenWrt and OpenWrt.lan as the DNS server. Do this for every IP the server listens on, if you don't stick with 0.0.0.0.
  • domain-insecure: tell Unbound that the LAN has no support for DNSSEC (Unbound has DNSSEC support on by default).
  • prefetch: this will make Unbound refresh popular records when they're about to expire.

We'll also need to forward the LAN DNS queries to dnsmasq. For that, we add the following to unbound.conf:

forward-zone:
    name: "lan."
    forward-addr: 127.0.0.1@54

forward-zone:
    name: "1.168.192.in-addr.arpa."
    forward-addr: 127.0.0.1@54
Dnsmasq configuration

All we need to do here is change dnsmasq's listening port. I set it to 54:

# vim /etc/config/dhcp
config dnsmasq
    [...]
    option port '54'

I noticed Windows had trouble resolving URLs using DHCP assigned IP addresses. A new OpenELEC installation even had Google's DNS servers set, the LAN DNS server being absent. Having dnsmasq communicate the DNS server (and, while you're at it, set the gateway too) should fix this. Add the following lines:

config dhcp 'lan'
    [...]
    list dhcp_option '3,192.168.1.1'
    list dhcp_option '6,192.168.1.1'

After that, stopping and starting both services (Unbound does not reread its configuration on reload, it seems) should do the trick - if not, reboot. If you have a separate guest network, be sure to add similar options to it as well.

Testing

You can perform a few checks to see if everything is working, like local DNS queries:

$ host amalthea
amalthea.lan has address 192.168.1.20

And to confirm Unbound is your primary DNS resolver:

$ dig version.bind CH txt +short
"unbound 1.5.4"

To see if DNSSEC is functional, query a domain that you know has DNSSEC turned on. You should see the ad (authenticated data) flag appear.

$ dig debian.org +dnssec|grep -m 1 flags
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 7, AUTHORITY: 0, ADDITIONAL: 1

You should also see a resource record signature (RRSIG):

$ dig debian.org +dnssec|grep RRSIG
debian.org.     300 IN  RRSIG   A 8 2 300 20151004125637 20150825115637 11496 debian.org. myLV5JBFI7GCrFEAjrlJESlMI61WbeSp/XOO14rN7qHozGMIDgqu7cOW mON+n2tj91CbqdnsDe3xj7U8052aGfy4BqAqpQjsKziSna/AF1o/7m9D WR0mm+PB/KgCijZDyRnaqJxJGQPOdgCU5XsvvpUlKU+jgwHTptHA7LoF e1J918YQO3/+kEmpIEKiUax+I3Y2vODl3o57qnwYSZBLNMsH5fHSVspi 9+zAQiMp0I5hSzGNV/rFKBq8h6aldBXl

NTP servers and insecure domains

If your router's internal clock happens to be off, Unbound will refuse to resolve. You can fix this by whitelisting an NTP server:

domain-insecure: fr.pool.ntp.org

My dynamic DNS domain (running this blog) gave problems with DNSSEC - so I had to add it to unbound.conf as well:

domain-insecure: volatilesystems.org

You can (or should) do so for any domain you encounter giving you problems.