There’s a saying that goes: “Show me your friends, I’ll tell you who you are”. A slight variation of this is: “Show me the websites you visit, I’ll tell you who you are”. A lot can be learned about an individual just by examining the websites they visit, the search queries they run, and the apps they use on a regular basis. A trove of information about a user’s online activity can be gleaned from their DNS traffic. Have you ever wondered why practically all Internet Service Providers (ISPs) pre-configure consumer routers with their own DNS servers?

In this article, we’ll learned a bit about DoH with a quick refresher on DNS and how it works, and go over a few strategies to improve online privacy by securing DNS.

What’s DoH?

The Domain Name System (DNS) is the system used to identify computers on the internet. Computers can only communicate with one another if they know each other’s IP addresses. But IP addresses, which are basically a bunch of numbers bundled together, are hard to memorize. If we had to use IP addresses to access websites, the web wouldn’t have taken off the way it did and you wouldn’t be reading this article online today. DNS was invented to accommodate humans, not machines.

Every time you type a URL in the browser, it issues a DNS a query to your DNS server in order to resolve the IP address of the website you’re trying to visit. The same thing happens when you open an app on your mobile device, when your smart light bulb phones home to fetch the most up-to-date brightness level it should shine at, and when you add a new show to your list on your smart TV Netflix app. Basically, every single action you make online triggers one or more DNS queries.

However, DNS was not built with privacy in mind. All DNS queries are routed through the internet in plain text. This means that anyone sniffing traffic on a network, or acting as a proxy to the internet, like an ISP or an enterprise router, can see the web locations that everyone on that network is visiting. While watching DNS traffic alone doesn’t give out information about the interactions between a client and a given website, it does paint a detailed picture of the web locations that a user has visited, an estimate of how long they have been on each website, the apps they’re using, and the type of Internet-of-Thing (IoT) devices they have installed in their homes, if any. It goes without saying that this is extremely detrimental to one’s online privacy. Fortunately, there are solutions to this problem.

One way of protecting one’s DNS queries from snooping eyes is to use encryption, like with DNS over TLS (DoT) or DNS over HTTPS (DoT). In this article we’ll focus on DoH which routes DNS traffic in an encrypted HTTPS tunnel. The client establishes a secure connection with the DNS server and funnels all DNS queries through it. This effectively encrypts DNS communications and renders them inaccessible to spying third parties. Now, let’s explore a few options of leveraging DoH to protect online activities1 and improve privacy.

Client Settings

Many browsers2 nowadays offer the option to configure a custom DoH server. However, this kind of configuration only applies to the activity happening in the browser and as mentioned previously, not all web requests originate from a browser. If we want to protect all DNS traffic emerging from a network, say a user’s home network, we should configure the network to use a local DNS server under our control that will turn around and delegate DNS resolution to a trusted upstream provider using DoH. First, let’s see how we can build this local DNS server.

Local DNS Server

Cloudflare built a nice little application called cloudflared that converts plain DNS queries to DoH. It’s free, open-source and easy to run. If you’ve read any other post on this blog you must’ve realized how much I love containers. So let’s build a cloudflared Docker image to run as a local DNS server. Unfortunately, Cloudflare doesn’t offer an official Docker image for cloudflared but we can make our own fairly easily. To do so, in a file named Dockerfile put the following content:

1
2
3
4
5
6
7
FROM alpine:3.15

RUN apk add --no-cache bash

RUN wget -q https://github.com/cloudflare/cloudflared/releases/download/2022.1.2/cloudflared-linux-arm \
	&& chmod +x cloudflared-linux-arm \
    && mv /cloudflared-linux-arm /usr/local/bin/cloudflared

Here, we’re using alpine version 3.15 and cloudflared version 2022.1.2 but more versions might have been released since this article was published. Feel free to update these versions to the latest.

In order to use this Docker image we can build a docker-compose service to run cloudflared. Alongside the previously created Dockerfile, let’s make a new file called docker-compose.yml with this content:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
version: "3.8"

services:
  cloudflared:
    build: .
    ports:
      - "53:5053"
    # replare <UPSTREAM SERVER> with the URL for the upstream DoH server
    # e.g.: https://doh.libredns.gr/dns-query
    entrypoint: bash -c "cloudflared proxy-dns --port 5053 --address 0.0.0.0 --upstream <UPSTREAM SERVER>"
    restart: unless-stopped

Make sure to replace <UPSTREAM SERVER> in the entrypoint command with your DoH server of choice. PrivacyGuides lists a few options you can pick from if you don’t already have a favourite DNS server. At this point, you have everything you need to convert all DNS queries in your network into DoH. Here are the steps to accomplish this:

  1. Run docker-compose up cloudflared from the folder where you put the Dockerfile and docker-compose.yml files to spin up a cloudflared container. Ideally, this should be done on a machine that’s constantly running on your network with a static IP address. A Raspberry Pi or a home server are great candidates for this.
  2. Configure your network’s DHCP server to use the machine where cloudflared is running as its default DNS server. This will automatically instruct all devices on the network to use the cloudflared container for DNS resolution. If you don’t have control over your DHCP server, or it’s too complicated to reconfigure at the moment, you can always start off by manually editing the DNS configuration on your most-used devices.

DoH in Pihole

If you already have a Pihole docker container running in your network and serving DNS queries, you can now set the cloudflared container as an upstream DNS server in Pihole and automatically upgrade all DNS queries to DoH. We’ve seen in a previous article how to set up Pihole using Docker. We’ll use the same docker-compose file here to illustrate how to integrate cloudflared.

First we need to place the Dockerfile file we created in the previous section inside a folder called cloudflared. Then, using our previous Pihole docker-compose file, we can add a new service for cloudflared as shown below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
version: "3.8"

services:
  pihole:
    # ...
    environment:
      # ...
      - PIHOLE_DNS_=172.31.0.200#5350
    # ...
    depends_on:
      - cloudflared
      - dhcphelper
    # ...

  dhcphelper:
    # ...

  cloudflared:
    build: ./cloudflared
    environment:
      # set UPSTREAM_PROVIDER to the URL for the upstream DOH server
      # e.g.: https://doh.libredns.gr/dns-query
      - UPSTREAM_PROVIDER=
    entrypoint: bash -c "cloudflared proxy-dns --port 5053 --address 0.0.0.0 --upstream $$UPSTREAM_PROVIDER"
    networks:
      backend:
        ipv4_address: '172.31.0.200'
    restart: unless-stopped

networks:
  # ...

volumes:
  # ...

You’ll notice that the cloudflared service looks similar to the one we built in the previous section. Here, we don’t need to expose the port 53 to the host because the only client connecting to the cloudflared container is the Pihole container running in the same Docker network. Also, we have to set a static IP address for the cloudflared container and add it to the PIHOLE_DNS_ environment variable of Pihole in order for it to be used as an upstream DNS server.

With the modifications above, restart your Pihole container by running docker-compose down and bringing it back up with docker-compose up pihole, and you should have a cloudflared container running alongside Pihole, ready to receive requests.

Wrap up

Congratulations, you made it! You now understand why protecting your DNS traffic is paramount to improving your online privacy and have in your toolbox 3 strategies for doing so, some more potent than others. In case you can’t run a full-fledged Pihole and cloudflared setup on your network, or don’t have the time to set that up yet, at least configure your most-frequently-used browser to resolve DNS through DoH. It takes almost no time, and goes a long way in getting you to a better place when it comes to your online privacy.


  1. Keep in mind that this only protects DNS queries. It is still possible for an adversary to figure out the websites you visit by doing reverse DNS resolution on the IP addresses of those websites. ↩︎

  2. Here’s an example for Firefox. ↩︎