Man står med armarna i kors och har en svart tröja med texten "DNSSEC been there done that".

Summer fun with a DNS resolver

On my way back home from the IETF93 meeting in Prague I decided to do a small summer project. For a long time I have wanted to run my own DNS resolver. Lately I had considered to run it on my Synology NAS. But my NAS is already stretched by all the applications running. So I decided to use one of my Raspberry Pi's to do the job. Now, after some runs of the washing machine, I am ready to go.

Obviously I need to have a running Raspberry Pi to begin with. That is quite easy to fix. So I took one of my Pis and installed Raspbian on it. The next step is (as always) to install all updates. Security holes are no fun, so just install the updates. There is quite a list of DNS resolvers. For my little project I searched for an open source validating resolver. So the software needs to support recursion, DNSSEC and IPv6. In the end I decided to run unbound from NLnet Labs.

Validate the key

To my surprise unbound is already available through the Debian packet manager on my Raspberry Pi. So a simple "sudo apt-get install unbound" was all that was needed to get the software installed.

Now is a good time to run the "unbound-anchor" program. Please read the warnings carefully. This is where you put the trust in DNSSEC validation. You really should validate the key installed on your machine. Yes, you should really do it!

I guess you already use IPv6 on your home network? If not you should and this little helper is a cute first service to provide on IPv6. But for this to work we need to enable IPv6 permanently on our Pi. In the file /etc/modules, add a line containing "ipv6". That should add IPv6 capability to your Pi even after rebooting.

Now if you have IPv6 from your ISP or through a tunnel you should enable the IPv6 privacy extensions. This will make unbound send out requests from ever changing IP addresses. It is easily done by adding the following lines to your /etc/sysctl.conf

[prism lang="markup"]
# IPv6 privacy extensions
net.ipv6.conf.all.use_tempaddr = 2
net.ipv6.conf.default.use_tempaddr = 2
[/prism]

If you, like me, intend to run your Pi on an ethernet cable the following lines should be added to your /etc/network/interfaces file

[prism lang="markup"]
# cable IPv6
iface eth0 inet6 static
pre-up modprobe ipv6
address dead::beef [/prism]

Where dead::beef should be replaced by a valid static IPv6 address from your network.

Somehow these small side problems seem to take more time and attention than the validating resolver setup. But now we are finally getting back to that. Before we can start our resolver we have to make some configurations here too.

My unbound configuration file /etc/unbound/unbound.conf looks like this.

[prism lang="markup"]
# Unbound configuration file for Debian.
#
# See the unbound.conf(5) man page.

server:
# server for local subnet
interface: 0.0.0.0
interface: ::0
access-control: 127.0.0.0/8 allow
access-control: 192.168.1.0/24 allow
access-control: fe80::0/16 allow
access-control: dead::beef/64 allow
verbosity: 1

# The following line will configure unbound to perform cryptographic
# DNSSEC validation using the root trust anchor.
auto-trust-anchor-file: "/etc/unbound/root.key" [/prism]

The two "interface" lines give you IPv4 and IPv6 service respectively. The important lines are the ones starting with "access-control". You need to adapt these to the IP ranges you are using.

Now we are ready to go. Start unbound and start using it for DNS resolution. You might want to install the dnsutils package. It will make it easier to check your new resolver. You could run "dig +dnssec @dead::beef iis.se any". This should give you a long answer with many records, some of which are NSEC and RRSIG, and in the header section the AD flag should be set. Next step, try the same query from another machine on your network.

If all is well you are now ready to use your new validating IPv6 enabled resolver. Most probably you use your home router's DHCP server. Configure the router to tell all clients to use your new resolver and while you're at it tell the router to not advertise your ISP's resolvers.

Now you are running your own resolver.

You want even more geek fun?

Dive deep into unbound configuration here.

Here are some ideas for improvements. Configure "private-address: 192.168.0.0/16" and "private-address: 10.0.0.0/8" to disallow private IP addresses in answers. This will protect you from some DNS attacks. But maybe you are using your own domain for your machines and these need to answer with just these private IP addresses? No problem, configure "private-domain: example.com" and you are good to go.

How about some performance improvements? There is a whole tutorial about that. Here are two options I use "prefetch: yes" and "prefetch-key: yes". These keep often used domains in the cache and provide my clients with faster answers.