DoH for Pi-hole [non webserver implementation]

I am not a fan of DNS over HTTPS (DoH) and would prefer DNS over TLS (DoT) over that. This because DoH can pass Pi-hole by solely using a browser and so Pi-hole does not has the ability to block unwanted or even bad stuff.

Begin situation is simple. I have Pi-hole running and it listens at the address 192.168.0.1 and makes the requested resolves upstream with an other DNS server.

Pi-hole is using a HTTP(S) server and standard is that Lighttpd and that web-server is listening on address 192.168.0.1 and ports 80 and 443.
You can go through those web-server ports and then to a proxy and then reach Pi-hole. I did not want to use a web-server in front so I have to create a virtual IP on which I can have my doh-proxy listening.

I have chosen the doh-proxy which is using Python3 (>= 3.6) and it can be found at this page and it also contains the installation instructions.

https://pypi.org/project/doh-proxy/

You need a TLS certificate to be able to encrypt traffic between you browser and the doh-proxy.

I advise to use a wildcard certificate and be smart and put in the SAN (alternative name) field of the certicate the wildcard and bare domain.
If we have a domain named example.com then we put in the SAN the wildcard *.example.com and the bare domain example.com. Letsencrypt offers free certificates and is well documented.

You could also create private certificates but then you have to load those on your device to be trusted.

Now we can create a static entry in Pi-hole so that we can use a domain and subdomain to reach our doh-proxy. The domain-name you use is the same as you have in your certificate and that could be doh.example.com

Now we have created a virtual IP address 192.168.0.2 and have a certificate then we can start the doh proxy manually.

usage: doh-proxy [-h] [--listen-address LISTEN_ADDRESS [LISTEN_ADDRESS ...]]
                 [--port PORT] --certfile CERTFILE --keyfile KEYFILE
                 [--upstream-resolver UPSTREAM_RESOLVER]
                 [--upstream-port UPSTREAM_PORT] [--uri URI] [--level LEVEL]
                 [--debug] [--version]

doh-proxy --listen-address 192.168.0.2 --certfile /etc/letsencrypt/live/example.com/cert.pem --keyfile /etc/letsencrypt/live/example.com/privkey.pem

That are the needed parameters. We don’t have to use ports or listen-address because we are running doh-proxy at the same device as Pi-hole and so default config for doh-proxy fits.

The TLS used is version 1.3 so the most modern one. Using a secure connection is slower than just using the default port DNS resolving but the purpose is to have extra secure access to Pi-hole DNS resolve.

The command above can be put in systemd (start on boot and restart/stop-able) in a fancy way or simple way. I have the used the simple way by adding a file named pihole-doh.service to /lib/systemd/system/ with the user: root:root and rights: 644 (rw-r–r--)

[Unit]
Description=DOH server for pihole through doh-proy Python3

[Service]
ExecStart=/usr/local/bin/doh-proxy --listen-address 192.168.0.2 --certfile /etc/letsencrypt/live/example.com/cert.pem --keyfile /etc/letsencrypt/live/example.com/privkey.pem

[Install]
WantedBy=multi-user.target

When you start doh-proxy through systemd then first stop your manual test version in which you one were testing if the browser did use DoH.

I read that doh-proxy was not stable using HTTP/2 and would crash after ten minutes or so. I don’t have that problem and I think that has to with that I have blocked UDP/443 in iptables on my RaspberryPI.

I am using a Firefox kind of browser in which you can put the needed connection info manually. On the moment Cloudflare is the default one and we overwrite it with our own.

So type in the adressbar of the browser: about:config and say yes if you are asked what you are doing…I hope that is the case. :wink:

Now search for network.trr and change the found values to the ones stated underneath.

network.trr.bootstrapAddress   192.168.0.2
network.trr.uri                https://doh.doh.example.com/dns-query
network.trr.custom_uri         https://doh.example.com/dns-query
network.trr.resolvers          [{ "name": "DOH Pi-hole", "url": "https://doh.example.com/dns-query" }]
network.trr.allow-rfc1918      true

network.trr.allow-rfc1918 has to be set to true otherwise you can’t reach http://pi.hole anymore because if false it will block answers of the following addresses:

     10.0.0.0        -   10.255.255.255  (10/8 prefix)
     172.16.0.0      -   172.31.255.255  (172.16/12 prefix)
     192.168.0.0     -   192.168.255.255 (192.168/16 prefix)

Now you can go to the settings (options from the menu) and scroll down the first page. There you will find Network Settings and click the button Settings.
You will get an pop-up and at the bottom you see the settings for DoH. Activate the the box DNS over HTTP and check if DOH Pi-hole is selected.

Now it should work also from that browser.

In the about:config searching for network.trr you find the value network.trr.mode with which you set if DoH is used or that the fasted method is used (DNS or DoH DNS).

This is only something you know you are going to use or else it is wasted time and you know about domain registrations, costs money, and using webservers/certificates.

Remark: when you are looking in you Pi-hole webinterface or log you can’t see the specific IP address from the client. This is due that doh-proxy has no ECS support so all client names are shown as the name or IP address of the RaspberryPI server name.

Background info about DoH and DoT: https://www.netmeister.org/blog/doh-dot-dnssec.html

If want to use doh-client to test locally and you get error due to asyncio then vissit this page for a solution.

https://github.com/facebookexperimental/doh-proxy/issues/63

Page about many doh solutions:

https://github.com/curl/curl/wiki/DNS-over-HTTPS

dnscrypt-proxy can now act as a local DoH server. A reddit discussion on the subject

question (apologies for my ignorance on the subject): can the following be achieved, using dnscrypt-proxy?

client, using system settings (port 53) ► pihole-FTL ► unbound (recursive resolver)

DOH client ► dnscrypt-proxy (local DOH server) ▲

The goal would be to, on the firewall, redirect all requests to DOH servers (not limited to these) to the local dnscrypt-proxy DOH server, thus eliminating the possibility that clients, including IOT devices, bypass pihole.

I don’t very much like any python solution. I stopped running maltrail (another useful DNS application, using python scripts) on my pi (3B), because the memory and CPU impact was to high.

You can only redirect UDP/TCP 53 traffic. TCP 443 also used for DoH so you can’t redirect, because you don’t have the correct certificate.

DoH coming from the client and have to go their way or be blocked and so all TCP/443 is blocked. You can block the IP addresses of DoH servers but do you got them all?

DoT is more suited for Pi-hole as upstream and Unbound supports DoT already.

1 Like