DNSSEC Validating Resolver on OpenWRT

Posted on July 6, 2012

I have been running bind in resolver mode, with DNSSEC validation enabled on my home computer for some years. The home router was not set up to point the rest of the devices to that, so they all used non-validating dnsmasq on the router.

Recently I got a new router and installed fresh version of openwrt. It has unbound in the default app repo so I soon switched off the DNS part of dnsmasq and enabled unbound instead.

This has a drawback of not having the local network served by dynamic dns of dnsmasq, so I could no longer access mobile devices by their names. It is not such a big deal, but it irritated me. So I decided to chain the two dns servers together, to have both a validating resolver and a properly handled domain for the home network.

Unfortunately, the build of unbound on openwrt is quite rough. It is not integrated with Luci, and does not even work out of the box until you manually disable the dns functionality of dnsmasq by setting the listen port to 0. There is a howto on the web on chaining unbound “behind” dnsmasq. I wanted it the other way around, something that the guy could not achieve.

First, we want to put dnsmasq on the side. The easiest way is to change the listening port to something non-standard. This can be done from the GUI:

Network -> DHCP and DNS -> Advanced Settings

set DNS Server Port to 5553, and hit Save&Apply;. (At this point DNS will stop working on your network until you configure and launch unbound.)

Now, to the magic part. We will be configuring unbound to serve all the world as a normal caching resolver, except the local domain and the reverse zone for the local network, for which it will act as a forwarder. In the examples we assume that the local domain is ‘lan’ and the local network is 192.168.<something>. We will need to add something to the configuration file “/etc/unbound/unbound.conf”.

First, the basic forwarding directives:

forward-zone:
        name: "lan."
        forward-addr: 127.0.0.1@5553
	
forward-zone:
        name: "168.192.in-addr.arpa."
        forward-addr: 127.0.0.1@5553

These are self-explanatory. But this is not enough. We have to add a number of directives to the server section. First, unbound refuses to send DNS requests to localhost by default. To persuade it, we need this directive:

        do-not-query-localhost: no

Then, by default, unbound will try to establish trust chain. Serving a query in the “lan.” domain, it will try to get the DS record form the parent domain, in this case “.”. Of course the Internet root servers have no idea about our local “lan.” domain and will respond with NXDOMAIN. Which will break the trust chain. To prevent unbound from attempting to build the trust chain, we need to write these directives (second is for the reverse zone):

        domain-insecure: "lan."
        domain-insecure: "168.192.in-addr.arpa."

Next, by default, unbound refuses to return As and AAAAs that point to private (RFC1918) addresses. We have such addresses in the “lan.” domain, so tell it to unbound:

        private-domain: "lan."

On the other side, unbound refuses to deal with PTRs that come from private addresses, so we must override that too:

        local-zone: "168.192.in-addr.arpa" nodefault

That’s all. Now save the file, “Enable” and “Start” the “unbound” service in System -> Startup and it should work.

Should you need to troubleshoot, run "unbound -vvv" and "logread -f". With some luck, the messages will hint to the problem.