Letsencrypt DNS Verification RFC2136
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.org), 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.NAMEim 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.org" {
type master;
file "example.org";
update-policy {
grant acme-update name _acme-challenge.example.org. TXT;
};
};
- Wildcard und Hauptname (
example.orgund*.example.org) teilen sich denselben Namen_acme-challenge.example.org— eine einzige Freigabe reicht für beide, dort stehen dann zwei TXT-Werte gleichzeitig. fileohne 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.org, der nicht im Wildcard-Zertifikat steckt) brauchen einen eigenen Grant:grant acme-update name _acme-challenge.wiki.example.org. 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/, Besitzerbind: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.org
- Zonendatei bearbeiten
- rndc thaw example.org
- 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.org zone example.org update add _acme-challenge.wiki.example.org. 60 TXT "test" send quit
- dig +short TXT _acme-challenge.wiki.example.org @ns1.example.org
"test"
- Aufräumen
- nsupdate -k /root/acme-update.key
server ns1.example.org zone example.org update delete _acme-challenge.wiki.example.org. 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_serverist die Adresse des Nameservers, bei dem die Zone dynamisch änderbar ist (der Primary).dns_rfc2136_nameist der Schlüsselname,dns_rfc2136_secretder Wert aus deracme-update.key.- Die Datei muss
600sein, sonst warnt Certbot. - Vom Webserver zum Nameserver müssen UDP und TCP auf Port 53 offen sein.
Zertifikat ausstellen
- certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/rfc2136.ini --dns-rfc2136-propagation-seconds 10 --cert-name wiki.example.org -d wiki.example.org -d example.org --agree-tos -m admin@example.org
- Ergebnis
/etc/letsencrypt/live/wiki.example.org/fullchain.pem /etc/letsencrypt/live/wiki.example.org/privkey.pem
- Wildcard
- certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/rfc2136.ini --dns-rfc2136-propagation-seconds 10 --cert-name example.org -d example.org -d '*.example.org'
Bestehendes Zertifikat (HTTP) auf DNS umstellen
Ab Certbot 4.0 (Debian 13) ohne neu auszustellen:
- certbot reconfigure --cert-name wiki.example.org --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/rfc2136.ini --dns-rfc2136-propagation-seconds 10
- grep -E "authenticator|dns_rfc2136" /etc/letsencrypt/renewal/wiki.example.org.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.org @ns1.example.org
- 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-keygenerzeugen, in derupdate-policyaustauschen, 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