Eine Zone für alle Zertifikate — DNS-01 ohne dynamische Zonen
Wie der DNS-Nachweis für Let's Encrypt in eine eigene, kleine Zone umgezogen ist — und warum alle anderen Zonen seitdem wieder so bleiben, wie sie geschrieben wurden.
Alle Zertifikate meiner Umgebung kommen von Let's Encrypt, mit dem Nachweis über DNS (DNS-01). Der Client legt dafür einen TXT-Eintrag _acme-challenge.<name> an, Let's Encrypt fragt ihn ab, danach wird er wieder gelöscht. Dafür braucht es keinen von außen erreichbaren Webserver, und es gehen auch Wildcard-Zertifikate.
Bis zum 14. Oktober 2026 schrieb jeder Client diesen Eintrag direkt in die Zone der jeweiligen Domain. Das funktionierte, hatte aber zwei Folgen, die mich gestört haben.
Was daran falsch war
Jede Zone war dynamisch. Damit der Client schreiben darf, braucht die Zone eine update-policy. Ab dann gehört die Zonendatei BIND: Er schreibt sie bei jeder Erneuerung neu, und Kommentare, Reihenfolge und Format sind weg. Von Hand ändern geht nur noch mit rndc freeze und thaw — wer das einmal vergisst, verliert seine Änderung oder BIND seine Zone.
Der Schlüssel durfte zu viel. Der Schlüssel für die Updates lag auf der Firewall und auf dem Proxmox-Host. Er durfte TXT-Einträge in jeder Zone setzen. Geschützt waren nur SPF, DMARC, DKIM und ein paar weitere Namen, über eigene Sperrzeilen. Wer den Schlüssel hatte, konnte trotzdem für jeden Namen ein Zertifikat beantragen.
Der Umbau
Der Nachweis darf auch über einen CNAME laufen. Let's Encrypt folgt ihm und liest den TXT-Eintrag am Ziel. Damit reicht eine dynamische Zone:
_acme-challenge.example.de. CNAME _acme-challenge.example.de.acme.<hauptdomain>.
- Die Zone
acme.<hauptdomain>ist klein, kurzlebig (TTL 60) und an die beiden öffentlichen Nameserver delegiert. - Nur sie ist dynamisch. Der Schlüssel darf dort TXT-Einträge setzen und nirgends sonst.
- Sie ist nicht mit DNSSEC signiert. Das ist gültig: Die übergeordnete Zone beweist signiert, dass es für sie keinen DS gibt. Für den Nachweis braucht es keine Signatur.
- Jeder Name mit Zertifikat bekommt einen CNAME. Der Eintrag gilt auch für das Wildcard-Zertifikat derselben Domain.
Auf den Clients ändert sich wenig. Die OPNsense und Proxmox kennen beide einen Alias-Modus: Der Eintrag entsteht nicht unter dem Namen selbst, sondern unter dem Ziel des CNAME. Neue Domains bekommen den CNAME beim Anlegen gleich mit, das erledigen die Vorlagen.
Stolpersteine
Die Zone ausdrücklich nennen. Ohne Angabe sucht der Client die zuständige Zone selbst, über eine SOA-Abfrage. Fragt er dabei die interne Sicht des Nameservers, kennt die die neue Zone nicht und nennt die übergeordnete. Das Update ginge dann in die falsche Zone — und würde dort jetzt zu Recht abgewiesen. In jedem Client steht die Zone deshalb fest drin.
Das Journal der Katalogzone. Die Zonen verteilen sich über eine Catalog Zone an die Nameserver. Nach einem Neustart von BIND passte deren Journal nicht mehr zur Datei, und die Katalogzone wurde gar nicht geladen. Die neue Zone kam deshalb auf den Nameservern nicht an. Das Puppet-Modul löscht das Journal jetzt, bevor es die Katalogzone neu schreibt und lädt. Es ist ohnehin aus der Datei ableitbar.
Am laufenden System umziehen. Alle Zonen mussten von dynamisch auf statisch wechseln, ohne dass ein Eintrag verloren geht. Ein Skript friert jede Zone ein, liest alten und neuen Inhalt in kanonischer Form, vergleicht Eintrag für Eintrag und übernimmt die neue Datei nur, wenn nichts fehlt außer den alten Nachweisen und einer Liste bewusst entfernter Einträge.
Aufgeräumt, wenn man schon dabei ist
Weil alle Zonen ohnehin neu geschrieben wurden:
- Einheitliches Format in allen 18 Zonen: dieselbe Reihenfolge, dieselben Abstände, Kommentare dort, wo etwas nicht offensichtlich ist.
- Reverse-Einträge in beide Richtungen. Jede eigene öffentliche Adresse, IPv4 wie IPv6, zeigt auf einen Namen, und dieser Name zeigt zurück auf genau diese Adresse. Mailserver und manche Dienste prüfen das.
- DMARC-Berichte gehen für alle Domains an eine eigene Adresse. Damit eine Domain Berichte an eine Adresse in einer anderen Domain schicken darf, braucht die empfangende Domain einen eigenen Freigabe-Eintrag. Ohne ihn verwerfen die Absender die Berichte stillschweigend.
Geprüft, nicht angenommen
Die eigentliche Probe ist ein Versuch, der scheitern muss: Ein Update mit demselben Schlüssel in eine andere Zone. BIND antwortet mit REFUSED. Damit ist belegt, dass die Zonen statisch sind und der Schlüssel nur noch dort schreiben darf, wo er soll. Danach liefen alle Zertifikate auf der Firewall und auf Proxmox einmal über den neuen Weg.
Was ich daraus mitnehme
Ein Werkzeug, das in jeder Zone schreiben darf, macht jede Zone zu seiner. Die CNAME-Delegation kostet eine Zeile je Name, und dafür gehören die Zonen wieder mir.