DNSSEC-Problem von Adguard Home + Unbound unter OpenWRT

Hallo,
ich habe auf Basis dieser Anleitung auf meine Router x86 mit OpenWrt diese Pakete installiert:

libunbound - 1.21.0-r1
luci-app-unbound - 25.066.49116~3182493
luci-i18n-unbound-de - 25.066.49116~3182493
unbound-anchor - 1.21.0-r1
unbound-checkconf - 1.21.0-r1
unbound-control - 1.21.0-r1
unbound-control-setup - 1.21.0-r1
unbound-daemon - 1.21.0-r1

Da bei OpenWrt AdGuard Home als Service verfügbar ist, habe ich die Anleitung für diesen Service anstatt Pi-hole „modifiziert“.

unbound lauscht auf 127.0.0.1:5335, AdGuard Home lauscht bei jedem Interface auf Port 53:

root@router:~# netstat -tulpn | grep 53
tcp        0      0 127.0.0.1:5335          0.0.0.0:*               LISTEN      9913/unbound
tcp        0      0 172.16.1.2:53           0.0.0.0:*               LISTEN      9381/AdGuardHome
tcp        0      0 127.0.0.1:53            0.0.0.0:*               LISTEN      9381/AdGuardHome
tcp        0      0 172.16.10.2:53          0.0.0.0:*               LISTEN      9381/AdGuardHome
tcp        0      0 ::1:53                  :::*                    LISTEN      9381/AdGuardHome
udp        0      0 172.16.10.2:53          0.0.0.0:*                           9381/AdGuardHome
udp        0      0 172.16.1.2:53           0.0.0.0:*                           9381/AdGuardHome
udp        0      0 127.0.0.1:53            0.0.0.0:*                           9381/AdGuardHome
udp        0      0 127.0.0.1:5335          0.0.0.0:*                           9913/unbound
udp        0      0 ::1:53                  :::*                                9381/AdGuardHome

Im AdGuard Home WebUI werden kann ich in den Statistiken Anfragen zu diesem Upstream sehen: 127.0.0.1:5335

Leider zeigt die Funktionsprüfung DNSSEC-Valdierung nicht den erwarteten Status:

root@router:~# dig fail01.dnssec.works @127.0.0.1 -p 5335

; <<>> DiG 9.20.4 <<>> fail01.dnssec.works @127.0.0.1 -p 5335
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47766
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;fail01.dnssec.works.		IN	A

;; ANSWER SECTION:
fail01.dnssec.works.	2484	IN	A	5.45.109.212

;; Query time: 0 msec
;; SERVER: 127.0.0.1#5335(127.0.0.1) (UDP)
;; WHEN: Sun Mar 09 22:51:55 CET 2025
;; MSG SIZE  rcvd: 64

Ich benötige Unterstützung beim Trouble-Shooting dieses Fehlers.
THX

Du meinst, dass die IP-Adresse ausgegeben wird?

Der DNSSEC-Validierer ist per Default ausgeschaltet. Du könntest es in der Datei /etc/config/unbound also mal damit probieren (ungetestet, Quelle):

config unbound
  option validator '1'

Lt. Blog-Artikel soll keine IP-Adresse und der Status soll SERVFAIL sein.

Ja. Die Frage war so halb rhetorisch. Ich wollte es nur explizit geschrieben haben, damit wir nicht aneinander vorbeireden. Hat der Vorschlag geholfen?

Nein.
Ich hätte darauf hinweisen sollen, dass ich die manuelle Konfiguration aktiviert habe.
Das heißt, es wird genau die unbound Konfiguration verwendet, die im Blog Artikel dokumentiert ist:

server:
    # If no logfile is specified, syslog is used
    # logfile: "/var/log/unbound/unbound.log"
    username: unbound
    pidfile: /var/run/unbound.pid
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt

    verbosity: 0

    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes

    # You want to leave this to no unless you have *native* IPv6. With 6to4 and
    # Terredo tunnels your web browser should favor IPv4 for the same reasons
    prefer-ip6: no

    # Use this only when you downloaded the list of primary root servers!
    # If you use the default dns-root-data package, unbound will find it automatically
    root-hints: "/var/lib/unbound/root.hints"

    # Trust glue only if it is within the server's authority
    harden-glue: yes

    # Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
    harden-dnssec-stripped: yes

    # Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
    # see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
    use-caps-for-id: no

    # Reduce EDNS reassembly buffer size.
    # IP fragmentation is unreliable on the Internet today, and can cause
    # transmission failures when large DNS messages are sent via UDP. Even
    # when fragmentation does work, it may not be secure; it is theoretically
    # possible to spoof parts of a fragmented DNS message, without easy
    # detection at the receiving end. Recently, there was an excellent study
    # >>> Defragmenting DNS - Determining the optimal maximum UDP response size for DNS <<<
    # by Axel Koolhaas, and Tjeerd Slokker (https://indico.dns-oarc.net/event/36/contributions/776/)
    # in collaboration with NLnet Labs explored DNS using real world data from the
    # the RIPE Atlas probes and the researchers suggested different values for
    # IPv4 and IPv6 and in different scenarios. They advise that servers should
    # be configured to limit DNS messages sent over UDP to a size that will not
    # trigger fragmentation on typical network links. DNS servers can switch
    # from UDP to TCP when a DNS response is too big to fit in this limited
    # buffer size. This value has also been suggested in DNS Flag Day 2020.
    edns-buffer-size: 1232

    # Perform prefetching of close to expired message cache entries
    # This only applies to domains that have been frequently queried
    prefetch: yes

    # One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks
 or on a single machine, it should be unnecessary to seek performance enhancement by increasing num-threads above 1.
    num-threads: 1

    # Ensure kernel buffer is large enough to not lose messages in traffic spikes
    so-rcvbuf: 1m

    # Ensure privacy of local IP ranges
    private-address: 192.168.0.0/16
    private-address: 169.254.0.0/16
    private-address: 172.16.0.0/12
    private-address: 10.0.0.0/8
    private-address: fd00::/8
    private-address: fe80::/10

    ## Performance
    # More cache memory, rrset=msg*2 | Default: 4m, 4m
    msg-cache-size: 32m
    rrset-cache-size: 64m
    # Time  to  live [minimum|maximum] for RRsets and messages in the cache | Default: 0, 86400
    cache-min-ttl: 3600
    cache-max-ttl: 86400
    # Serve old responses from cache with a TTL of 0 in the response without waiting for the actual resolution to finish | Def
ault: no, 0
    serve-expired: yes
    serve-expired-ttl: 86400
    # Fetch DNSKEYs earlier (DNSSEC): More cpu usage, less latency | Default: no
    prefetch-key: yes
    # Helps to reduce the query rate towards targets that get a very high nonexistent name lookup rate | Default: yes
    aggressive-nsec: yes

Nur bei Verwendung dieser Konfiguration lauscht unbound auf 127.0.0.1:5335.

Wenn ich auf die von OpenWrt generierte Konfiguration umstelle, dann sehen die Services etwas anders aus:

root@router:~# netstat -tulpn | grep 53
tcp        0      0 172.16.10.2:53          0.0.0.0:*               LISTEN      3268/AdGuardHome
tcp        0      0 127.0.0.1:8953          0.0.0.0:*               LISTEN      6776/unbound
tcp        0      0 0.0.0.0:5335            0.0.0.0:*               LISTEN      6776/unbound
tcp        0      0 127.0.0.1:53            0.0.0.0:*               LISTEN      3268/AdGuardHome
tcp        0      0 172.16.1.2:53           0.0.0.0:*               LISTEN      3268/AdGuardHome
tcp        0      0 ::1:53                  :::*                    LISTEN      3268/AdGuardHome
tcp        0      0 ::1:8953                :::*                    LISTEN      6776/unbound
tcp        0      0 :::5335                 :::*                    LISTEN      6776/unbound
udp        0      0 172.16.10.2:53          0.0.0.0:*                           3268/AdGuardHome
udp        0      0 172.16.1.2:53           0.0.0.0:*                           3268/AdGuardHome
udp        0      0 127.0.0.1:53            0.0.0.0:*                           3268/AdGuardHome
udp        0      0 0.0.0.0:5335            0.0.0.0:*                           6776/unbound
udp        0      0 ::1:53                  :::*                                3268/AdGuardHome
udp        0      0 :::5335                 :::*                                6776/unbound

Wenn ich dann dig fail01.dnssec.works @172.16.1.2 -p 53 ausführe, dann ist die Ausgabe so wie gewünscht:

root@router:~# dig fail01.dnssec.works @172.16.1.2 -p 53

; <<>> DiG 9.20.4 <<>> fail01.dnssec.works @172.16.1.2 -p 53
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 25862
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;fail01.dnssec.works.		IN	A

;; Query time: 0 msec
;; SERVER: 172.16.1.2#53(172.16.1.2) (UDP)
;; WHEN: Mon Mar 10 19:48:12 CET 2025
;; MSG SIZE  rcvd: 48

Mir kam gerade, dass es am fehlenden trust anchor liegen könnte, also den DNSSEC-Daten für die Root-Zone. Per OpenWRT-Konfiguration weiß Unbound vielleicht, wo es diese findet, aber nicht per eigenen Default-Einstellungen. Damit habe ich diese Webseite auf cambus.net gefunden, die Folgendes angibt:

We also need to fetch (or update) the root anchor manually:

opkg install unbound-anchor
unbound-anchor -a "/etc/unbound/root.key"

Configuring Unbound to validate answers:

# If you want to perform DNSSEC validation, run unbound-anchor before
# you start unbound (i.e. in the system boot scripts).  And enable:
# Please note usage of unbound-anchor root anchor is at your own risk
# and under the terms of our LICENSE (see that file in the source).
auto-trust-anchor-file: "/var/lib/unbound/root.key"

Edit: auto-trust-anchor-file korrigiert.

Nachdem ich dies implementiert habe verweigert der Unbound-Service zu starten.
Hier ein Screenshot der relevanten Fehlermeldungen u.a. von Befehl `unbound-checkconf’ und die Ausgabe des Logfiles:

Ich verstehe nicht warum diese Fehler gemeldet werden.

Ich nehme an wegen des Jails, siehe „HOW TO: Manual Override“ für Unbound auf OpenWRTs GitHub. Unbound sucht in /etc/unbound/, obwohl es dort keinen Zugriff hat. Daher musst du es wohl auf /var/lib/unbound umbiegen:

All of /etc/unbound (persistent, ROM) is copied to /var/lib/unbound (tmpfs, RAM). Edit your manual /etc/unbound/unbound.conf to reference this /var/lib/unbound location for included files. Note in preparation for a jail, /var/lib/unbound is chown unbound. Configure for security in/etc/unbound/unbound.conf with options username:unbound and chroot:/var/lib/unbound.

username finde ich in deiner Konfig, chroot noch nicht. Und mein letzter Hinweis muss dementsprechend vermutlich auch korrigiert werden. Also:

server:
  chroot: "/var/lib/unbound"
  auto-trust-anchor-file: "/var/lib/unbound/root.key"

Problem behoben, Unbound startet ohne Fehler.
Die Konfiguration musste wie folgt angepasst werden:

server:
    # If no logfile is specified, syslog is used
    # logfile: "/var/log/unbound/unbound.log"
    username: unbound
    chroot:/var/lib/unbound
    pidfile: /var/run/unbound.pid
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt

    verbosity: 0

    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes

    # You want to leave this to no unless you have *native* IPv6. With 6to4 and
    # Terredo tunnels your web browser should favor IPv4 for the same reasons
    prefer-ip6: no

    # Use this only when you downloaded the list of primary root servers!
    # If you use the default dns-root-data package, unbound will find it automatically
    root-hints: "root.hints"
[...]
   ## Privacy | Default: no, no
    hide-identity: yes
    hide-version: yes
    auto-trust-anchor-file: "root.key"