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!