Zum Inhalt

Netzwerkweites DNS-Sinkhole: Pi-hole v6 Embedded Engine, FTL-Tuning & DoH/DoT-Prävention

Erstellt: 06.11.2026 Aktualisiert: 06.11.2026

Management Summary: Client-seitige Browser-Werbeblocker können weder IoT-Geräte, Smart-TVs noch mobile Hintergrund-Telemetrie oder Firmware daran hindern, Verhaltensdaten zu exfiltrieren. Dieser Leitfaden beschreibt das Produktiv-Deployment von Pi-hole v6. Im Fokus stehen die C-basierte FTL (pihole-FTL) Engine mit integriertem dnsmasq und Civetweb-REST-API-Server, die SQLite3 Gravity-Datenbankoptimierung, VLAN-fähige Client-Gruppenrichtlinien sowie Firewall-Regeln gegen hartcodierte DoH-Bypässe.

  • Pi-hole v6 Monolithisches FTL


    Ersetzt veraltete Lighttpd/PHP-Stacks durch den einheitlichen pihole-FTL-Daemon mit nativen REST-Endpunkten, Embedded TLS und Shared-Memory-Abfrageverarbeitung.

  • B-Tree Gravity Engine


    Prüft Millionen von Blocklist-Einträgen und kompilierte POSIX Extended Regular Expressions (regex_blacklist) in Sub-Millisekunden-Latenz via SQLite3 (gravity.db).

  • VLAN-Clientgruppen-Richtlinien


    Weist IoT-Subnetzen, Gastnetzwerken und Workstations differenzierte Filterprofile über MAC-Adressen oder CIDR-Gruppen zu.

  • Anti-Bypass-Firewalling


    Fängt hartcodierte 8.8.8.8:53-Abfragen via DNAT ab und blockiert ausgehendes DoT (853/tcp) sowie DoH-Canary-Resolver am Gateway.


1. Architektonische Evolution: Pi-hole v6 & FTL Shared Memory

In Pi-hole v6 wurde die frühere Architektur aus Lighttpd und PHP-FPM vollständig abgelöst. Das Binary pihole-FTL (Faster Than Light) bündelt einen angepassten Fork von dnsmasq, einen hochgradig nebenläufigen Civetweb-HTTP/HTTPS-Server und einen In-Memory-Ringpuffer in einem einzigen Prozess.

%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
    subgraph LANClients["Interne VLAN-Subnetze"]
        Workstation["Workstation VLAN 10<br/>Gruppe: Engineering"]
        IoT["IoT-Subnetz VLAN 30<br/>Gruppe: Strikter Sinkhole"]
    end

    subgraph PiholeEngine["Pi-hole v6 (pihole-FTL Daemon)"]
        FTLDNS["Embedded dnsmasq<br/>UDP/TCP Port 53"]
        GravityDB[("gravity.db (SQLite3)<br/>Domains + Regex-Regeln")]
        SHM["POSIX Shared Memory<br/>In-RAM Query-Cache"]
        FTLDB[("pihole-FTL.db<br/>Langzeit-Query-Log")]
    end

    subgraph UpstreamDNS["Souveräner rekursiver Resolver"]
        Unbound["Lokaler Unbound Daemon<br/>127.0.0.1:5335 (DNSSEC)"]
    end

    Workstation -->|"DNS-Anfrage A/AAAA"| FTLDNS
    IoT -->|"Telemetrie-Lookup"| FTLDNS
    FTLDNS <-->|"Sub-ms B-Tree Abgleich"| GravityDB
    FTLDNS <-->|"Zero-Disk Cache"| SHM
    FTLDNS -.->|"Asynchroner Batch-Commit"| FTLDB
    FTLDNS -->|"Zulässiger Cache-Miss"| Unbound

Blocking-Modus-Semantik (NULL vs. NXDOMAIN)

Pi-hole verwendet standardmäßig BLOCKINGMODE=NULL, wodurch für blockierte Domains 0.0.0.0 (IPv4) und :: (IPv6) zurückgegeben werden:

Blocking-Modus DNS-Antwort Verhalten des Client-Betriebssystems Empfohlener Einsatzbereich
NULL (Standard) 0.0.0.0 / :: Sofortiger Verbindungsabbruch (ECONNREFUSED / ENETUNREACH in < 1 ms). Produktionsstandard: Vermeidet Verbindungs-Timeouts und Retry-Wellen.
NXDOMAIN RCODE=3 (Non-Existent Domain) RFC-konforme Signalisierung, dass die Domain nicht existiert. Sinnvoll für RFC-strikte Tests; manche unsaubere IoT-Firmware wiederholt Anfragen aggressiv.
NODATA RCODE=0 mit 0 Answer-Records Zeigt an, dass Domain existiert, aber nicht für den angefragten Typ (A/HTTPS). Intern für Canary-Domains (use-application-dns.net) verwendet.

2. Container-Deployment & Host-Tuning (systemd-resolved)

Auf modernen Linux-Distributionen bindet der lokale Stub-Listener von systemd-resolved Port 53 auf 127.0.0.53. Deaktivieren Sie diesen Stub-Listener, bevor Pi-hole an Port 53 gebunden wird:

Port 53 von systemd-resolved freigeben
sudo mkdir -p /etc/systemd/resolved.conf.d
cat << 'EOF' | sudo tee /etc/systemd/resolved.conf.d/disable-stub.conf
[Resolve]
DNSStubListener=no
EOF
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved

Produktions-Compose-Spezifikation (Pi-hole v6 + Unbound)

/opt/pihole/compose.yaml
services:
  pihole:
    image: pihole/pihole:latest
    container_name: pihole-ftl
    hostname: dns-sinkhole
    restart: unless-stopped
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "127.0.0.1:8080:80/tcp"
      - "127.0.0.1:8443:443/tcp"
    environment:
      TZ: "Europe/Berlin"
      FTLCONF_webserver_api_password: "${PIHOLE_API_PASSWORD}"
      FTLCONF_dns_upstreams: "127.0.0.1#5335"
      FTLCONF_dns_dnssec: "true"
      FTLCONF_dns_listeningMode: "single"
      FTLCONF_dns_cache_size: "25000"
    volumes:
      - ./etc-pihole:/etc/pihole:Z
    cap_add:
      - NET_BIND_SERVICE
      - NET_RAW
    shm_size: "256m"

3. FTL-Konfiguration (pihole.toml) & SSD/NVMe-Verschleißschutz

Pi-hole v6 bündelt alle früheren Konfigurationsdateien (setupVars.conf, pihole-FTL.conf, dnsmasq.d) in einer zentralen /etc/pihole/pihole.toml.

/etc/pihole/pihole.toml
[dns]
  upstreams = [ "127.0.0.1#5335" ]
  CNAMEdeepInspect = true
  blockESNI = true
  EDNS0ECS = true
  dnssec = true
  listeningMode = "local"

  [dns.cache]
    size = 25000
    optimizer = 3600

  [dns.rateLimit]
    count = 1500
    interval = 60

[database]
  # In-Memory-Abfragen alle 600s (10 min) statt 60s committen, um Flash-Speicher zu schonen
  DBimport = true
  maxDBdays = 90
  DBinterval = 600
  useWAL = true

[misc]
  privacylevel = 0

CNAME Cloaking Deep Inspection (CNAMEdeepInspect = true)

Werbenetzwerke umgehen einfache Domain-Blocklisten oft, indem Website-Betreiber eine Subdomain der eigenen Domain (z. B. metrics.example.com) per CNAME-Record auf Tracker-Server delegieren. Mit CNAMEdeepInspect = true prüft pihole-FTL jedes Zwischen- und Endziel der CNAME-Kette gegen gravity.db, bevor eine Antwort zurückgeliefert wird.


4. SQLite3 Gravity-Verwaltung & Präzise Regex-Filter

Statt ausschließlich über die Web-Oberfläche können Gravity-Blocklisten, Gruppenrichtlinien und kompilierte POSIX-Regex-Muster direkt via SQLite3 gesteuert werden:

Gravity-Regeln per SQLite3 prüfen und verwalten (/etc/pihole/gravity.db)
-- Spezifische IoT-Quarantäne-Filtergruppe anlegen
INSERT OR IGNORE INTO "group" (enabled, name, description)
VALUES (1, 'IoT_Quarantine', 'Block all telemetry and cloud phone-home endpoints');

-- Hochwirksame kompilierte Regex-Filter gegen Smart-TV- und OS-Telemetrie eintragen
INSERT OR IGNORE INTO domainlist (type, domain, enabled, comment) VALUES
(3, '(^|\.)(metrics|telemetry|analytics|adservice|doubleclick)\.', 1, 'Generische Telemetrie-Subdomains'),
(3, '^.*(acr|samsungcloudsolution|samsungelectronics|lgad)\.com$', 1, 'Smart-TV ACR-Fingerprinting'),
(3, '(^|\.)wpad\.', 1, 'WPAD-Proxy-Auto-Discovery-Vergiftung verhindern');

Gravity-Datenbank im Shared Memory ohne Neustart des DNS-Sockets neu laden:

Gravity kompilieren & FTL-Listen neu laden
pihole reloadlists

5. Hartcodiertes DNS & DoH/DoT-Bypässe an der Firewall unterbinden

Zahlreiche IoT-Geräte (Chromecast, Smart-TVs) ignorieren DHCP Option 6 und senden DNS-Anfragen direkt an 8.8.8.8:53 oder nutzen verschlüsseltes DoT (853/tcp). Mit nftables lässt sich die DNS-Lufthoheit am Router erzwingen:

/etc/nftables.d/dns-enforcement.nft
table inet dns_enforcement {
    chain prerouting {
        type nat hook prerouting priority dstnat; policy accept;

        # Alle externen Port-53-Anfragen aus dem LAN transparent auf Pi-hole (192.168.10.2) umleiten
        iifname "lan0" ip saddr != 192.168.10.2 udp dport 53 dnat to 192.168.10.2:53
        iifname "lan0" ip saddr != 192.168.10.2 tcp dport 53 dnat to 192.168.10.2:53
    }

    chain forward {
        type filter hook forward priority filter; policy accept;

        # Ausgehendes DNS-over-TLS (Port 853) für alle Clients außer dem DNS-Server sperren
        iifname "lan0" ip saddr != 192.168.10.2 tcp dport 853 reject with tcp reset
        iifname "lan0" ip saddr != 192.168.10.2 udp dport 853 reject
    }
}

6. Verifikation & Diagnostik

Prüfen Sie FTL-Cache-Metriken über die lokale DNS-Chaos-Klasse (CHAOS TXT) und testen Sie DNSSEC sowie Sinkhole-Antworten:

Sinkhole-Auflösung, Cache-Hit-Rate & DNSSEC verifizieren
# 1. Prüfen, ob blockierte Domain in < 1ms 0.0.0.0 liefert
dig @127.0.0.1 doubleclick.net A +short

# 2. Live FTL-Cache-Metriken über CHAOS TXT abfragen
dig +short chaos txt cachesize.bind @127.0.0.1
dig +short chaos txt hits.bind @127.0.0.1
dig +short chaos txt misses.bind @127.0.0.1

# 3. Verifizieren, dass DNSSEC bei ungültiger Signatur mit SERVFAIL abbricht
dig @127.0.0.1 sigfail.verteiltesysteme.net A