Letsencrypt DNS Verification RFC2136

Aus Xinux Wiki
Version vom 22. Oktober 2026, 18:05 Uhr von Thomas.will (Diskussion | Beiträge) (Platzhalter-Domain: example.org durchgehend durch example.com ersetzt)
Zur Navigation springen Zur Suche springen

Prinzip

  • Certbot hat ein eingebautes Plugin, das den TXT-Eintrag für die DNS-Challenge selbst per RFC 2136 (dynamisches DNS-Update) im eigenen BIND setzt und danach wieder löscht.
  • Das ist der neue Weg gegenüber dem alten mit Hook-Skript (siehe Letsencrypt DNS Verification):
Alter Weg Neuer Weg (Plugin)
Skript eigenes certbot-dns-validation.sh keins, das Plugin macht das
Schlüssel steht im Skript eigene Datei, nur root lesbar
Erneuerung langer certbot renew …-Aufruf mit Hook läuft automatisch über certbot.timer
Rechte im BIND meist ganze Zone nur TXT an den _acme-challenge-Namen
  • Voraussetzung: ein eigener BIND, in dem du Zonen dynamisch ändern darfst.

Wann braucht man das

  • Port 80 ist nicht erreichbar oder wird gefiltert (Firewall, Sperrlisten gegen Bots).
  • Wildcard-Zertifikate (*.example.com), das geht nur per DNS-Challenge.
  • Das Zertifikat soll vor einem DNS-Umzug fertig sein, der Name zeigt noch auf den alten Server.
  • Die HTTP-Prüfung macht Probleme (Weiterleitungen, mehrere Server hinter einem Namen).

Überblick

Certbot (Webserver) --nsupdate (TSIG)--> BIND  <--TXT-Abfrage-- Let's Encrypt
  • Certbot setzt den TXT-Eintrag _acme-challenge.NAME im BIND.
  • Let's Encrypt fragt die autoritativen Nameserver ab und prüft den Wert.
  • Certbot löscht den Eintrag wieder.

1. BIND vorbereiten

Schlüssel erzeugen

Auf dem Nameserver
  • tsig-keygen -a hmac-sha256 acme-update > /etc/bind/acme-update.key
  • chown root:bind /etc/bind/acme-update.key
  • chmod 640 /etc/bind/acme-update.key
  • cat /etc/bind/acme-update.key
key "acme-update" {
    algorithm hmac-sha256;
    secret "BASE64-GEHEIMNIS==";
};

Der Name des Schlüssels (hier acme-update) ist die Identität im BIND. Das Geheimnis wird gleich für Certbot gebraucht und nirgends sonst abgelegt.

Schlüssel einbinden und Zone freigeben

Datei named.conf.local
include "/etc/bind/acme-update.key";

zone "example.com" {
    type master;
    file "example.com";
    update-policy {
        grant acme-update name _acme-challenge.example.com. TXT;
    };
};
  • Wildcard und Hauptname (example.com und *.example.com) teilen sich denselben Namen _acme-challenge.example.com — eine einzige Freigabe reicht für beide, dort stehen dann zwei TXT-Werte gleichzeitig.
  • file ohne Pfad und ohne Endung, wie bei den anderen Zonen auch: BIND sucht sie im konfigurierten Zonenverzeichnis.
  • Zusätzliche, eigenständige Namen außerhalb der Wildcard (z. B. ein eigener vHost wie wiki.example.com, der nicht im Wildcard-Zertifikat steckt) brauchen einen eigenen Grant: grant acme-update name _acme-challenge.wiki.example.com. TXT; — nur nötig, wenn dafür wirklich ein eigenes Zertifikat per DNS-Challenge ausgestellt wird.
  • Der Schlüssel darf nur TXT an diese Namen ändern, sonst nichts in der Zone.
  • Die Kurzform grant acme-update zonesub TXT; erlaubt TXT überall in der Zone. Das ist bequemer, aber weniger streng.
Wichtig
die Zone ist jetzt dynamisch
  • Die Zonendatei muss für den BIND-Benutzer beschreibbar sein (unter Debian /var/cache/bind/, Besitzer bind:bind — /etc/bind/ und /var/lib/bind/ gehören root und sind für den BIND-Prozess nicht beschreibbar).
  • Von Hand ändern geht nur noch mit Einfrieren, sonst gehen Änderungen verloren:
    • rndc freeze example.com
    • Zonendatei bearbeiten
    • rndc thaw example.com
  • Neben der Zonendatei liegt ein Journal (.jnl), das gehört zur Zone und darf nicht gelöscht werden.
Konfiguration laden
  • named-checkconf
  • rndc reconfig

2. Zugang testen (noch ohne Certbot)

Vom Webserver aus, mit kopierter Schlüsseldatei

Auf dem Webserver ist kein BIND installiert, /etc/bind/ gibt es dort also nicht. Die Schlüsseldatei landet einfach irgendwo, z. B. unter /root/:

  • scp /etc/bind/acme-update.key webserver:/root/acme-update.key
  • nsupdate -k /root/acme-update.key
server ns1.example.com
zone example.com
update add _acme-challenge.wiki.example.com. 60 TXT "test"
send
quit
  • dig +short TXT _acme-challenge.wiki.example.com @ns1.example.com
"test"
Aufräumen
  • nsupdate -k /root/acme-update.key
server ns1.example.com
zone example.com
update delete _acme-challenge.wiki.example.com. TXT
send
quit

Klappt das, ist der BIND-Teil fertig. Meldet er REFUSED, siehe Fehlersuche.

3. Certbot einrichten

Plugin installieren

  • apt install certbot python3-certbot-dns-rfc2136
  • certbot --version

Zugangsdatei

  • install -m 600 /dev/null /etc/letsencrypt/rfc2136.ini
  • vim /etc/letsencrypt/rfc2136.ini
dns_rfc2136_server = IP-DES-NAMENSERVERS
dns_rfc2136_port = 53
dns_rfc2136_name = acme-update.
dns_rfc2136_secret = BASE64-GEHEIMNIS==
dns_rfc2136_algorithm = HMAC-SHA256
  • dns_rfc2136_server ist die Adresse des Nameservers, bei dem die Zone dynamisch änderbar ist (der Primary).
  • dns_rfc2136_name ist der Schlüsselname, dns_rfc2136_secret der Wert aus der acme-update.key.
  • Die Datei muss 600 sein, sonst warnt Certbot.
  • Vom Webserver zum Nameserver müssen UDP und TCP auf Port 53 offen sein.

Zertifikat ausstellen

  • Pro Zweck ein eigenes Zertifikat: einen eigenständigen vHost (z. B. wiki.example.com) und die Apex-Domain (example.com, ggf. mit Wildcard) nicht in einen gemeinsamen --cert-name mischen, außer beide werden wirklich vom selben Webserver-vHost mit derselben Konfiguration bedient.
Einzelner Host
  • certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/rfc2136.ini --dns-rfc2136-propagation-seconds 10 --cert-name wiki.example.com -d wiki.example.com --agree-tos -m admin@example.com
Ergebnis
/etc/letsencrypt/live/wiki.example.com/fullchain.pem
/etc/letsencrypt/live/wiki.example.com/privkey.pem
Wildcard
  • certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/rfc2136.ini --dns-rfc2136-propagation-seconds 10 --cert-name example.com -d example.com -d '*.example.com'
  • Das Sternchen unbedingt in Anführungszeichen, sonst expandiert ihn die Shell gegen Dateinamen im aktuellen Verzeichnis, bevor certbot ihn überhaupt sieht.
  • Existiert das Zertifikat schon ohne Wildcard, reicht ein erneutes certbot certonly mit demselben --cert-name und der zusätzlichen SAN -d '*.example.com' — es wird dann um den Wildcard-Eintrag erweitert, nicht neu angelegt.
  • Ohne Anführungszeichen vergessen und stattdessen zweimal denselben Namen angegeben (-d example.com -d example.com) stellt certbot klaglos ein Zertifikat ohne Wildcard aus — kein Fehler, nur fehlt dann die zweite SAN.

Bestehendes Zertifikat (HTTP) auf DNS umstellen

Ab Certbot 4.0 (Debian 13) ohne neu auszustellen:

  • certbot reconfigure --cert-name wiki.example.com --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/rfc2136.ini --dns-rfc2136-propagation-seconds 10
  • grep -E "authenticator|dns_rfc2136" /etc/letsencrypt/renewal/wiki.example.com.conf
authenticator = dns-rfc2136

Certbot macht dabei einen Testlauf. Danach erneuert sich das Zertifikat mit der DNS-Challenge.

4. Erneuerung

  • Das Debian-Paket bringt den Timer mit, Cron ist nicht nötig:
    • systemctl list-timers | grep certbot
  • Damit der Webserver das neue Zertifikat auch lädt, braucht es einen Hook:
    • vim /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/sh
systemctl reload nginx
    • chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
  • Testen (Certbot wartet im automatischen Modus bis zu 8 Minuten per Zufallsverzögerung, mit dem Schalter geht es sofort):
    • certbot renew --dry-run --no-random-sleep-on-renew
  • Kontrolle:
    • certbot certificates

Fehlersuche

Meldung / Symptom Ursache Lösung
REFUSED oder NOTAUTH bei nsupdate Schlüsselname oder update-policy passt nicht, oder die Adresse ist nicht der Primary Name des Schlüssels prüfen, _acme-challenge… in der Policy mit Punkt am Ende, named-checkconf
update failed: NOTIMP Zone ist nicht dynamisch update-policy in der Zone, Zonendatei beschreibbar
TSIG error with server: tsig verify failure falsches Geheimnis oder falscher Algorithmus Secret aus acme-update.key kopieren, dns_rfc2136_algorithm passend setzen
TXT-Eintrag ist da, Let's Encrypt findet ihn nicht Sekundäre Nameserver haben die Zone noch nicht übernommen --dns-rfc2136-propagation-seconds erhöhen, also-notify und Zonentransfer prüfen
Zugangsdatei wird bemängelt Rechte zu offen chmod 600 /etc/letsencrypt/rfc2136.ini
Nichts passiert, Zone lässt sich nicht von Hand ändern Zone ist dynamisch rndc freeze, ändern, rndc thaw
Logs
  • journalctl -u named -e
  • tail -f /var/log/letsencrypt/letsencrypt.log
Was Let's Encrypt sieht
  • dig +short TXT _acme-challenge.wiki.example.com @ns1.example.com
  • Die Abfrage geht an die autoritativen Server der Zone, nicht an deinen Resolver.

Sicherheit

  • Der Schlüssel erlaubt nur TXT an den _acme-challenge-Namen. Wer ihn stiehlt, kann Zertifikate für diese Namen bestellen, aber sonst nichts in der Zone ändern. Gib ihm trotzdem nur die Namen, die du wirklich brauchst.
  • Das Geheimnis liegt nur in /etc/letsencrypt/rfc2136.ini (600) und in der Schlüsseldatei am Nameserver (640, root:bind). Nicht in ein Skript, nicht in ein Git-Repository, nicht in ein Wiki.
  • Schlüssel regelmäßig tauschen: neuen mit tsig-keygen erzeugen, in der update-policy austauschen, Zugangsdatei anpassen, den alten entfernen.
  • Je Server einen eigenen Schlüssel. Wird einer entwendet, sperrst du nur diesen.

Merksatz

Plugin statt Skript | Schlüssel nur für TXT an _acme-challenge | Zone wird dynamisch, Änderungen von Hand nur mit rndc freeze und thaw | Erneuerung macht der Timer, nginx lädt per Hook | erst mit nsupdate testen, dann mit Certbot

Links