u/neo-ahmad

Technitium DNS PSA: Don't whitelist the root domain unless you really mean it

I learned something today while tuning my Technitium DNS Server with HaGeZi Ultimate, HaGeZi TIF, OISD Big, OISD NSFW, and the DoH/VPN bypass list.

At first, I was whitelisting root domains like:

facebook.com
tailscale.com
anydesk.com
fbcdn.net

I assumed this would only allow those specific domains.

After testing with dig and Technitium's EDE (Extended DNS Error) responses, I discovered that's not how the allow list behaves.

Example:

If you whitelist:

tailscale.com

then these are effectively allowed as well:

login.tailscale.com
log.tailscale.com
pkgs.tailscale.com
controlplane.tailscale.com
wwwv2.tailscale.com

Likewise:

facebook.com

also allows:

graph.facebook.com
pixel.facebook.com
m.facebook.com
lookaside.facebook.com
...

The proof came when I removed the parent domain from my whitelist and instead allowed only the hostnames my applications actually required.

Immediately afterwards:

$ dig pixel.facebook.com

;; status: NXDOMAIN

EDE: 15 (Blocked)
source=OISD Big
source=HaGeZi Ultimate

So my tracking protection started working again without breaking Facebook, WhatsApp, Instagram, or Tailscale functionality.

My recommendation is:

This keeps your services working while allowing your blocklists to continue blocking advertising, telemetry, and tracking endpoints.

Has anyone else observed the same behavior in Technitium's allow list? I'd be interested to know if this is documented somewhere, because it was one of the most useful things I've learned while fine-tuning my DNS setup.

reddit.com
u/neo-ahmad — 1 hour ago

Switching from AGH to Technitium

Happy to be here!

I've been using AdGuard Home (AGH) in my homelab for about four years to protect my family's devices and to avoid relying on my ISP for DNS. It worked well overall, but I ran into a few issues over the years, especially with DHCP and some resolver-related behavior.

My setup was:

Clients → AdGuard Home → Unbound → Cloudflare DoT

This gave me two layers: AGH for filtering and Unbound as the validating recursive resolver forwarding securely to Cloudflare.

I had tried Technitium DNS Server before, but I wasn't confident enough to switch completely. This week I decided to give it another chance and see if I could use it as a complete solution without adding multiple DNS layers.

The only thing that confused me was DNSSEC validation. At first, I added Unbound because I wasn't sure whether Technitium was validating correctly. After doing much deeper testing with dig and other tools, I tested dnssec-failed.org:

dig dnssec-failed.org +dnssec

Here are the results I received:

Cloudflare

EDE: 9 (DNSKEY Missing): (no SEP matching the DS found for dnssec-failed.org.)

Quad9

EDE: 6 (DNSSEC Bogus)

Technitium (DNSSEC Validation enabled, forwarding to Cloudflare DoT)

EDE: 9 (DNSKEY Missing): (no SEP matching the DS found for dnssec-failed.org.)   ← From Cloudflare

EDE: 9 (DNSKEY Missing): (Attack detected! No SEP matching the DS found for dnssec-failed.org.)   ← Added by Technitium

Based on these results, it appears that Technitium is performing DNSSEC validation correctly. However, I'm still confused about one thing:

Why does Technitium report EDE 9 (DNSKEY Missing) instead of EDE 6 (DNSSEC Bogus) like Quad9?

Is this simply a difference in implementation and reporting between DNS resolvers, or is there something in my configuration that I'm missing?

I'd appreciate any clarification from people who have more experience with Technitium and DNSSEC.

reddit.com
u/neo-ahmad — 6 days ago

Unbound for DNSSEC validation with Technitium DNS Server

This issue led me to use Unbound for DNSSEC validation with Technitium DNS Server. I adjusted several settings to optimize compatibility with Unbound, and the issue has been resolved successfully.

Below is my Unbound configuration file, along with screenshots of the Technitium DNS Server cache and main settings. Feel free to use them if you find them helpful.:

```

server:

# --- Basic Settings ---

directory: "/var/lib/unbound" # Working directory for Unbound where it looks for configs/keys

username: "unbound" # Security: Drops root privileges after binding to ports

chroot: "" # Sandboxing: Disabled ("") because systemd/AppArmor already handle it securely

use-syslog: yes # Logging: Sends logs directly to the system journal/syslog daemon

pidfile: "/run/unbound.pid" # Process Tracking: Location of the Process ID file for systemd management

verbosity: 2 # Logging Level: 2 provides detailed operational info/errors without flooding the disk

# --- Interfaces & Protocol ---

interface: 127.0.0.1# Bind Address: Localhost IPv4, securing it behind AdGuard Home

interface: ::1 # Bind Address: Localhost IPv6, securing it behind AdGuard Home

port: 5335 # Listen Port: Custom port for AdGuard Home upstream routing

do-ip4: yes # Network: Enable IPv4 query handling

do-ip6: yes # Network: Enable IPv6 query handling

do-udp: yes # Network: Enable standard UDP DNS traffic

do-tcp: yes # Network: Enable TCP DNS traffic (Required for large DNSSEC/DoT responses)

# --- Sockets & Threading (Optimised for i3-2100: 4 Threads) ---

so-reuseport: yes # Performance: Allows all 4 threads to bind to port 5335 simultaneously for kernel-level load balancing

so-rcvbuf: 32m # Performance: Increases OS receive buffer to 32MB to absorb heavy traffic spikes

so-sndbuf: 32m # Performance: Increases OS send buffer to 32MB

num-threads: 4 # CPU Mapping: Exact match for Intel i3-2100 (2 Cores / 4 logical Threads)

num-queries-per-thread: 2048 # Security/Performance: 1.24.0+ Default. Raised from 1024 to prevent thread starvation during DDoS

# NOTE: msg-cache-slabs, rrset-cache-slabs, etc., are intentionally removed. Unbound 1.24.0+ auto-configures these optimally based on num-threads.

# --- Cache Sizes (Conservative for 4GB RAM) ---

rrset-cache-size: 128m # Memory: Cache for DNS records (Strict rule: MUST be 2x msg-cache-size)

msg-cache-size: 64m # Memory: Cache for DNS messages (metadata and routing)

key-cache-size: 4m # Memory: Cache for DNSSEC cryptographic keys

neg-cache-size: 4m # Memory: Cache for NXDOMAIN (non-existent domain) answers to prevent re-querying dead links

cache-min-ttl: 0 # Compliance: 0 allows CDN geographic load balancing to work correctly (RFC 8767 compliant)

cache-max-ttl: 86400 # Compliance: Caps the maximum time any record can live in cache at 24 hours (86400s)

infra-cache-max-rtt: 5000 # Performance: Caps ping tracking for upstream servers at 5000 milliseconds

# --- Prefetching & Serve-Expired ---

prefetch: yes # Speed: Fetches a fresh copy of a popular domain in the background when its TTL is almost up

prefetch-key: yes # Speed: Fetches expiring DNSSEC keys in the background to ensure validation is always instant

serve-expired: yes # Reliability: (RFC 8767) Serves old cache data if Cloudflare DoT is temporarily down

serve-expired-ttl: 86400 # Reliability: Limits serving an expired record to 1 day after its actual expiration

serve-expired-client-timeout: 0 # Speed: Instantly serves the expired record to AdGuard while querying upstream (Zero-latency failover)

serve-expired-reply-ttl: 30 # Compliance: Tells AdGuard "this is expired data, only cache it locally for 30 seconds"

serve-expired-ttl-reset: no # Security: Prevents an expired record from staying alive forever if upstream is permanently dead

aggressive-nsec: yes # Speed/Security: Uses DNSSEC cryptographic proofs to instantly answer NXDOMAIN for typos

# --- Rate Limiting (Disabled for AdGuard Proxy Topology) ---

ratelimit: 0 # Disabled: Unbound only sees 127.0.0.1. Enabling this would throttle your entire network blindly.

ip-ratelimit: 0 # Disabled: Same reason as above.

# --- Hardening & Security ---

iter-scrub-rrsig: 8 # Security: (1.25.x feature) Mitigates "KeyTrap" CPU exhaustion attacks by limiting RRSIG validations per RRset

harden-unverified-glue: yes # Security: Drops out-of-zone data from authoritative servers to prevent DNS cache poisoning

harden-dnssec-stripped: yes # Security: Requires DNSSEC data for zones known to be signed (stops downgrade attacks)

harden-glue: yes # Security: Forces Unbound to fetch glue records itself if it doubts the authority's provided glue

harden-below-nxdomain: yes # Security: If "example.com" doesn't exist, instantly drops queries for "www.example.com"

harden-algo-downgrade: yes # Security: Prevents attackers from forcing Unbound to use weak, broken cryptographic algorithms

harden-large-queries: yes # Security: Drops suspiciously large queries that are often part of amplification DDoS attacks

qname-minimisation: yes # Privacy: (RFC 7816) Only sends the required part of a domain name to root/TLD servers

qname-minimisation-strict: no # Reliability: Set to 'no' to prevent breaking badly configured authoritative name servers

hide-identity: yes # Privacy: Refuses to broadcast the server's identity/hostname when asked via CHAOS queries

hide-version: yes # Privacy: Refuses to broadcast the Unbound software version when asked

minimal-responses: yes # Performance/Security: Removes unnecessary additional data from replies to save UDP bandwidth

val-clean-additional: yes # Security: Removes unvalidated/insecure DNSSEC data from the additional section of replies

unwanted-reply-threshold: 100000 # Security: If 100,000 unsolicited replies hit the server, it clears the cache assuming a poisoning attack

edns-buffer-size: 1232 # Security: (DNS Flag Day 2020) Prevents IP fragmentation attacks by capping UDP packet size

max-udp-size: 1232 # Security: Matches edns-buffer-size for standard UDP limits

deny-any: yes # Security: Drops "ANY" type queries, which are heavily abused in amplification attacks

rrset-roundrobin: yes # Load Balancing: Randomizes the order of multiple IP addresses returned for a single domain

# --- DNSSEC Validation ---

auto-trust-anchor-file: "/var/lib/unbound/root.key" # DNSSEC: Path to the root zone cryptographic anchor (ICANN's key)

val-permissive-mode: no # Security: Strictly enforces DNSSEC. Drops queries completely if validation fails.

val-log-level: 2 # Logging: Logs the exact text reason why a domain failed DNSSEC validation

trust-anchor-signaling: yes # DNSSEC: Tells upstream servers which DNSSEC algorithms Unbound supports

root-key-sentinel: yes # DNSSEC: Helps the global community track adoption of new root keys

# --- Tailscale Integration & Private Routing ---

domain-insecure: "ts.net." # Tailscale: Disables DNSSEC validation for Tailscale's magic domains (they aren't signed)

private-domain: "ts.net." # Tailscale: Prevents DNS rebinding attacks against Tailscale domains

private-address: 10.0.0.0/8# Security: Blocks public DNS servers from returning private 10.x IPs (Rebinding protection)

private-address: 172.16.0.0/12# Security: Blocks public DNS servers from returning private 172.16.x IPs

private-address: 192.168.0.0/16# Security: Blocks public DNS servers from returning private 192.168.x IPs

private-address: 169.254.0.0/16# Security: Blocks public DNS servers from returning APIPA/Link-Local IPs

private-address: 100.64.0.0/10# Security: Blocks public DNS servers from returning CGNAT/Tailscale IPs

private-address: fd00::/8 # Security: Blocks public DNS servers from returning IPv6 Unique Local Addresses

private-address: fe80::/10 # Security: Blocks public DNS servers from returning IPv6 Link-Local Addresses

private-address: ::ffff:0:0/96 # Security: Blocks public DNS servers from returning IPv4-mapped IPv6 addresses

unblock-lan-zones: no # Security: Ensures private zones aren't leaked to upstream internet servers

insecure-lan-zones: no # Security: Enforces standard security even for unblocked LAN zones

# --- TLS Upstream (Cloudflare DoT) ---

tls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt" # TLS: Path to system certificates to cryptographically verify Cloudflare's identity

tls-use-sni: yes # TLS: Sends the Server Name Indication (SNI) to Cloudflare during the handshake

tls-protocols: "TLSv1.2 TLSv1.3" # TLS: Strictly enforces modern TLS protocols, refusing old vulnerable ones (1.25.1 validated)

tcp-reuse-timeout: 60000 # Performance: Keeps the secure TLS tunnel to Cloudflare open for 60 seconds (Multiplexing)

max-reuse-tcp-queries: 200 # Performance: Sends up to 200 queries down the same TLS tunnel before opening a new one

# --- Extended DNS Errors & Reporting (RFC 8914 & 9567) ---

ede: yes # Diagnostics: (RFC 8914) Sends exact error codes (e.g., "DNSSEC Bogus") to AdGuard Home

ede-serve-expired: yes # Diagnostics: Tells AdGuard Home via EDNS that the record being served is expired

dns-error-reporting: yes # Diagnostics: (1.23.0+ feature) Automatically reports DNSSEC failures back to domain owners

# --- Logging & Stats ---

extended-statistics: yes # Stats: Generates highly detailed metrics for unbound-control analysis

statistics-interval: 3600 # Stats: Dumps statistics to the log file every 1 hour (3600 seconds)

log-time-iso: yes # Logging: Formats log timestamps in ISO8601 (easier for machine parsing)

log-thread-id: yes # Logging: Tags log entries with the thread ID, vital for debugging multi-threaded setups

# --- Advanced Tuning (Forwarder-Optimised) ---

outgoing-range: 2048 # Performance: Max number of simultaneous outgoing port connections to Cloudflare

incoming-num-tcp: 256 # Performance: Max number of incoming TCP connections from AdGuard

outgoing-num-tcp: 256 # Performance: Max number of outgoing TCP connections to Cloudflare

discard-timeout: 5000 # Protection: Drops queries that take longer than 5 seconds to prevent memory exhaustion

delay-close: 0 # Performance: Closes UDP sockets instantly after replying

do-not-query-localhost: yes # Loop Prevention: Stops Unbound from accidentally querying itself or other localhost daemons

# --- Module Config ---

module-config: "validator iterator" # Architecture: 'validator' performs DNSSEC checking, 'iterator' fetches the records

remote-control:

control-enable: yes # Management: Enables the unbound-control command line utility

control-interface: 127.0.0.1# Security: Locks unbound-control strictly to localhost access only

control-port: 8953 # Security: Designated port for unbound-control communications

control-use-cert: yes # Security: Requires cryptographic certificates to use unbound-control

server-key-file: /etc/unbound/unbound_server.key # Path: Server private key for control channel

server-cert-file: /etc/unbound/unbound_server.pem # Path: Server public certificate for control channel

control-key-file: /etc/unbound/unbound_control.key # Path: Control client private key

control-cert-file: /etc/unbound/unbound_control.pem # Path: Control client public certificate

# Forward standard queries securely to Cloudflare DoT

forward-zone:

name: "." # Target: Catch-all for all standard internet domains

forward-tls-upstream: yes # Protocol: Forces Unbound to encrypt these queries over TLS (DNS-over-TLS)

forward-addr: 1.1.1.1@853#1dot1dot1dot1.cloudflare-dns.com # Primary DoT server routing over port 853 with strict SNI verification

forward-addr: 1.0.0.1@853#1dot1dot1dot1.cloudflare-dns.com # Secondary DoT server routing over port 853 with strict SNI verification

# Forward Tailscale internal DNS queries locally

forward-zone:

name: "ts.net." # Target: Only intercepts domains ending in ts.net

forward-addr: 100.100.100.100# Target: Forwards in plaintext (UDP/53) to the local Tailscale daemon for private routing

```

https://preview.redd.it/2idd6gq5w8ah1.png?width=516&format=png&auto=webp&s=f4cf403c0563b8f2b0943bd31ae5cbc3f0347ae1

https://preview.redd.it/56apim37w8ah1.png?width=517&format=png&auto=webp&s=f2f8671f6689f9e5e701b53a262ec91bc5358d7f

reddit.com
u/neo-ahmad — 7 days ago

Add Unbound as Forwarding and DNSSEC Validation refer to Issue DNSSEC validation with Technitium

This issue led me to use Unbound for DNSSEC validation with Technitium DNS Server. I adjusted several settings to optimize compatibility with Unbound, and the issue has been resolved successfully.

Below is my Unbound configuration file, along with screenshots of the Technitium DNS Server cache and main settings. Feel free to use them if you find them helpful.: https://gist.github.com/jo20201/0c9df33bc26faefca9b44ba9729cb83a#file-unbound-conf

https://preview.redd.it/bhkvl2r5b8ah1.png?width=516&format=png&auto=webp&s=b757342c60c2903cbbda7034825b9e9c4714e4c9

https://preview.redd.it/t17wutk7b8ah1.png?width=517&format=png&auto=webp&s=e3b518145df1ef355943e3982e9b38a247d40227

reddit.com
u/neo-ahmad — 7 days ago