A simple way to secure your home network’s external DNS queries

1–2 minutes

I watched an interesting YouTube video today and got inspired on setting up a DNS over HTTPS (DOH) server. The idea was to put a new DOH server as the forwarder/next hop for my network’s DNS. So heres the quick breakdown:

While watching some SEC football I installed Ubuntu as a VM on my Hyper-V setup. It was setup to use the minimal config so not much was added. First add dnsdist.

sudo apt update && sudo apt upgrade -y
sudo apt install -y dnsdist
sudo systemctl enable dnsdist

Next I needed to get a TLS certificate for the dnsdist config, so I installed cerbot and the Cloudflare plugin.

sudo apt update
sudo apt install certbot python3-certbot-dns-cloudflare -y

Using Cloudflare (which is hosting my DNS zone) I also needed to get an API token to use within the Ubuntu system to request and renew the cert. Login to Cloudflare, select/click the Profile in the top right. Next in on the profile page select “{} API Tokens” on the left and then click “Create Token”:

Once you have the token, copy and add to a new file [/etc/letsencrypt/cloudflare.ini] on Ubuntu:

dns_cloudflare_api_token = YOUR_API_TOKEN

Then set persmissions on the file:

sudo chmod 600 /etc/letsencrypt/cloudflare.ini

Now create a request for the cert:

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d doh.example.com \
  -m [email protected] \
  --agree-tos

Add the cert to the dnsdist config – edit /etc/dnsdist/dnsdist.conf:

-- Allow all clients (tighten this for production)
setACL("0.0.0.0/0")

-- Classic DNS for LAN (UDP+TCP on :53)
addLocal("0.0.0.0:53")

-- DoH listener for clients (HTTPS on :443, path /dns-query)
addDOHLocal(
  "0.0.0.0",
  "/etc/letsencrypt/live/doh.example.com/fullchain.pem",
  "/etc/letsencrypt/live/doh.example.com/privkey.pem",
  "/dns-query"
)

-- ===== Choose ONE downstream path =====
-- (A) Outgoing DoH upstream (Cloudflare):

newServer({
  address = "1.1.1.1:443",
  tls = "openssl",
  subjectName = "cloudflare-dns.com",  -- SNI / certificate hostname
  dohPath = "/dns-query",
  validateCertificates = true,
  name = "cf-doh4"
})

-- (B) Or use local BIND instead of Cloudflare:
-- newServer({ address = "127.0.0.1:53" })

-- Basic load‑balancing policy (safe default)
setServerPolicy(leastOutstanding)

I next setup my internal Windows DNS server to use the DOH server as the only forwarder:

Here’s how the DNS traffic now flows:

Now all my external DNS queries are being secured via HTTPS. Take that data aggregators.

Happy building.

Dan Newton has spent 25 years designing and building cloud and infrastructure for clients: AWS, Terraform, Kubernetes and identity.

Connect on LinkedIn →