Some time ago I installed a Pi-hole on a Raspberry Pi at home to filter unwanted ads. This works very well after some startup problems (self-made firewall problems). This will now filter all network traffic on my network, including TVs, smartphones, tablets, and so on.
Especially devices such as smartphones, which often feature in-app ads, benefit from filtering DNS requests directly on the DNS server. Not only ads are filtered, but also tracker from Samsung TV, Google Analytics or Sonos (which can't be turned off).

More information about the Pi-hole and instructions for installation can be found here:

Recently, a new DNS service Quad9 launched. Quad9 differentiates from similar services by focussing on ease-of-use, scalability, security and privacy. One interesting and seemingly undocumented feature is the fact that you can communicate with the service using DNS-over-TLS. This encrypts the communication between your client and the DNS server, safeguarding your privacy until it reaches the dns servers. To date, Quad9 has promised not to collect any personal data.

I now would like to set up the Pi-Hole, that forwarded DNS queries are sent encrypted with TLS to Quad9.

Stubby

For this we need a DNS client that is DNS-over-TLS capable. The most advanced (experimental) client is currently Stubby from getdns.

Update: There is now stubby in the Package Sources of Debian Buster. My Raspberry Pi is currently running with Debian Stretch, so I compile the client from the source.

Build

On the Raspberry Pi:

# install dependencies for build and run
sudo apt-get install build-essential libssl-dev libtool m4 autoconf automake libyaml-dev libev4 libuv1

Now check out the Git-Repo and update the Git-Submodules

git clone https://github.com/getdnsapi/getdns.git
cd getdns
sed -i 's#git://#https://#g' .gitmodules # fix for git checkout
git submodule update --init
libtoolize -ci
autoreconf -fi
mkdir -v build && cd build
../configure --prefix=/usr/local --without-libidn --without-libidn2 --enable-stub-only --with-stubby

Coffee time:

make && sudo make install
Konfiguration

So far Stubby is compiled and installed. Now we create the Stubby configuration and integrate it into Systemd so that it can be managed reliably:

cd ../stubby
sudo useradd stubby
sudo /usr/bin/install -Dm644 stubby.yml.example /etc/stubby.yml

Attention! By default the Strict-Mode is active, therefore no fallback to tcp or udp dns.

The transport options in strict mode limit the communication to TLS and additionally require an authentication of the upstream DNS.

dns_transport_list:
  - GETDNS_TRANSPORT_TLS
#  - GETDNS_TRANSPORT_TCP   # disabled, cleartext queries
#  - GETDNS_TRANSPORT_UDP   # disabled, cleartext queries

# For Strict use        GETDNS_AUTHENTICATION_REQUIRED
# For Opportunistic use GETDNS_AUTHENTICATION_NONE
tls_authentication: GETDNS_AUTHENTICATION_REQUIRED

We edit the Stubby configuration and enter under listen_addresses which address and port the client should listen to. Since the client is used by the Pi-hole, Stubby only needs to be accessible locally. In addition, we need a different port, since the Pi-hole itself runs a DNS service on the standard port 53.
I used 127.0.2.2.2 as local IP to distinguish in the log and the graphs of Pi-Hole where the queries might be forwarded. Thus it is clear that these are placed at Stubby.

# sudo vi /etc/stubby.yml
listen_addresses:
  - 127.0.2.2@5353
  - 0::2@5353

As Upstream Servers (to which the DNS queries are forwarded), I have now added the Quad9 servers to the list below. This can of course be any DNS service that supports TLS. I also verify the authenticity of the server via the tls_auth_name, so that no one else than pretend to be dns.quad9.net.

upstream_recursive_servers:
  - address_data: 9.9.9.9
    tls_auth_name: "dns.quad9.net"
  - address_data: 2620:fe::fe
    tls_auth_name: "dns.quad9.net"

Alternatively, authentication with public-key pinning (SPKI) for TLS could be used (RFC7858) (tls_pubkey_pinset), but this requires a manual customization of the configuration when renewing the certificate.
The pubkey digest value can be queried by yourself (works for every TLS DNS server):

echo | openssl s_client -connect '9.9.9.9:853' 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

And there we have the answer for Quad9: MujBQ+U0p2eZLTnQ2KGEqs+fPLYV/1DnpZDjBDPwUqQ=

The Quad9's certificate is from Let's Encrypt, so it is to be assumed that after renewing the certificate the digest changes and the authentication does not work anymore. It is therefore recommended to use only tls_auth_name.

Here is my final Stubby-Configfile /etc/stubby.yml:

resolution_type: GETDNS_RESOLUTION_STUB
dns_transport_list:
  - GETDNS_TRANSPORT_TLS

tls_authentication: GETDNS_AUTHENTICATION_REQUIRED
tls_query_padding_blocksize: 256

edns_client_subnet_private: 1
idle_timeout: 10000
round_robin_upstreams: 0

listen_addresses:
  - 127.0.2.2@5353
  - 0::2@5353
upstream_recursive_servers:
  - address_data: 9.9.9.9
    tls_auth_name: "dns.quad9.net"

Installation of the Systemd-Services

sudo vi /lib/systemd/system/stubby.service 

[Unit]
Description=stubby DNS resolver
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/usr/local/bin/stubby -C /etc/stubby.yml
Restart=on-abort
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
User=stubby

[Install]
WantedBy=multi-user.target

As a last step for the TLS-DNS-Client part we start and test Stubby:

sudo systemctl daemon-reload
sudo systemctl enable stubby
sudo systemctl start stubby
pi@raspberrypi:~/ $ dig @127.0.2.2 -p 5353 quad9.net

; <<>> DiG 9.10.3-P4-Raspbian <<>> @127.0.2.2 -p 5353 quad9.net
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12094
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; CLIENT-SUBNET: 9.9.0.0/20/0
;; QUESTION SECTION:
;quad9.net.         IN  A

;; ANSWER SECTION:
quad9.net.      120 IN  A   216.21.3.77

;; Query time: 142 msec
;; SERVER: 127.0.2.2#5353(127.0.2.2)
;; WHEN: Sun Jan 14 11:49:22 UTC 2018
;; MSG SIZE  rcvd: 74

Hooray!

Pi-hole with Stubby

In order for Pi-hole to use our local DNS-Resolver Stubby, we reconfigure the forwarding DNS servers from Pi-hole.

The Pi-hole Webgui doesn't let you configure DNS servers with ports, so you have to adjust it manually.

I create a new configfile /etc/dnsmasq.d/02-stubby.conf and enter the Stubby addresses:

server=127.0.2.2#5353

Adaptation to the Pi-hole so that the servers are not duplicated or incorrectly configured:

  • delete server= from /etc/dnsmasq.d/01-pihole.conf.
  • delete PIHOLE_DNS_1 from /etc/pihole/setupVars.conf.

And finally restart the Pi-hole DNS service:

sudo systemctl restart dnsmasq

Test

In the Pi-hole log /var/log/pihole.log or in the webgui the requests should now be forwarded to Stubby:

forwarded maps.googleapis.com to 127.0.2.2
forwarded fedoraproject.org to 127.0.2.2

With tcpdump I recorded the sent and received packets on the router and evaluated them in Wireshark.
As expected, DNS traffic is now encrypted:

Wireshark Segment

Next Post Previous Post

Add a comment

Comments

Bei mir hängt es bei der dig-Abfrage - da kommt bei mir folgendes: pi@raspberrypi:~ $ dig @127.0.2.2 -p 5353 quad9.net ; DiG 9.10.3-P4-Raspbian @127.0.2.2 -p 5353 quad9.net ; (1 server found) ;; global options: cmd ;; connection timed out; no servers could be reached pi@raspberrypi:~ $ Die config-Files habe ich, wie auch den Rest, gecopypasted - und dann noch dreimal auf Fehler geprüft. Daran kanns also eigentlich nicht liegen :/ Der Stubby selbst scheint aber auch zu spinnen: service stubby status ● stubby.service - stubby DNS resolver Loaded: loaded (/lib/systemd/system/stubby.service; enabled; vendor preset: Active: failed (Result: exit-code) since Wed 2018-07-11 11:54:12 UTC; 6h ago Process: 20362 ExecStart=/usr/local/bin/stubby -C /etc/stubby.yml (code=exite Main PID: 20362 (code=exited, status=127) Jul 11 11:54:11 raspberrypi systemd[1]: Started stubby DNS resolver. Jul 11 11:54:12 raspberrypi systemd[1]: stubby.service: Main process exited, co Jul 11 11:54:12 raspberrypi systemd[1]: stubby.service: Unit entered failed sta Jul 11 11:54:12 raspberrypi systemd[1]: stubby.service: Failed with result 'exi lines 1-10/10 (END) Eine Idee?
Written on Wed, 11 Jul 2018 20:04:20 by Andreas
I'm stuck at this point -> dig @127.0.2.2 -p 5353 quad9.net. The response I get isn't successful. dig @127.0.2.2 -p 5353 quad9.net ; DiG 9.10.3-P4-Raspbian @127.0.2.2 -p 5353 quad9.net ; (1 server found) ;; global options: cmd ;; connection timed out; no servers could be reached I have no idea how to fix this - I've followed the instructions exactly but I have no idea how to fix this error. Any help would be appreciated.
Written on Mon, 02 Jul 2018 15:11:11 by Josh
Please be aware that if you use android phone it will leak your DNS even if your networks DHCP settings tell it not to. Only solution I have found so far is to manually set the DNS to Pi-hole using DNS Changer app from playstore (which acts as vpn) forcing android to use that DNS. If anyone finds a better solution than doesn't require manual intervention please post here:).
Written on Mon, 18 Jun 2018 15:03:55 by Roland
Hi James again, I was able to get this running within a container via Linux Deploy on Android. MODIFIED STEPS - Stubby configuration for pihole setup You will need to make the following changes to the procedure above. - After creating the systemd script. (Step - Installation of the Systemd-Services) Power off then power on the container. This will run the newly created systemd script on startup. - Change to su then run systemctl enable stubby. This should show the following: Created symlink /etc/systemd/system/multi-user.target.wants/stubby.service -> /lib/systemd/system/stubby.service. - Power off the container then power it on again. This will re-run the systemd script and ensure that stubby is enabled. - Run stubby -i, this will show you what configuration file stubby is looking at. For some reason, the stubby configuration file that appears to be referenced is: /usr/local/etc/stubby/stubby.yml rather than the tutorial specified /etc/stubby.yml. Edit the stubby file in the /usr directory - Then run sudo stubby to start stubby. stubby starts as a daemon by sudo stubby. WAIT. IT WILL TAKE SOME TIME TO START THIS SERVICE! It will appear to hang. If there are no errors during launch, begin the testing. For testing, you could alternatively run stubby -l as this will show a rolling log in the console. Thank you for this great tutorial. Regards, James
Written on Mon, 04 Jun 2018 13:26:09 by James
Thank you for this great tutorial. I'm running Debian Stretch within a container on Android using Linux Deploy (https://play.google.com/store/apps/details?id=ru.meefik.linuxdeploy&hl=en) There were no errors or issues in following these instructions until I got to this point: sudo systemctl daemon-reload sudo systemctl enable stubby sudo systemctl start stubby When I try to run these commands, I get the following error message: Running in chroot, ignoring request. Could I please request your assistance with this issue? Regards, James
Written on Sun, 03 Jun 2018 11:54:45 by James
Awesome! works great.
Written on Thu, 12 Apr 2018 05:40:27 by jm