Pi-hole with DNS over HTTPS

Note

Update June 2020 (Version 2): Revised manual with conversion to DNSCrypt proxy.

Pi-Hole is a free software with the function of a tracking and advertising blocker as well as an optional DHCP server. I have been using it for some time now and it should now be coupled with the DNS over HTTPS function. The DNS requests are sent via an encrypted HTTPS channel, which means that they cannot (theoretically) be intercepted and therefore remain anonymous.

Using DNSCrypt proxy

Since the cloudflard caused problems from time to time, I switched to dnscrypt-proxy. There is already a tutorial for the integration into Pi-Hole available.

Besides DoH, DNSCrypt-Proxy can also use other encrypted DNS protocols like DNSCrypt v2 and also the Anonymized DNSCrypt. These are used automatically.

Installation cloudflared

To use this function, I use the program “cloudflared” from Cloudflare. This can be downloaded precompiled from the provider’s website. In my case I use a Raspberry Pi and need the package for an ARM and Debian.

wget https://bin.equinox.io/c/VdrWdbjqyF/cloudflared-stable-linux-arm.deb
sudo apt-get install ./cloudflared-stable-linux-arm.deb

If the message “package architecture (arm) does not match system (armhf)” appears, I have described a solution for this problem at the end of this manual.

Setting up the system daemon

After the installation we have to set up the system daemon. This requires several steps.

For security reasons we let the cloudflared run under an own user. We have to create this user first. This is done with the following command:

sudo useradd -s /usr/sbin/nologin -r -M cloudflared

Next, we create a configuration file in which we specify the default parameters. The port on which the daemon listens later is also defined here. Since we use our own user, we have to use a so-called higher port (over 1024). Since DNS normally uses port 53, I simply use 5353 here. The configuration file is at /etc/default/cloudflared.

# Parameter für cloudflared
CLOUDFLARED_OPTS=--port 5353 --upstream https://1.1.1.1/dns-query

In the next step, we adjust the permissions and transfer both the program and the configuration to our previously created user.

sudo chown cloudflared:cloudflared /etc/default/cloudflared
sudo chown cloudflared:cloudflared /usr/local/bin/cloudflared

Finally, we must ensure that the daemon is launched. This is done via a systemd script. We create this under the file name /etc/systemd/system/cloudflared.service:

[Unit]
Description=cloudflared DNS over HTTPS proxy
After=syslog.target network-online.target

[Service]
Type=simple
User=cloudflared
EnvironmentFile=/etc/default/cloudflared
ExecStart=/usr/local/bin/cloudflared proxy-dns $CLOUDFLARED_OPTS
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target

We will now start this daemon and ensure that it is started automatically in the future.

sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sudo systemctl status cloudflared

Function test

If the daemon is set up, we should check the function. This is done with the command dig, which we send to our local cloudflared. The whole thing should look like this:

dig @127.0.0.1 -p 5353 tobias-bauer.de

; <<>> DiG 9.11.5-P4-5.1-Raspbian <<>> @127.0.0.1 -p 5353 tobias-bauer.de
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9592
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1452
; PAD: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ("................................................................")
;; QUESTION SECTION:
;tobias-bauer.de.               IN      A

;; ANSWER SECTION:
tobias-bauer.de.        1639    IN      A       109.235.58.69

;; Query time: 36 msec
;; SERVER: 127.0.0.1#5354(127.0.0.1)
;; WHEN: Fri Oct 11 09:09:28 CEST 2019
;; MSG SIZE  rcvd: 143

Integrating in Pi-hole

Next, we need to configure our pi-hole server to use our daemon. To do this we go to the web interface in the point “Settings” and select there the tab “DNS”. We uncheck all upstream DNS servers.

Under “Custom 1 (IPv4)” we now enter our server. So in our case the value “127.0.0.1#5353”.

After clicking on “Save” and restarting the Pi-Hole daemon the DNS queries should run over HTTPS.

Fix the „package architecture (arm) does not match system (armhf)“ error

If the error “package architecture (arm) does not match system (armhf)” appears during installation, the local Debian runs under armhf and not under the pure arm architecture. This can easily be fixed by adding arm as architecture to the system. This is done with the following command:

sudo dpkg --add-architecture arm

After that you should be able to install the cloudflared without any problems.

However, there can be problems now if you run an update using apt-get. Here the error is displayed that the packages are not available for poor on the servers. But this problem can also be solved relatively easily by simply specifying the architecture in the sources.

To do this, edit the files in the directory /etc/apt/sources.list.d and insert [arch=armhf] into each line. A file will look like this:

deb [arch=armhf] http://archive.raspberrypi.org/debian/ buster main ui

Once you have modified all the files, an apt-get works again without any problems.

Problems

DNSSEC problem

I had the problem that after some time the resolution via DNSSEC did not work properly anymore. Here was a simple solution. DNSCrypt-Proxy works internally with DNSSEC. This means that the validation is guaranteed here. The problems occurred in the communication between two DNSCrypt-Proxy and Pi-Hole. If you deactivate DNSSEC in Pi-Hole the resolution works without problems.

Cloudflare problem

I have again and again, not only here, resolution problems with Cloudflare. The only solution for me was to lock Cloudflare and not to use it anymore. This is relatively easy with the proxy with the following configuration setting:

``bash
disabled_server_names = [‘cloudflare’]