Introduction
Unbound is a validating, recursive, caching open-source DNS resolver primarily developed by NLnet Labs, VeriSign Inc., Nominet, and Kirei. To help increase our online privacy, unbound supports DNS-over-TLS and DNS-over-HTTPS which allows clients to encrypt their communication. Unbound runs on FreeBSD, OpenBSD, NetBSD, MacOS, Linux and Microsoft Windows, with packages available for most platforms.
This guide is intended for unbound installation in conjunction with Pi-hole and therefore Pi-hole is the only prerequisite.
The Pi-hole is a DNS sinkhole that protects your devices from unwanted content, without installing any client-side software. (documentation)
You can install and configure Pi-hole with the command below on any distribution which utilizes systemd or sysvinit. To verify if your distribution is supported, check out Supported Operating Systems section in the Pi-hole documentation. This guide will not go through the Pi-hole installation steps.
$ curl -sSL https://install.pi-hole.net | bash
Personal setup
We decided to install unbound on a dedicated system connected directly to our router via ethernet. Here is the complete list of hardware and accessories we have used for this project:
- Raspberry Pi 4 Model B (4GB RAM)
- OS : Raspberry Pi OS Lite (32-bit)
- Raspberry Pi 15W USB-C Power Supply
- Flirc Raspberry Pi 4 Case
- SanDisk micro SDHC 32 GB Extreme Pro
If you are familiar with how does DNS and unbound work, feel free to skip to the Installation section.
How does it actually work?
In this section, we will learn how does DNS work, what is the difference between recursive and iterative DNS resolver, how does unbound work and what are the pros and the cons of running your own recursive DNS resolver at home.
How does DNS actually work?
After a user types a domain name (e.g. “thehackernews.com”) into their browser, DNS lookup is triggered. A group of DNS servers then help to find the IP address for the domain and return it back to the user’s computer.
Because we will be installing and setting up unbound as a recursive DNS resolver, let’s talk about the difference between recursive and iterative DNS resolver.
Recursive DNS resolver is a middleman between a client (you) and a DNS nameserver. This type of a DNS resolver is recursively querying other DNS servers until it has an IP address for the requested domain, which is returned to the client. If a DNS resolver has already performed the same query in the recent past, this DNS query is cached and when performed again, our resolver respond to us with the cashed data instead of querying other DNS servers.
Here are other DNS servers that are queried when using a recursive DNS resolver:
- DNS root nameserver
- responds by directing the recursive resolver to a TLD nameserver, based on the extension of that domain (.com, .net, .org, etc.)
- DNS TLD nameserver
- maintains information for all the domain names that share a common domain extension (.com, .net, .org, etc.)
- DNS authoritative nameserver
- contains information specific to the domain name it serves and it also provides a recursive resolver with the IP address of that server found in the DNS A record
- if the domain has a CNAME record it will provide the recursive resolver with an alias domain which means the recursive resolver will have to perform a new DNS lookup for this record
Let’s visualize what a DNS lookup looks like when a client is using a recursive DNS resolver.
On the other hand, when using an iterative DNS resolver client will allow a DNS server to return the best answer it can. If the queried DNS server does not have a match for the query name, it will return a referral to a DNS server authoritative for a lower level of the domain namespace. The DNS client will then make a query to the referral address. This process continues with additional DNS servers down the query chain until either an error or timeout occurs.
Now let’s visualize what a DNS lookup looks like when a client is using a iterative DNS resolver.
How does unbound work?
In a few steps below, we will describe what are the differences when running only a Pi-hole and a Pi-hole with unbound.
Standard Pi-hole configuration:
- Client asks the Pi-hole who is
domain.com
. - Pi-hole will check its cache and reply if the answer is known.
- Pi-hole will check the blocking lists and reply if the domain is blocked.
- If neither 2. nor 3. point is true, the Pi-hole forwards the DNS request to the configured upstream DNS (usually it is default one from your ISP)
- When Pi-hole receives the answer, it will reply to a client with the answer.
- Lastly, Pi-hole will cache the answer so it will be able to respond faster next time a client queries the same domain.
- Client asks the Pi-hole who is
Pi-hole setup with unbound as a local recursive DNS resolver:
- Client asks the Pi-hole who is
domain.com
. - Pi-hole will check its cache and reply if the answer is known.
- Pi-hole will check the blocking lists and reply if the domain is blocked.
- If neither 2. nor 3. point is true, the Pi-hole forwards the DNS request to the local recursive DNS resolver = unbound.
- Unbound will send a query to the DNS root servers asking who is handling
.com
. - The root server answers with a referral to the TLD (Top level domain) server for
.com
. - Unbound will send a query to one of the TLD DNS server for
.com
asking who is handlingdomain.com
. - The TLD server answers with a referral to the authoritative name servers for
domain.com
. - Unbound will send a query to the authoritative name servers asking what is the IP for
domain.com
. - The authoritative server will answer with the IP address of the domain
domain.com
. - Unbound will send the reply to Pi-hole which will reply with the answer to a client.
- Lastly, Pi-hole will cache the answer so it will be able to respond faster next time a client queries the same domain.
- Client asks the Pi-hole who is
Pros and Cons
- Pros
- Privacy : Directly contacting the responsive servers means that no server can fully log the exact paths you’re going (e.g. Google DNS servers will only be asked if you want to visit a Google website).
- Cons
- When traversing the path for the first, especially visiting a website for the first time, it may be slower than when we are using big DNS providers which have answers for common used domains cached. The first request to a formerly unknown TLD may take up to a second. Subsequent requests to domains under the same TLD usually complete in < 0.1s. Fortunately, our setup will be configured for efficient caching to minimize the number of queries that will actually have to be performed.
Installation
Unbound can be simply installed from a package manager with the command below, if you are using apt
as a package manager.
$ sudo apt install unbound
Or you can download unbound from Github and then compile and install it with the following command.
$ ./configure && make && make install
If you are not installing unbound from a package manager, you have to download the current root hints file (list of primary root servers). After downloading this file, you will need to uncomment the
root-hints:
configuration line in the suggested config file provided below.
$ wget https://www.internic.net/domain/named.root -qO- | sudo tee /var/lib/unbound/root.hints
Add a cronjob or run this command roughly every six months to update the root.hints file.
Configuration
Now we need to create a config file called pi-hole.conf
in /etc/unbound/unbound.conf.d/
directory. Run the following command to create such a file and then copy the content of suggested config file provided to us from an official unbound documentation.
$ sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf
Suggested config file
# Unbound config file
server:
# If no logfile is specified, syslog is used
# logfile: "/var/log/unbound/unbound.log"
verbosity: 0
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
# May be set to yes if you have IPv6 connectivity
do-ip6: no
# You want to leave this to no unless you have *native* IPv6. With 6to4 and
# Terredo tunnels your web browser should favor IPv4 for the same reasons
prefer-ip6: no
# Use this only when you downloaded the list of primary root servers!
# If you use the default dns-root-data package, unbound will find it automatically
#root-hints: "/var/lib/unbound/root.hints"
# Trust glue only if it is within the server's authority
harden-glue: yes
# Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
harden-dnssec-stripped: yes
# Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
# see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
use-caps-for-id: no
# Reduce EDNS reassembly buffer size.
# IP fragmentation is unreliable on the Internet today, and can cause
# transmission failures when large DNS messages are sent via UDP. Even
# when fragmentation does work, it may not be secure; it is theoretically
# possible to spoof parts of a fragmented DNS message, without easy
# detection at the receiving end. Recently, there was an excellent study
# >>> Defragmenting DNS - Determining the optimal maximum UDP response size for DNS <<<
# by Axel Koolhaas, and Tjeerd Slokker (https://indico.dns-oarc.net/event/36/contributions/776/)
# in collaboration with NLnet Labs explored DNS using real world data from the
# the RIPE Atlas probes and the researchers suggested different values for
# IPv4 and IPv6 and in different scenarios. They advise that servers should
# be configured to limit DNS messages sent over UDP to a size that will not
# trigger fragmentation on typical network links. DNS servers can switch
# from UDP to TCP when a DNS response is too big to fit in this limited
# buffer size. This value has also been suggested in DNS Flag Day 2020.
edns-buffer-size: 1232
# Perform prefetching of close to expired message cache entries
# This only applies to domains that have been frequently queried
prefetch: yes
# One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks or on a single machine, it should be unnecessary to seek performance enhancement by increasing num-threads above 1.
num-threads: 1
# Ensure kernel buffer is large enough to not lose messages in traffic spikes
so-rcvbuf: 1m
# Ensure privacy of local IP ranges
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 10.0.0.0/8
private-address: fd00::/8
private-address: fe80::/10
After saving pi-hole.conf
configuration file, we will start our local DNS recursive server with the command below.
$ sudo service unbound restart
Testing DNS lookup
Finally, we will test our unbound recursive DNS resolver with a dig
command querying a DNS record for a pi-hole.net
domain. Notice status value NOERROR
and the IP address for our requested domain.
$ dig pi-hole.net @127.0.0.1 -p 5335
; <<>> DiG 9.16.22-Raspbian <<>> pi-hole.net @127.0.0.1 -p 5335
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6970
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;pi-hole.net. IN A
;; ANSWER SECTION:
pi-hole.net. 300 IN A 3.18.136.52
;; Query time: 19 msec
;; SERVER: 127.0.0.1#5335(127.0.0.1)
;; WHEN: Thu Jan 27 18:41:51 CET 2022
;; MSG SIZE rcvd: 56
Testing DNSSEC
Last step before changing our network settings will be testing DNSSEC validation using once again dig
command.
The first command should give us a status report of SERVFAIL
and no IP address.
$ dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5335
; <<>> DiG 9.16.22-Raspbian <<>> sigfail.verteiltesysteme.net @127.0.0.1 -p 5335
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 22604
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;sigfail.verteiltesysteme.net. IN A
;; Query time: 299 msec
;; SERVER: 127.0.0.1#5335(127.0.0.1)
;; WHEN: Thu Jan 27 18:03:00 CET 2022
;; MSG SIZE rcvd: 57
The second command should give us NOERROR
and IP address.
$ dig sigok.verteiltesysteme.net @127.0.0.1 -p 5335
; <<>> DiG 9.16.22-Raspbian <<>> sigok.verteiltesysteme.net @127.0.0.1 -p 5335
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 64024
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;sigok.verteiltesysteme.net. IN A
;; ANSWER SECTION:
sigok.verteiltesysteme.net. 60 IN A 134.91.78.139
;; Query time: 29 msec
;; SERVER: 127.0.0.1#5335(127.0.0.1)
;; WHEN: Thu Jan 27 18:03:15 CET 2022
;; MSG SIZE rcvd: 71
Pi-hole settings
In your Pi-hole web console, navigate to the Settings, then DNS. Make sure to uncheck all upstream DNS servers.
Now, scroll down to the second Upstream DNS Servers section and specify 127.0.0.1#5335
as the Custom DNS (IPv4). This means that your Pi-hole will use your recursive DNS server provided by unbound.
At last, scroll all the way down and save your settings. After pressing the Save button you should see a pop up Info window
Validating our setup
At the time of writing this post, we have been running Pi-hole with unbound for almost 24 hours. We are able to validate that our configuration works from the Upstream servers pie chart, where 65% of all the DNS queries were handled by our local recursive DNS resolver and 20% were cached queries. Remaining 15% of DNS queries were handled by our preconfigured Quad9 upstream server and other public resolver while we were configuring local recursive DNS resolver unbound.
Resources
- Pi-hole - documentation : https://docs.pi-hole.net/
- unbound - about : https://nlnetlabs.nl/projects/unbound/about/
- unbound - install : https://docs.pi-hole.net/guides/dns/unbound/
- What is DNS? : https://www.cloudflare.com/learning/dns/what-is-dns/
- What is recursive DNS? : https://www.cloudflare.com/learning/dns/what-is-recursive-dns/
- DNS server types : https://www.cloudflare.com/learning/dns/dns-server-types/
Thank you for reading and we hope you learned something new._