Over the past few years, I have been exploring various options for doing local DNSSEC validation. Validating locally is necessary in order to avoid DNS answers being forged on the path from the ISP resolvers (or from open validating resolvers) to the local network.
If validating on servers and laptops is a solved problem, doing so on mobile devices such as phones and tablets is still an open question. For these use cases, having a validating resolver running directly on a router is convenient. As it turns out, it's a pretty simple two steps process to achieve this with OpenWrt.
Dnsmasq is used within OpenWrt as both DHCP and DNS server, but we only want to use its DHCP component and let Unbound perform DNS resolution.
For doing so, after logging into LuCI, please go to: Network => DHCP and DNS => Advanced Settings.
Set the DNS server port to 0, as shown in the following screenshot:
Using opkg install:
opkg install unbound
The bundled init script supports the following commands:
/etc/init.d/unbound Syntax: /etc/init.d/unbound [command] Available commands: start Start the service stop Stop the service restart Restart the service reload Reload configuration files (or restart if that fails) enable Enable service autostart disable Disable service autostart
We start Unbound and enable autostart at boot time:
/etc/init.d/unbound start /etc/init.d/unbound enable
We also need to fetch (or update) the root anchor manually:
opkg install unbound-anchor unbound-anchor -a "/etc/unbound/root.key"
Configuring Unbound to validate answers:
# If you want to perform DNSSEC validation, run unbound-anchor before # you start unbound (i.e. in the system boot scripts). And enable: # Please note usage of unbound-anchor root anchor is at your own risk # and under the terms of our LICENSE (see that file in the source). auto-trust-anchor-file: "/etc/unbound/root.key"
We can easily verify that our local Unbound instance is answering queries:
dig version.bind CH txt +short "unbound 1.4.17"
Finally, let's try to resolve a DNSSEC secured domain:
dig statdns.net +dnssec ; <<>> DiG 9.8.3-P1 <<>> statdns.net +dnssec ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28028 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;statdns.net. IN A ;; ANSWER SECTION: statdns.net. 594 IN A 188.8.131.52 statdns.net. 594 IN RRSIG A 7 2 600 20150901000000 20140901115509 23348 statdns.net. bxWP9OzGs8v6gb9zEJHecQaBkU+BLKr8qRUi6VFPFVoKbZpHwiqkGatb rq7ov2AlnUrjs/a46xiu+bNNx8K9xQvY0f6QrBb/7RUPKPYSRNFiyLkb w9p92QFt5qr4LQL5kpddf/bnJYrVBDP3b6KZ2ph5X5x+C1hDq0HjgqZz EQg= ;; AUTHORITY SECTION: statdns.net. 594 IN NS ns1.statdns.com. statdns.net. 594 IN NS ns2.statdns.com. statdns.net. 594 IN RRSIG NS 7 2 600 20150901000000 20140901115509 23348 statdns.net. FOEKRnI3VkI8EOmBj5xqqSKwdWwdFS24FroZMwBJisN4wSbDrz/EFWaa H0UqPZKKi0ViLM2z0sg1BfEvrDxFb2G4RDGpVsx6uh4BKXeaQ/1KK2Cc IF9gCSGvgyGZMCHd/DI0EeWRU9In3JK+YVI5AGA5qG2oY3IzQl4F1ho9 8RM= ;; Query time: 4 msec ;; SERVER: 10.0.0.1#53(10.0.0.1) ;; WHEN: Sat Oct 4 17:53:53 2014 ;; MSG SIZE rcvd: 445
As we notice, the AD flag is set in the header, meaning the answer is secure.