Post

New Raspberry Pi setup - Part II (Pi-hole)

New Raspberry Pi setup - Part II (Pi-hole)

Install Pi-hole

In part I of this series I described how I installed Raspberry Pi OS. Now that we got the basics configured, let’s start installing Pi-Hole.

Certificates

Please note: quite a lot of the services I’ve installed are relying on https and not all features might work (i.e. on your phone) if the traffic is not encrypted. Thus I’ve configured encryption where possible. This will require a personal CA (certificate authority). Please follow this post to set it up and generate a certificate/key - usually one is enough as it can be configured for all services - for your Pi signed by your CA.

Pi-hole

There are a few ways to install Pi-hole which are all outlined in the official documentation. I went for Alternative 1 (See also my old post here). The new Pi-hole version 6 does not rely on lighttpd anymore, it comes with an inbuilt web server already. Anyway, get everything installed by:

1
2
3
4
5
$ sudo apt install git
$ cd 
$ git clone --depth 1 https://github.com/pi-hole/pi-hole.git Pi-hole
$ cd "Pi-hole/automated install"
$ sudo bash ./basic-install.sh

Follow the on screen instructions:

  • Static IP Address: Yes: Set static IP using current values
  • Upstream DNS Provider: OpenDNS (ECS, DNSSEC)
  • Blocklists: Yes (include StevenBlack’s)
  • Admin Web Interface: Yes
  • Web Server: Yes
  • Enable Logging: Yes
  • Privacy mode FTL: Show everything (private Installation)

Delete the cloned git repository again:

1
2
$ cd
$ rm -rf Pi-hole

Either note the password given during the installation or set a new custom password now:

1
$ sudo pihole setpassword

In my setup I wanted Pi-hole’s webserver to use encryption and listen to a non-default port. For that you need a certificate file and copy it to the system. The certificate file needs to include the key as well. So, if cert.pem is your certificate and key.pem is your key file:

1
2
3
4
$ cat key.pem >> cert.pem
$ sudo cp cert.pem /etc/pihole
$ sudo chown pihole:pihole /etc/pihole/cert.pem
$ sudo chmod 600 /etc/pihole/cert.pem

Firewall

Open ports for your Pi-hole installation:

1
2
3
4
5
6
7
$ sudo firewall-cmd --permanent --add-service=dns
$ sudo firewall-cmd --permanent --add-service=http
$ sudo firewall-cmd --permanent --add-service=https
$ sudo firewall-cmd --permanent --add-service=dhcp   # only required if your Pi-hole will also act as DHCP server
$ sudo firewall-cmd --permanent --add-service=dhcpv6 # s. above
$ sudo firewall-cmd --permanent --add-port=8443/tcp  # only required if you're changing the port (s. below)
$ sudo firewall-cmd --reload

Configuration

Webserver

Now point your browser to http://<YOUR_PI_IP>/admin and login with the password set before. Go to Settings -> All settings -> Webserver and API. Search for webserver.tls.cert and insert /etc/pihole/cert.pem. Then search for webserver.port and replace current port settings by 8443s,[::]:8443s. Hit “Save & Apply”. This will restart the webserver and now you’ll be able to connect again via https://<YOUR_PI_IP>:8443/admin.

Add Blocklists

Optionally add some block lists from https://github.com/hagezi/dns-blocklists. If you do, don’t forget to got to Tools -> Update Gravity and hit Update.

Enable DHCP server

I prefer to use Pi-hole also as DHCP server. For that, disable your router’s DHCP server (or modify its DHCP range) and then enable it in Pi-hole: Settings -> DHCP. Adjust the DHCP range to your requirements, than hit “Save & Apply”.

HINT: if you do not make Pi-hole your DHCP server, make sure to configure your router’s DHCP server to promote your Pi as DNS server (add IPv4 + IPv6 to the configuration!)

Unbound

To run your own recursive DNS server, simply install unbound (s. documentation here):

1
$ sudo apt install unbound

Next create the new config file /etc/unbound/unbound.conf.d/pi-hole.conf:

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
67
68
69
70
71
72
73
74
75
76
77
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 no if you don't have IPv6 connectivity
    do-ip6: yes

    # 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

    # Ensure no reverse queries to non-public IP ranges (RFC6303 4.2)
    private-address: 192.0.2.0/24
    private-address: 198.51.100.0/24
    private-address: 203.0.113.0/24
    private-address: 255.255.255.255/32
    private-address: 2001:db8::/32

    ## Privacy | Default: no, no
    hide-identity: yes
    hide-version: yes

… and restart unbound:

1
$ sudo systemctl restart unbound

Then login to your Pi-hole again at https://<YOUR_PI_IP>:8443/admin and go to Settings -> DNS. Uncheck all upstream DNS servers and add 127.0.0.1#5335 by clicking the + after Custom DNS servers. I’ve also enabled “Use DNSSEC”. Now hit Save & Apply and you’re done.

Check configuration

To see if all works well, you can run a few tests on your Pi (s. Mike Kuketz’ Blog here (in German)):

DNSSEC

Check DNSSEC by:

1
$ dig fail01.dnssec.works @127.0.0.1 -p 5335

This should return something like status: SERVFAIL somewhere in the output => this is good and expected!

Now we’ll see if we also find a valid DNSSEC response:

1
$ dig dnssec.works @127.0.0.1 -p 5335

This time you should get a valid and complete response, including the IP address and status: NOERROR!

Client

To check from your Linux client (probably works similarly with Windows or MacOSX), run something like this:

1
2
3
4
$ dig @<YOUR_PI_IPv4> example.com +udp
$ dig @<YOUR_PI_IPv4> example.com +tcp
$ dig @<YOUR_PI_IPv4> -t AAAA example.com
$ dig -6 <YOUR_PI_IPv6> -t AAAA example.com

Those should all return valid answers.

Updating Pi-hole

If there’s a new version of Pi-hole available, you can update by:

1
$ sudo pihole -up

Done: all clients (maybe they require a reboot ;-) ) on your network should now use Pi-hole as DNS server!

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