DNSSEC Validation with Unbound on a Raspberry

To overcome the chicken-or-egg problem for DNSSEC (“I don’t need a DNSSEC validating resolver if there are no signed zones”), let’s install the DNS server Unbound on a Raspberry Pi for home usage. Up then, domain names are DNSSEC validated. I am listing the commands to install Unbound on a Raspberry Pi as well as some further commands to test and troubleshoot it. Finally I am showing a few Wireshark screenshots from a sample iterative DNS capture. Here we go:

It is really simple to operate an Unbound DNS resolver locally on a Raspberry Pi. Merely an installation and some config changes. That’s it. The Unbound package on a Raspbian Linux of Unbound validates DNSSEC by default. Great!

Installation

I am using an “old” Raspberry Pi 1 Model B with Raspbian GNU/Linux 7 (wheezy) and kernel 4.1.13+. The version of Unbound which comes with this OS is not the newest one (1.4.17-3+deb7u2), but it fits. The installation is really simple:

1

2

sudo apt-getupdate

sudo apt-getinstall unbound

The Unbound server starts automatically. Have look at the listening ports with:

1

2

3

4

5

6

7

pi@jw-pi01~$sudo netstat-tulpen|grepunbound

tcp00127.0.0.1:530.0.0.0:*LISTEN0731268027897/unbound

tcp00127.0.0.1:89530.0.0.0:*LISTEN0731268427897/unbound

tcp600::1:53:::*LISTEN0731267627897/unbound

tcp600::1:8953:::*LISTEN0731268227897/unbound

udp00127.0.0.1:530.0.0.0:*0731267827897/unbound

udp600::1:53:::*0731267427897/unbound

Unbound works out of the box for queries from the localhost. In order to allow queries from any host, the configuration file must be edited. It is stored at
/etc/unbound/unbound.conf . Note that the config has already DNSSEC validation enabled!

1

2

3

4

5

6

7

8

9

10

11

12

pi@jw-pi01/etc/unbound$catunbound.conf

# Unbound configuration file for Debian.

#

# See the unbound.conf(5) man page.

#

# See /usr/share/doc/unbound/examples/unbound.conf for a commented

# reference config file.

server:

# The following line will configure unbound to perform cryptographic

# DNSSEC validation using the root trust anchor.

auto-trust-anchor-file:"/var/lib/unbound/root.key"

Now, to allow queries add the following lines within the “server:” paragraph:

1

2

3

4

interface:0.0.0.0

interface:::0

access-control:0.0.0.0/0allow

access-control:::0/0allow

check the config:

1

2

pi@jw-pi01~$unbound-checkconf

unbound-checkconf:no errors in/etc/unbound/unbound.conf

and restart the server:

1

2

pi@jw-pi01~$sudo service unbound restart

[ok]Restarting recursive DNS server:unbound.

That’s it! To see a list of all configuration options click here. If you only wanted to install Unbound you’re already done!

-> The following information are only for further analysis etc.

Root Hints & Root Key

Unbound uses a list of the root servers as well as the root dnskey for its DNSSEC validation. Both should be updated regularly to avoid DNS problems in case of real root server changes. To update and use the root-hints file (for the list of root-servers), download the official list:

Of course, a failure in DNSSEC leads to a SERVFAIL (line 7) without any answer:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

pi@ntp1:~$dig@192.168.7.5fail03.dnssec.works

;<<>>DiG9.9.5-9+deb8u6-Raspbian<<>>@192.168.7.5fail03.dnssec.works

;(1server found)

;;global options:+cmd

;;Got answer:

;;->>HEADER<<-opcode:QUERY,status:SERVFAIL,id:42531

;;flags:qr rd ra;QUERY:1,ANSWER:0,AUTHORITY:0,ADDITIONAL:1

;;OPT PSEUDOSECTION:

;EDNS:version:0,flags:;udp:4096

;;QUESTION SECTION:

;fail03.dnssec.works.INA

;;Query time:111msec

;;SERVER:192.168.7.5#53(192.168.7.5)

;;WHEN:Thu Jun0917:24:25CEST2016

;;MSG SIZE rcvd:48

Good.

In order to view the status of Unbound, use the following commands:
unbound-control status and
unbound-control stats_noreset :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

pi@jw-pi01~$sudo unbound-control status

version:1.4.17

verbosity:1

threads:1

modules:2[validator iterator]

uptime:11744seconds

unbound(pid28021)isrunning...

pi@jw-pi01~$sudo unbound-control stats_noreset

thread0.num.queries=120

thread0.num.cachehits=18

thread0.num.cachemiss=102

thread0.num.prefetch=0

thread0.num.recursivereplies=102

thread0.requestlist.avg=0.54902

thread0.requestlist.max=18

thread0.requestlist.overwritten=0

thread0.requestlist.exceeded=0

thread0.requestlist.current.all=0

thread0.requestlist.current.user=0

thread0.recursion.time.avg=0.201798

thread0.recursion.time.median=0.17367

total.num.queries=120

total.num.cachehits=18

total.num.cachemiss=102

total.num.prefetch=0

total.num.recursivereplies=102

total.requestlist.avg=0.54902

total.requestlist.max=18

total.requestlist.overwritten=0

total.requestlist.exceeded=0

total.requestlist.current.all=0

total.requestlist.current.user=0

total.recursion.time.avg=0.201798

total.recursion.time.median=0.17367

time.now=1465816919.018412

time.up=460.981236

time.elapsed=460.981236

To list the current values of options, e.g. the default values, use unbound-checkconf with the “-o parameter” option, such as:

1

2

3

4

5

6

7

8

9

10

11

12

pi@jw-pi01~$sudo unbound-checkconf-ouse-syslog

yes

pi@jw-pi01~$sudo unbound-checkconf-overbosity

1

pi@jw-pi01~$sudo unbound-checkconf-odo-ip6

yes

pi@jw-pi01~$sudo unbound-checkconf-odo-ip4

yes

pi@jw-pi01~$sudo unbound-checkconf-odo-udp

yes

pi@jw-pi01~$sudo unbound-checkconf-odo-tcp

yes

To dump the cache for further investigations, use this:

1

2

pi@jw-pi01~$sudo unbound-control dump_cache|less

pi@jw-pi01~$sudo unbound-control dump_cache|grepwebernetz.net

Per default, the log messages are sent to syslog. That is, they are stored in the default /var/log/syslog file which can be followed with:

1

pi@jw-pi01~$tail-f/var/log/syslog|grepunbound

To increase the verbosity level, add/change the verbosity line in the unbound.conf file. The default is “1”. A level of “2” already logs every DNS request. Use it for troubleshooting but be careful with it!

DNS Server on LAN

To use this Unbound DNS server for all clients in the LAN, it must be announced via DHCP as the DNS server. In my home network I have an AVM FRITZ!Box router which connects to the Internet via FTTH. The settings are as follows. I am using the static IPv4 address as well as the link-local IPv6 address as the DNS server address:

IPv4 local DNS server.

And IPv6, too.

Tested from another Raspberry Pi, this leads to the following resolv.conf entries:

1

2

3

4

5

pi@ntp1:~$cat/etc/resolv.conf

# Generated by resolvconf

domain fritz.box

nameserver192.168.7.5

nameserver fe80::ba27:ebff:fec9:1637%eth0

This is a test from a Linux client. Once more, note the “ad” flag in line 7:

So maybe it’s not a good idea to use the link-local address for the DNS server. Maybe I will disable the “IPv6 DNS Server” option in my home network since the availability of a legacy IP DNS server perfectly fits for dual-stacked clients.

Sample Wireshark Screenshots

This is a small tcpdump capture on the Raspberry Pi, displayed with Wireshark. It shows an iterative (!) lookup of “licher.de” with its initial request from the client to the Raspberry Pi, the iterative lookup and the final answer to the client. Also note the “Statistics -> DNS” summary within Wireshark which can be used for troubleshooting, too:

Initial request from the client.

Iterative query (1).

Iterative query (2).

Reply to the client.

Wireshark DNS Statistics.

Conclusion

Now, all DNS answers are DNSSEC validated. Really all DNS answer? Well, actually, no. Only those which are DNSSEC signed. However, we solved the chicken-egg problem and are now able to validate DNSSEC answers.