Souveränes rekursives DNS: Unbound Root-Zonen-Auflösung, QNAME-Minimierung & DNSSEC-Validierung
Erstellt: 06.11.2026 Aktualisiert: 06.11.2026
Management Summary: Die Weiterleitung lokaler DNS-Anfragen an kommerzielle Upstream-Resolver (
1.1.1.1,8.8.8.8,9.9.9.9) – selbst über DoH oder DoT – übermittelt ein lückenloses chronologisches Profil des gesamten Netzwerktraffics an Drittanbieter. Diese Architekturreferenz beschreibt den Betrieb von NLnet Labs Unbound als selbst gehosteten, validierenden, rekursiven Caching-Resolver. Unbound traversiert die globale DNS-Hierarchie direkt ausgehend von den 13 IANA Root-Server-Clustern unter Nutzung von RFC 9156 QNAME-Minimierung, RFC 8198 Aggressivem NSEC-Caching, RFC 8806 Lokalem Root-Mirroring (auth-zone) und Multithreaded-Slab-Cache-Tuning.
-
Direkte Root-Zonen-Rekursion
Macht DNS-Zwischenhändler überflüssig, indem iterativ von den IANA-Root-Servern (
a.root-servers.net–m.root-servers.net) bis zu den autoritativen Nameservern aufgelöst wird. -
RFC 9156 QNAME-Minimierung
Sendet auf jeder Delegationsebene nur den minimal notwendigen Domain-Label (
.desieht nur.de, niemalsmail.internal.nejat-hakan.de). -
Kryptografische DNSSEC-Kette
Validiert
DNSKEY-,DS- undRRSIG-Signaturen gegen den IANA Root Trust Anchor (root.key) und synthetisiert Negativantworten via Aggressives NSEC. -
RFC 8806 Lokale Root-Zone
Spiegelt die gesamte IANA-Root-Zone lokal via AXFR/HTTPS (
auth-zone: "."), um externe Root-Abfragen zu eliminieren und Verkehrsanalysen zu verhindern.
1. Architektur: Iterative Root-Rekursion mit QNAME-Minimierung
Wird vault.infra.nejat-hakan.de ohne QNAME-Minimierung aufgelöst, erfährt bereits der Root-Server sowie der .de-TLD-Server den vollständigen Hostnamen. Mit qname-minimisation: yes schneidet Unbound alle überflüssigen Sublabels ab:
%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
subgraph LocalStack["Souveräner DNS-Stack"]
Sinkhole["Pi-hole / AdGuard Home<br/>127.0.0.1:53"]
Unbound["Unbound Rekursive Engine<br/>127.0.0.1:5335 (DNSSEC)"]
LocalRoot[("RFC 8806 Lokaler Root<br/>auth-zone '.' Spiegel")]
end
subgraph GlobalHierarchy["Globale autoritative Hierarchie"]
TLD["DENIC (.de TLD NS)<br/>Sieht NUR: nejat-hakan.de NS?"]
AuthNS["Autoritativer NS<br/>Sieht: vault.nejat-hakan.de A?"]
end
Sinkhole -->|"Anfrage: vault.nejat-hakan.de"| Unbound
Unbound <-->|"1. Anfrage: .de NS?<br/>(0ms Lokaler Loopback)"| LocalRoot
Unbound -->|"2. QNAME-minimiert:<br/>nejat-hakan.de NS?"| TLD
TLD -->|"Referral + DS-Record"| Unbound
Unbound -->|"3. Finale Anfrage:<br/>vault.nejat-hakan.de A?"| AuthNS
AuthNS -->|"Signiertes A + RRSIG"| Unbound
Kryptografische & Privatsphäre-Mechanismen
- 0x20-Bit-Verfahren (
use-caps-for-id: yes): Unbound randomisiert Groß- und Kleinschreibung in ausgehenden Domainanfragen (z. B.vAuLt.NeJaT-hAkAn.De). Da autoritative Nameserver die Schreibweise exakt spiegeln müssen, entsteht zusätzliche Entropie gegen Kaminsky-Cache-Poisoning-Angriffe. - Aggressives NSEC/NSEC3-Caching (
aggressive-nsec: yes): Gemäß RFC 8198 nutzt Unbound kryptografische Bereichsbeweise, umNXDOMAIN- undNODATA-Antworten lokal zu generieren, ohne externe Nameserver erneut zu befragen. - Cache-Prefetching (
prefetch: yes&serve-expired: yes): Wird ein Eintrag in den letzten 10 % seiner TTL angefragt, liefert Unbound sofort den gecachten Wert aus und aktualisiert den Datensatz asynchron im Hintergrund.
2. Installation & Root Trust Anchor Initialisierung
Unter Debian/Ubuntu stellt das Paket dns-root-data die Root-Hints (/usr/share/dns/root.hints) sowie den automatisierten DNSSEC-Trust-Anchor nach RFC 5011 bereit:
sudo apt update && sudo apt install -y unbound unbound-anchor dns-root-data dnsutils
# DNSSEC Root Trust Anchor (/var/lib/unbound/root.key) überprüfen oder initialisieren
sudo -u unbound unbound-anchor -a /var/lib/unbound/root.key -v
3. Gehärtete Produktivkonfiguration (sovereign-recursive.conf)
Die folgende Konfiguration ist für ein 4-Core-System optimiert, das Unbound auf 127.0.0.1:5335 als Upstream für Pi-hole oder AdGuard Home bereitstellt. Wichtig: Die Slab-Werte (msg-cache-slabs, rrset-cache-slabs, etc.) müssen Zweierpotenzen sein (2, 4, 8), um Lock-Contention zwischen CPU-Cores zu minimieren.
server:
# --- Netzwerk- & Schnittstellen-Binding ---
interface: 127.0.0.1
interface: ::1
port: 5335
do-ip4: yes
do-ip6: yes
do-udp: yes
do-tcp: yes
prefer-ip6: no
# --- Zugriffskontrolllisten (Loopback) ---
access-control: 127.0.0.0/8 allow
access-control: ::1/128 allow
access-control: 0.0.0.0/0 refuse
access-control: ::0/0 refuse
# --- Root Hints & Trust Anchor ---
root-hints: "/usr/share/dns/root.hints"
auto-trust-anchor-file: "/var/lib/unbound/root.key"
# --- Härtung & Privatsphäre ---
hide-identity: yes
hide-version: yes
qname-minimisation: yes
qname-minimisation-strict: no
harden-glue: yes
harden-dnssec-stripped: yes
harden-below-nxdomain: yes
harden-referral-path: no
harden-algo-downgrade: yes
use-caps-for-id: no
aggressive-nsec: yes
# --- DNS-Rebinding-Schutz (RFC1918 in öffentlichen Antworten sperren) ---
private-address: 10.0.0.0/8
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: fd00::/8
private-address: fe80::/10
# Lokale Domain darf RFC1918 zurückgeben
private-domain: "lan.nejat-hakan.de"
# --- Multi-Core Performance & Slab Memory Allokation ---
num-threads: 4
msg-cache-slabs: 4
rrset-cache-slabs: 4
infra-cache-slabs: 4
key-cache-slabs: 4
# 128MB Message Cache und 256MB RRset Cache (Verhältnis 2:1)
msg-cache-size: 128m
rrset-cache-size: 256m
key-cache-size: 64m
neg-cache-size: 32m
# --- Socket-Puffer & EDNS-Optimierung ---
so-rcvbuf: 4m
so-sndbuf: 4m
so-reuseport: yes
edns-buffer-size: 1232
num-queries-per-thread: 4096
outgoing-range: 8192
# --- Low-Latency Prefetch & Stale Cache ---
prefetch: yes
prefetch-key: yes
serve-expired: yes
serve-expired-ttl: 86400
serve-expired-client-timeout: 1800
cache-min-ttl: 300
cache-max-ttl: 86400
# --- RFC 8806: IANA Root-Zone lokal spiegeln ---
auth-zone:
name: "."
primary: 199.9.14.201 # b.root-servers.net
primary: 192.33.4.12 # c.root-servers.net
primary: 199.7.91.13 # d.root-servers.net
primary: 192.5.5.241 # f.root-servers.net
primary: 192.112.36.4 # g.root-servers.net
primary: 193.0.14.129 # k.root-servers.net
primary: 192.0.47.132 # xfr.cjr.dns.icann.org
primary: 192.0.32.132 # xfr.lax.dns.icann.org
fallback-enabled: yes
for-downstream: no
for-upstream: yes
zonefile: "/var/lib/unbound/root.zone"
Warum edns-buffer-size: 1232 entscheidend ist
Gemäß dem Standard des DNS Flag Day 2020 verhindert der Wert 1232 eine Paketfragmentierung über die minimale IPv6-MTU (1280 Bytes minus 40 Byte IPv6- und 8 Byte UDP-Header). Große DNSSEC-Antworten über 1232 Bytes signalisieren sauber TC (Truncated) und wechseln auf TCP, anstatt an fehlerhaften ISP-Routern lautlos verworfen zu werden.
4. Split-Horizon Lokale Zonen (local-zone & local-data)
Interne Netzwerkreferenzen lassen sich direkt in Unbound definieren, sodass interne Hosts selbst bei getrennter Internetverbindung aufgelöst werden können:
server:
local-zone: "lan.nejat-hakan.de." static
local-data: "router.lan.nejat-hakan.de. 3600 IN A 192.168.10.1"
local-data: "nas.lan.nejat-hakan.de. 3600 IN A 192.168.10.10"
local-data: "proxmox.lan.nejat-hakan.de. 3600 IN A 192.168.10.20"
# Automatische Reverse PTR Records
local-data-ptr: "192.168.10.1 router.lan.nejat-hakan.de."
local-data-ptr: "192.168.10.10 nas.lan.nejat-hakan.de."
local-data-ptr: "192.168.10.20 proxmox.lan.nejat-hakan.de."
5. Validierung, DNSSEC-Prüfung & Diagnostik
Konfigurationssyntax testen, Dienst neu starten und das kryptografische AD-Flag (Authenticated Data) prüfen:
# 1. Konfigurationssyntax vor dem Neustart validieren
sudo unbound-checkconf
# 2. Dienst neu starten und Status prüfen
sudo systemctl restart unbound
# 3. Prüfen, ob gültige DNSSEC-Signatur NOERROR mit gesetztem 'ad'-Flag liefert
dig @127.0.0.1 -p 5335 icann.org A +dnssec +multiline
# 4. Verifizieren, dass gefälschte/abgelaufene Signaturen mit SERVFAIL abgewiesen werden
dig @127.0.0.1 -p 5335 dnssec-failed.org A
# 5. Live-Statistiken und Cache-Trefferquoten auslesen
sudo unbound-control stats_noreset | grep -E "total.num|histogram"