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.
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.
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
make && sudo make install
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.
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
upstream_recursive_servers: - address_data: 220.127.116.11 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 '18.104.22.168: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:
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
Here is my final Stubby-Configfile
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: 22.214.171.124 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: 126.96.36.199/20/0 ;; QUESTION SECTION: ;quad9.net. IN A ;; ANSWER SECTION: quad9.net. 120 IN A 188.8.131.52 ;; 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
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:
Adaptation to the Pi-hole so that the servers are not duplicated or incorrectly configured:
And finally restart the Pi-hole DNS service:
sudo systemctl restart dnsmasq
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: