Post

Recursive DNS Server using Pi-hole

What is Pi-hole?

The Pi-hole® is a DNS sinkhole that protects your devices from unwanted content without installing client-side software.

In other words, a Pi-hole is a DNS server that prevents ads from loading when we attempt to load web pages.

This document

Here, we will set up Pi-hole as a recursive DNS server using Unbound.

Benefits

It will search for the authoritative DNS server for each domain name instead of asking a 3rd party. This setup will help prevent 3rd party DNS from compiling your profile based on the websites you visited and will make you safer against DNS spoofing.

Unbound

For the recursive part of the server, we will be using Unbound. Unbound is a secure open-source recursive DNS server.

You can find the official documentation here.

Step 1 - Install Pi-hole

Before proceeding, ensure that your machine has a static IP.

To install Pi-hole, just run the following command and follow the instructions.

1
curl -sSL https://install.pi-hole.net | bash

You can use the default for everything. Later, we will edit the configuration files as required.

Password

Upon installation, you should have your password on the console. If you want to change it, you can use the following:

1
sudo pihole -a -p <password>

Post install

Go to your web interface and try to log in. The web interface is on http://<server IP>/admin/.

It should look something like this:

Pi-hole login interface Pi-hole login

Step 2 - Install Unbound

To install Unbound, run the following command:

1
sudo apt install unbound -y

Unbound will only work after we configure.

For the official documentation for this setup, follow the Pi-hole documentation tutorial.

Step 3 - Configure Unbound

We need to create the following file:

1
sudo vim /etc/unbound/unbound.conf.d/pi-hole.conf

Then, we need to give it a new configuration:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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

Save and exit vim using esc and the type :wq and hit enter

Start and test your service:

1
2
sudo service unbound start;
dig google.com @127.0.0.1 -p 5335

If everything goes well, you should get the following output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
; <<>> DiG 9.18.19-1~deb12u1-Debian <<>> google.com @127.0.0.1 -p 5335
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15988
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;google.com.                    IN      A

;; ANSWER SECTION:
google.com.             300     IN      A       142.250.200.110

;; Query time: 204 msec
;; SERVER: 127.0.0.1#5335(127.0.0.1) (UDP)
;; WHEN: Sat Dec 30 17:41:57 WET 2023
;; MSG SIZE  rcvd: 55

You should also consider setting the EDNS to its recomendation, using:

1
echo edns-packet-max=1232 | sudo tee -a 99-edns.conf

EDNS (Extension DNS) is a hop-by-hop extension to DNS. This means the use of EDNS is negotiated between each pair of hosts in a DNS resolution process, for instance, the stub resolver communicating with the recursive resolver or the recursive resolver communicating with an authoritative server. J. Damas et. al 2013.

Step 4 - Configure Pi-hole

Login into your Pi-hole main page. Go to Settings->DNS. You must uncheck any Upstream DNS Servers and selct a custom one with 127.0.0.1#5335. If your server is on a different network than all devices that use it, you must enable Respond only on interface ens18. The configuration must look something like this:

Pi-hole login interfaceDNS Configuration

Back to the terminal, disable resolvconf.conf entry for unbound:

1
2
3
4
sudo systemctl disable --now unbound-resolvconf.service;
sudo sed -Ei 's/^unbound_conf=/#unbound_conf=/' /etc/resolvconf.conf;
sudo rm /etc/unbound/unbound.conf.d/resolvconf_resolvers.conf;
sudo service unbound restart

Step 5 - Enable Logging

Logging can be helpful for several reasons, including debugging.

Start by editing the config file /etc/unbound/unbound.conf.d/pi-hole.conf add the following:

1
2
3
4
5
server:
    # If no logfile is specified, syslog is used
    logfile: "/var/log/unbound/unbound.log"
    log-time-ascii: yes
    verbosity: 1

Note that the verbosity will define the amount of logging used. It works like this:

1
2
3
4
5
6
Level 0 means no verbosity, only errors
Level 1 gives operational information
Level 2 gives  detailed operational  information
Level 3 gives query level information
Level 4 gives  algorithm  level  information
Level 5 logs client identification for cache misses

Now create the logging file and give it permissions:

1
2
3
4
5
6
7
8
9
10
11
sudo mkdir -p /var/log/unbound;
sudo touch /var/log/unbound/unbound.log;
sudo chown unbound /var/log/unbound/unbound.log;

echo "/var/log/unbound/unbound.log rw," | sudo tee -a /etc/apparmor.d/local/usr.sbin.unbound;

sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.unbound;

sudo service apparmor restart;

sudo service unbound restart

Conclusion

In conclusion, setting up a Pi-hole as a recursive DNS server using Unbound offers several benefits, primarily focused on enhancing privacy and security. By searching for authoritative DNS servers for each domain name rather than relying on third-party DNS services, this configuration helps prevent the compilation of user profiles based on visited websites. It provides a layer of defence against DNS spoofing.

The process involves installing a Pi-hole using a simple command, ensuring a static IP for the machine, and configuring it through the web interface. Unbound, an open-source recursive DNS server, is installed and configured to work seamlessly with Pi-hole.

The configuration of Unbound involves creating a specific file with recommended settings, including adjusting EDNS to its recommended values for better compatibility. Pi-hole settings are then modified to use the Unbound server as a custom DNS server.

Additionally, enabling logging can be valuable for debugging and monitoring. By adjusting the verbosity level, users can control the amount of logging generated by Unbound.

This setup provides users a powerful tool to block unwanted content, particularly ads, while ensuring a more private and secure DNS resolution process. Overall, the combination of Pi-hole and Unbound creates a robust and efficient DNS sinkhole solution for users seeking enhanced control over their online experience.

Inspiration

This setup is inspired by Jeff’s from Craft Computing video.

This post is licensed under CC BY 4.0 by the author.