Zero-Trust DNS Infrastructure: Unbound & Pi-hole with DoH and DNSSEC

Contents
Relying on external public DNS resolvers (such as 8.8.8.8 or 1.1.1.1) inherently requires trusting third-party infrastructure with complete browsing metadata. Even with encryption, central DNS providers can log domain queries and profile network activity. Establishing a Zero-Trust DNS architecture by combining Pi-hole with a local, validating Unbound recursive resolver eliminates third-party reliance entirely. By performing direct root-server recursion with DNSSEC validation, cryptographic integrity is guaranteed, preventing cache poisoning and ISP manipulation; on its own, this does not prevent eavesdropping on the line.
1. Architectural Principles: Zero-Trust DNS & Recursion
In a standard network setup, DNS queries are forwarded upstream to an ISP or public resolver. In contrast, a Zero-Trust local resolver architecture separates responsibilities into two distinct stages:
- Pi-hole (DNS Sinkhole & Filtering Engine): Intercepts incoming queries from local network devices, filtering out telemetry, advertising, and known malicious domains based on blocklists.
- Unbound (Recursive Resolver & Validator): Receives legitimate requests from Pi-hole and recursively queries the DNS hierarchy (Root Servers → TLD Servers → Authoritative Nameservers) directly. Each answer is validated against DNSSEC cryptographic signatures before being cached locally.
2. Step-by-Step Installation and Hardening of Unbound
To configure Unbound as a high-security recursive resolver on a Linux server (e.g., Debian or Raspberry Pi OS), the following deployment procedure must be executed:
- Package Installation: Install the resolver and standard DNS documentation packages:
sudo apt update && sudo apt install unbound unbound-anchor -y - DNSSEC Root Trust Anchor Setup: Retrieve the official DNSSEC root key to verify signatures:
sudo unbound-anchor -a /var/lib/unbound/root.key - Configuring the Hardened Resolver File: Create or edit the main configuration file at
/etc/unbound/unbound.conf.d/pi-hole.confwith optimized privacy and security parameters:server: verbosity: 1 interface: 127.0.0.1 port: 5335 do-ip4: yes do-udp: yes do-tcp: yes # Security & DNSSEC Validation auto-trust-anchor-file: "/var/lib/unbound/root.key" harden-glue: yes harden-dnssec-stripped: yes use-caps-for-id: no edns-buffer-size: 1232 prefetch: yes num-threads: 1 # Privacy Hardening (Minimal Information Disclosure) qname-minimisation: yes hide-identity: yes hide-version: yes private-address: 10.0.0.0/8 private-address: 172.16.0.0/12 private-address: 192.168.0.0/16 - Service Restart & Verification: Enable and restart the Unbound daemon, then test local resolution and DNSSEC validation on the assigned port:
sudo systemctl restart unbounddig @127.0.0.1 -p 5335 dnssec-failed.org +dnssec(Must returnSERVFAILdue to invalid DNSSEC signature).
3. Step-by-Step Integration with Pi-hole & DoH Fallback
Once local recursion is functional, Pi-hole must be routed to communicate exclusively with Unbound:
- Upstream Configuration in Pi-hole: Access the Pi-hole Web Admin Interface and navigate to
Settings > DNS. - Disable Default Upstream Providers: Uncheck all pre-configured external upstream DNS servers (e.g., Google, Cloudflare, Quad9).
- Assign Local Custom Upstream: Under
Custom 1 (IPv4), enter the localhost Unbound socket:127.0.0.1#5335. - Enable DNSSEC Checkbox: Check the
Use DNSSECsetting to allow Pi-hole to properly pass and report DNSSEC status flags to local clients. - Optional DNS-over-HTTPS (DoH) Tunneling: In environments where ISP packet inspection on port 53 is a concern, Unbound can be configured to forward queries through a local cryptographic DoH proxy (e.g.,
cloudflaredordnscrypt-proxy) instead of direct root recursion, ensuring TLS encryption across untrusted transit networks.
4. Summary & Architectural Value
What this tutorial achieves: The successful deployment of a sovereign, Zero-Trust DNS resolver architecture combining Pi-hole ad-blocking with an Unbound recursive resolver enforcing strict DNSSEC cryptographic validation and query minimization.
Resulting value: Complete independence from commercial DNS providers is established, ensuring that no external DNS provider can profile or monetize internal network lookup logs. Cryptographic validation protects all local devices against cache poisoning, spoofing, and man-in-the-middle manipulation, while query name (QNAME) minimization leaks only the bare minimum of domain information required to resolve each target address.
Questions and answers
Does running recursion through Unbound protect DNS queries from the internet provider?
From manipulation, yes; from being read, no. DNSSEC secures the authenticity of the answers but encrypts nothing. Unbound queries root, TLD and authoritative servers directly over port 53 in plain text, and those queries travel over the provider’s line. QNAME minimization only reduces what each individual server learns: a root server sees the top-level domain, not the full name. On the line itself, every query is still readable.
The DoH encryption the article mentions as an option changes the picture fundamentally: Unbound then no longer walks the hierarchy itself but forwards every query to the operator of the DoH service. The provider no longer sees any of the queries, but the DoH operator sees all of them, which puts back exactly the kind of central resolver that recursion was meant to avoid. Both at once is not possible, because authoritative servers as a rule do not accept encrypted queries.
With either approach, the connection made after the DNS lookup still reveals its destination to the internet provider through the IP address and usually through the server name, which is sent unencrypted during the TLS handshake. Choosing between recursion and DoH is therefore a choice of who can see the queries, not whether anyone can.
Why do some public names that point to an internal address stop resolving after the switch to Unbound?
The private-address lines in the configuration are a protection against DNS rebinding: Unbound removes addresses from these ranges from every answer that comes from the public DNS. A name that deliberately points to an internal address is therefore left without a result until a line such as private-domain: "example.com" exempts its domain from the check. Local records in Pi-hole are not affected, because Pi-hole answers queries for them itself and never asks Unbound.