A couple of months ago, friends and I were discussing the benefits of running pi.hole on our home networks to curb the volume of unwanted advertisements while browsing the web. In addition, given that we all have kids, we started to solution methods to control non-family friendly content. During this discussion, I started thinking about the plaintext nature of DNS and how to mitigate exposure of the queries leaving our network to the upstream provider. Enter DNS over TLS and DNS over HTTP.

DNS over TLS was introduced as part of RFC 7858 in 2016

DNS over HTTP was introduced as part of RFC 8484 in 2018.

Both of these specifications were designed for the purpose of “mitigat[ing] both passive surveillance RFC 7258 and active attacks that attempt to divert DNS traffic to rogue servers (see Section 2.5.1 of RFC 7626).” While their implementations are slightly different, the method of their mitigation is similar. Cloudflare has an overview of the two implementations in their learning center that is worth a read if you’re interested in the technical detail.

At a high level, an upstream resolver listening for DNS queries over TLS is simply communicating with the caller using the standard name resolution protocol but wrapped by a negotiated TLS connection encrypting the content of the query and the response. DNS over HTTP sends a DNS query using the standard HTTP format and the response is delivered as contents with a corresponding content type. This method provides the added protection of blending in with standard web queries as the traffic is delivered over the same ports. The challenge with incorporating DoH versus DoT is that it requires a proxy to forward standard DNS queries from the trusted network to the upstream resolver.

If I chose the easy route to implement DoT, I would be able to configure resolved (part of systemd)9 to forward queries over DoT through the addition of a single key value pair configuration setting DNSOverTLS. As long as the upstream resolver supports DoT, then this value can be set to true (it takes a Boolean, or the value opportunistic). With resolved configured properly, and running on the RPi, the final step is to configure the upstream DNS server to point to the local resolved listener. In the screenshot below, I configured pihole to use the DoH proxy which is listening on port 5053.

Given that my requirement was to implement DoH for my local network, I set out to find a DoH proxy that I could easily implement on the RPi. I had considered configuring Unbound a number of times over the years, but the overhead never seemed worth it. Installing and configuring BIND also seemed like overkill especially considering pihole was using Dnsmasq under the covers. I decided to re-read the pihole documentation to see if the community had shared any other alternatives.

In the official documentation, under the guides section, I discovered cloudflared. Apparently, the amazing folks at cloudflare wrote an open source’d tunnel client to support their customers connecting to their resources over a private connection. A small feature of this client is to listen for standard DNS requests, and proxy them to a DoH resolver. 🎆

I followed the documentation provided by the pihole folks to the last period and in a few minutes, I completed my requirement of sending DNS queries completely encrypted to my resolver of choice. pihole continues to perform as exactly as designed. I discovered from digging through the cloudflared repository that the daemon has a metrics endpoint which I may investigate in the future.

Thanks to the folks at pihole and at cloudflare for contributing to the community!