Dynamische DNS-Zonen und DNSSEC schließen sich nicht aus. Mit BIND 9.20 lässt sich eine bestehende, per DHCP/DDNS gepflegte Zone nachträglich auf DNSSEC mit automatischer Schlüsselverwaltung und Inline-Signing umstellen.

In diesem Projekt ging es darum, mehrere bereits produktiv verwendete dynamische Zonen eines internen DNS-Servers nachträglich mit DNSSEC abzusichern. Dabei zeigte sich allerdings ein interessanter Fehlerzustand: Nach der Aktivierung von Inline-Signing wurden DDNS-Änderungen zwar in der unsignierten Zone angenommen, aber nicht mehr in der signierten Zone sichtbar.

Die anschließende Reparatur war ein kleines, aber lehrreiches BIND-Recovery-Projekt.


Ausgangssituation

Die Umgebung bestand aus zwei BIND-DNS-Servern:

  • einem primären DNS-Server
  • einem sekundären DNS-Server
  • BIND 9.20.x
  • dynamische DNS-Zonen
  • Kea DHCP mit Kea DHCP-DDNS
  • TSIG zur Authentifizierung der DNS-Updates
  • Zonentransfers zwischen Primary und Secondary
  • DNSSEC sollte nachträglich aktiviert werden

Die internen DNS-Namen und Netzwerke sind in diesem Artikel anonymisiert.

Die DHCP-Infrastruktur aktualisierte unter anderem:

example.internal.
168.192.in-addr.arpa.
16.172.in-addr.arpa.

Die Forward-Zone und die beiden Reverse-Zonen wurden bereits dynamisch per DDNS gepflegt.

Zusätzlich gab es eine wichtige Anforderung:

Die Zonen müssen weiterhin gelegentlich manuell bearbeitet werden können.

Das bedeutet, dass eine reine Dynamic-DNS-Konfiguration ohne Inline-Signing keine Option war. Die manuelle Bearbeitung erfolgt weiterhin über:

rndc freeze <zone>

Datei bearbeiten, anschließend:

rndc thaw <zone>

Dabei muss bei manuellen Änderungen selbstverständlich auch die SOA-Serial erhöht werden.


Warum Inline-Signing?

BIND kann DNSSEC auf unterschiedliche Arten mit dynamischen Zonen kombinieren.

Bei einer dynamischen DNSSEC-Zone ohne Inline-Signing werden die DNSSEC-Daten direkt in der dynamischen Zone beziehungsweise deren Journal verwaltet.

Bei Inline-Signing existieren dagegen zwei Zustände:

                 +-------------------+
DDNS ----------> | unsigned zone     |
                 | example.zone       |
                 +---------+---------+
                           |
                           | Inline-Signing
                           v
                 +-------------------+
                 | signed zone       |
                 | example.zone.signed|
                 +-------------------+

Das hat für diesen Anwendungsfall einen entscheidenden Vorteil:

Die ursprüngliche Zone bleibt die Quelle der administrativen Änderungen, während BIND daraus automatisch die signierte Version erzeugt.

Das passt gut zu einer Umgebung, in der sowohl DDNS-Updates als auch gelegentliche manuelle Änderungen stattfinden.


Vorbereitungen

Vor der Migration wurde zunächst geprüft, ob die vorhandenen Zonen überhaupt mit der konfigurierten maximalen TTL kompatibel waren.

In der BIND-Konfiguration war beispielsweise eine maximale TTL von:

86400

gesetzt.

Die vorhandenen Zonen enthielten teilweise noch Einträge wie:

$TTL 259200 ; 3 days

Das verhinderte die Aktivierung der DNSSEC-Konfiguration.

Die betroffenen TTLs wurden daher zunächst auf einen zulässigen Wert reduziert, beispielsweise:

$TTL 1200 ; 20 minutes

Anschließend wurde die Zone mit:

named-checkzone example.internal. /var/named/example.hosts

geprüft.

Erst wenn named-checkzone die Zone mit OK bestätigte, wurde die Konfiguration weiter geändert.


DNSSEC-Konfiguration

Für eine Zone sah die Konfiguration anschließend beispielsweise so aus:

zone "example.internal." {
    type master;
    file "example.hosts";

    dnssec-policy default;
    inline-signing yes;

    allow-update { key DHCP_UPDATER; };

    allow-transfer { 192.0.2.66; };
    also-notify { 192.0.2.66; };
    notify yes;
};

Bei den Reverse-Zonen war die Konfiguration entsprechend:

zone "16.172.in-addr.arpa." {
    type master;
    file "172.16.rev";

    dnssec-policy default;
    inline-signing yes;

    allow-update { key DHCP_UPDATER; };

    allow-transfer { 192.0.2.66; };
    also-notify { 192.0.2.66; };
    notify yes;
};

Danach:

named-checkconf

und anschließend:

rndc reconfig

Erste Prüfung nach der Aktivierung

Der Zustand der Zone lässt sich mit:

rndc zonestatus 16.172.in-addr.arpa.

prüfen.

Ein erfolgreicher Zustand sieht beispielsweise so aus:

name: 16.172.in-addr.arpa.
type: primary
files: 172.16.rev
serial: 2026092919
signed serial: 2026092929
nodes: 28
secure: yes
inline signing: yes
key maintenance: automatic
dynamic: yes
frozen: no

Besonders interessant sind:

secure: yes
inline signing: yes
dynamic: yes

Damit ist zunächst bestätigt, dass BIND die Zone als DNSSEC-geschützte dynamische Inline-Signing-Zone verwaltet.


Test eines vorhandenen Eintrags

Als nächstes wurde ein bereits existierender PTR-Eintrag geprüft:

dig @127.0.0.1 -x 172.16.0.147 +dnssec

Die Antwort enthielt neben dem PTR-Eintrag eine RRSIG:

ANSWER SECTION:
147.0.16.172.in-addr.arpa. 1200 IN PTR one-device.example.internal.
147.0.16.172.in-addr.arpa. 1200 IN RRSIG PTR ...

Damit war klar:

Die vorhandenen Daten werden korrekt signiert.

Anschließend wurde derselbe Eintrag auf dem Secondary geprüft:

dig @192.0.2.66 -x 172.16.0.147 +dnssec

Auch dort erschienen PTR und RRSIG.


Der interessante Teil: DDNS funktioniert – aber nicht mit DNSSEC

Nun wurde ein temporärer PTR-Eintrag per DDNS erzeugt:

server 127.0.0.1
zone 16.172.in-addr.arpa.
update add 251.1.16.172.in-addr.arpa. 1200 PTR dnssec-test.example.internal.
send

Der DNS-Update wurde mit:

status: NOERROR

akzeptiert.

Das sah zunächst gut aus.

Eine anschließende Abfrage ergab jedoch:

status: NXDOMAIN

Der neue PTR war nicht sichtbar.

Noch interessanter wurde es mit:

rndc zonestatus 16.172.in-addr.arpa.

Die Ausgabe zeigte:

serial:        2026092918
signed serial: 2026092927
nodes:         28

Die unsignierte Zone hatte sich also verändert.

Die signierte Zone war dagegen nicht nachgezogen worden.

Troubleshooting: DDNS-Update mit NOERROR, aber Datensatz nicht sichtbar

Wenn nsupdate mit NOERROR endet, der neue Datensatz anschließend aber weder in der DNS-Abfrage noch in der signierten Zone erscheint, lohnt sich zunächst:

rndc zonestatus <zone>

Achte insbesondere darauf, ob sich serial der unsignierten Zone erhöht, während signed serial unverändert bleibt.

Im Log können in diesem Fall Meldungen wie

dns_diff_apply: ... del not exact
receive_secure_serial: not exact

auftauchen. Das deutet darauf hin, dass die unsignierte und die signierte Version der Inline-Signing-Zone nicht mehr konsistent sind.

Wichtig: Nicht sofort die Zone neu erstellen oder inline-signing abschalten. Zuerst ein vollständiges Backup von Zone und Journals erstellen. Anschließend kann die signierte Arbeitskopie kontrolliert neu aufgebaut werden.


Die entscheidende Fehlermeldung

Ein Blick in das Journal zeigte:

dns_diff_apply: 16.0.16.172.in-addr.arpa/PTR/IN: del not exact
zone 16.172.in-addr.arpa/IN (signed): receive_secure_serial: not exact

Diese beiden Meldungen waren der entscheidende Hinweis.

Die unsignierte Zone funktionierte weiterhin.

Das DDNS-Update wurde angenommen und in das Journal der unsignierten Zone geschrieben.

Der Inline-Signer konnte die Änderung jedoch nicht auf die bereits vorhandene signierte Zone anwenden.

Der Zustand war damit vereinfacht:

                 DDNS
                  |
                  v
          +---------------+
          | unsigned zone |
          |      OK       |
          +-------+-------+
                  |
                  X
             Synchronisation
                  |
                  v
          +---------------+
          |  signed zone  |
          |  veraltet     |
          +---------------+

Erst sichern, dann reparieren

Bevor irgendeine Recovery-Maßnahme durchgeführt wurde, wurden alle relevanten Dateien gesichert.

Bei der Reverse-Zone:

mkdir -p /root/bind-recovery-YYYYMMDD/172.16-before-repair

cp -a /var/named/172.16.rev \
      /var/named/172.16.rev.jnl \
      /root/bind-recovery-YYYYMMDD/172.16-before-repair/

cp -a /var/named/172.16.rev.signed \
      /var/named/172.16.rev.signed.jnl \
      /root/bind-recovery-YYYYMMDD/172.16-before-repair/

Damit konnte jederzeit auf den ursprünglichen Zustand zurückgegriffen werden.

Das ist bei einer DNSSEC-Recovery besonders wichtig: Die Journals enthalten unter Umständen Änderungen, die noch nicht in der eigentlichen Zonendatei stehen.


Unsigned Journal synchronisieren

Als nächstes wurde:

rndc sync -clean 16.172.in-addr.arpa.

ausgeführt.

Damit wurden die noch offenen Änderungen des unsignierten Journals in die Zonendatei übernommen.

Danach zeigte zonestatus weiterhin:

serial:        2026092918
signed serial: 2026092927

Die signierte Zone war also weiterhin veraltet.


Signed Zone neu aufbauen

Nun wurde BIND kontrolliert gestoppt:

systemctl stop named

Anschließend wurde geprüft:

systemctl is-active named

Erwartet:

inactive

Die vorhandene signierte Zone wurde nicht gelöscht, sondern zunächst nur umbenannt:

mv /var/named/172.16.rev.signed \
   /var/named/172.16.rev.signed.recovery-old

Interessanterweise war das signierte Journal zu diesem Zeitpunkt bereits nicht mehr vorhanden. Das war kein Problem.

Die ursprüngliche Zonendatei blieb unangetastet.

Anschließend wurde BIND wieder gestartet:

systemctl start named

BIND erzeugte die signierte Zone daraufhin aus dem aktuellen Stand der unsignierten Zone neu.


Kontrolle nach dem Neuaufbau

rndc zonestatus zeigte nun:

serial:        2026092918
signed serial: 2026092928
nodes:         28
secure:        yes
inline signing: yes
dynamic:       yes

Die signierte Serial hatte sich erhöht.

Der zuvor fehlende DDNS-Eintrag war nun ebenfalls wieder sichtbar:

dig @127.0.0.1 251.1.16.172.in-addr.arpa. PTR +dnssec

Antwort:

status: NOERROR

ANSWER SECTION:
251.1.16.172.in-addr.arpa. 1200 IN PTR dnssec-test-172.example.internal.
251.1.16.172.in-addr.arpa. 1200 IN RRSIG PTR ...

Damit war bewiesen:

Der Inline-Signer arbeitet wieder korrekt mit der dynamischen Zone zusammen.


Replikation zum Secondary

Anschließend wurde derselbe Datensatz auf dem Secondary geprüft:

dig @192.0.2.66 251.1.16.172.in-addr.arpa. PTR +dnssec

Auch dort erschienen:

PTR
RRSIG

und zwar mit derselben Signatur.

Damit war auch die Replikation des reparierten Zustands erfolgreich.


Löschtest

Zum Abschluss wurde der temporäre PTR wieder über DDNS gelöscht:

server 127.0.0.1
zone 16.172.in-addr.arpa.
update delete 251.1.16.172.in-addr.arpa. PTR
send

Auch dieser Update wurde mit:

status: NOERROR

bestätigt.

Eine anschließende Abfrage:

dig @127.0.0.1 251.1.16.172.in-addr.arpa. PTR +dnssec

ergab:

status: NXDOMAIN

und enthielt zusätzlich:

SOA
RRSIG SOA
NSEC
RRSIG NSEC

Das ist ein wichtiger Test, denn damit wurde nicht nur die positive DNSSEC-Antwort geprüft, sondern auch die korrekte Signierung negativer Antworten.

Die Gegenprobe auf dem Secondary ergab dasselbe Ergebnis.


Manuelle Änderungen bleiben möglich

Bei der Migration der Forward-Zone wurde zusätzlich eine manuelle Änderung getestet.

Dafür wurde die Zone zunächst eingefroren:

rndc freeze example.internal.

Nach der Änderung der Zonendatei wurde die SOA-Serial erhöht und die Zone geprüft:

named-checkzone example.internal. /var/named/example.hosts

Danach:

rndc thaw example.internal.

Die Änderung wurde anschließend sowohl auf dem Primary als auch auf dem Secondary mit RRSIG ausgeliefert.

Ein wichtiger Punkt dabei:

Bei manuellen Änderungen muss die SOA-Serial erhöht werden.

Nur eine Änderung der Zonendatei ohne Erhöhung der Serial reicht bei diesem Setup nicht zuverlässig aus, damit die Änderung in die signierte Zone übernommen und repliziert wird.


Was wir dabei gelernt haben

Das Projekt hat einige interessante Punkte gezeigt.

1. Dynamisches DNS und DNSSEC funktionieren problemlos zusammen

Kea DHCP-DDNS kann weiterhin ganz normal DNS-Updates durchführen.

DNSSEC ist dabei für die DHCP-Infrastruktur transparent.

Der Ablauf lautet:

Kea DHCP
   |
   | authenticated DNS UPDATE
   v
BIND unsigned zone
   |
   | inline signing
   v
BIND signed zone
   |
   +----> Secondary

2. dnssec-policy default vereinfacht die Schlüsselverwaltung

BIND übernimmt bei Verwendung einer DNSSEC Policy die automatische Schlüsselverwaltung und die regelmäßige Erneuerung beziehungsweise Pflege der Schlüssel.

Dadurch muss nicht für jede Zone manuell eine komplette DNSSEC-Key-Infrastruktur aufgebaut werden.


3. inline-signing ist für diesen Anwendungsfall sinnvoll

Die Zone bleibt dynamisch und kann weiterhin über DDNS aktualisiert werden.

Gleichzeitig bleibt die Möglichkeit erhalten, die Zone kontrolliert mit:

rndc freeze

und:

rndc thaw

manuell zu bearbeiten.


4. receive_secure_serial: not exact ist ernst zu nehmen

Diese Meldung sollte nicht einfach ignoriert werden.

In unserem Fall bedeutete sie:

Die signierte und die unsignierte Zone waren intern nicht mehr synchron.

Die unsignierte Zone konnte weiterhin Updates annehmen, während die signierte Zone diese Änderungen nicht mehr korrekt übernahm.

Das kann besonders gefährlich sein, wenn der Fehler unbemerkt bleibt: DNS-Updates scheinen erfolgreich zu sein, werden aber von DNSSEC-signierten Abfragen nicht sichtbar.


5. Journals sind bei dynamischen Zonen entscheidend

Bei einer dynamischen Zone darf man nicht einfach nur die Zonendatei betrachten.

Relevant sind unter anderem:

zone
zone.jnl
zone.signed
zone.signed.jnl

Deshalb sollte vor einer Reparatur immer eine Sicherung des kompletten relevanten Zustands erstellt werden.


Ein sinnvoller Recovery-Ablauf

Für einen vergleichbaren Fehler würde ich heute folgenden Ablauf verwenden:

1. Fehler reproduzieren bzw. eindeutig feststellen
             |
             v
2. zonestatus prüfen
             |
             v
3. Logs prüfen
   receive_secure_serial: not exact
             |
             v
4. alle Zone-/Journal-Dateien sichern
             |
             v
5. rndc sync -clean
             |
             v
6. named kontrolliert stoppen
             |
             v
7. signed + signed.jnl sichern/verschieben
             |
             v
8. named starten
             |
             v
9. zonestatus prüfen
             |
             v
10. DDNS-Test
             |
             v
11. Primary + Secondary prüfen
             |
             v
12. DDNS-Löschtest
             |
             v
13. NXDOMAIN + NSEC + RRSIG prüfen

Wichtig ist dabei:

Keine Dateien löschen, bevor eine Sicherung des Zustands existiert.


Fazit

Die nachträgliche Migration einer produktiven dynamischen BIND-Zone auf DNSSEC mit Inline-Signing ist grundsätzlich unkompliziert.

Die eigentliche Herausforderung liegt weniger in der DNSSEC-Konfiguration als in der sauberen Behandlung des bestehenden dynamischen Zustands.

In diesem Projekt funktionierte die Aktivierung von DNSSEC zunächst problemlos. Erst der erste echte DDNS-Update zeigte, dass die signierte und unsignierte Zone intern nicht mehr sauber synchronisiert waren.

Die Fehlermeldungen

dns_diff_apply: ... del not exact
receive_secure_serial: not exact

waren dabei der entscheidende Hinweis.

Durch:

  1. Sicherung des vollständigen Zustands,
  2. Synchronisierung des unsignierten Journals,
  3. kontrollierten Neuaufbau der signierten Zone,
  4. anschließende DDNS-, DNSSEC- und Secondary-Tests

konnte die Zone ohne Datenverlust wieder in einen konsistenten Zustand gebracht werden.

Am Ende stand eine Konfiguration, die gleichzeitig:

  • dynamische DHCP-DNS-Updates,
  • DNSSEC,
  • automatische Schlüsselverwaltung,
  • Inline-Signing,
  • Zonentransfers
  • und kontrollierte manuelle Änderungen

unterstützt.

Für mich ist genau das der interessante Teil des Projekts: Nicht die Aktivierung von DNSSEC war die eigentliche Herausforderung, sondern der Moment, in dem eine scheinbar erfolgreiche Konfiguration beim ersten echten DDNS-Update ihren Fehlerzustand offenbarte – und die anschließende kontrollierte Recovery.

Vorheriger Beitrag Nächster Beitrag