Let’s Encrypt Zertifikate sind inzwischen fürs Webhosting weit verbreitet: Die Akzeptanz ist hervorragend, die Erstellung sowie regelmäßigen Verlängerungen sind gut automatisierbar. Außer bei den inzwischen angebotenen Wildcard-Zertifikaten. Hier ist Handarbeit angesagt. Oder doch nicht?

Mit Tools wie certbot (https://certbot.eff.org) lassen sich sehr leicht Zertifikate verwalten. Von der Erstellung, der regelmäßigen Verlängerung sowie dem löschen falls man ein Zertifikat nicht mehr benötigt ist alles drin. Ebenso lassen sich Zertifikate bearbeiten um z.B. weitere SAN Einträge hinzuzufügen. Bei Wildcard-Zertifikaten hört diese Freundlichkeit jedoch abrupt auf: „DNS-01 challenge“. Schlichtweg gesagt bedeutet dies, dass man für die Prüfung ob man berechtigt ist dieses Zertifikat zu beantragen Einträge im DNS vornehmen muss. Und Änderungen in DNS-Zonen sind leider nicht so ohne weiteres möglich, noch startet sich mal eben rasch ein Dummy-DNS-Server der einmalig die Anfragen entsprechend beantwortet. Hier spielen zu viele Faktoren rein, einer nennt sich „Delegierung“. Alles in allem stellt das ein Problem dar. Ganz anders als bei der sonst oftmals verwendeten „HTTP-01 challenge“ welche sich wunderbar in den unterschiedlichsten Umgebungen automatisieren lässt. Weitere Details zu den Challenges? Finden sich hier: https://letsencrypt.org/de/docs/challenge-types/.

Aber irgendwie muss es doch gehen, zumal man laut RFC 2136 dynamisch DNS-Zonen bearbeiten kann (https://tools.ietf.org/html/rfc2136). Bis vor einigen Monaten fehlten noch praktikable Beispiele dazu im Internet. Inzwischen kann man einige finden, wie zum Beispiel diese hier:

Im Anschluss daran folgt „meine“ Variante dessen wie ich Sie gerne einsetze.

Voraussetzungen

Wie im RFC 2136 beschrieben ist es möglich DNS-Zonen zur Laufzeit zu bearbeiten. Also genau das was man für eine Automatisierung für die „DNS-01 challenge“ von Let’s Encrypt braucht. Ein fertiges Plugin hierfür fehlt im certbot: https://certbot.eff.org/docs/using.html#dns-plugins. Und einen der gelisteten DNS-Anbieter verwendet ebenfalls nicht unbedingt jeder.

An der Stelle helfe ich mir jedoch selbst: Ich betreibe eine eigene DNS-Serverlandschaft basierend auf dem ISC bind9 (https://www.isc.org/bind/). Ok, das hat nicht unbedingt jeder. Es reicht aber auch eine DNS-Delegation der Subdomain _acme-challenge.<domain>. Bei mir also z.B. _acme-challenge.gehirn-mag.net an meinen eigenen, kleinen DNS. Der muss ja nur diese eine Zone verwalten und denn kann man, nachdem die Erstellung/Verlängerung durch ist auch bereits wieder anhalten. Aber wie gesagt, bei mir laufen die DNS-Server sowieso bereits auf mehreren Maschinen. Ein Vorteil für mich 😉

Umsetzung

Nameserver bind

Also fix die Zonendatei von gehirn-mag.net um folgende Zeile ergänzt:

_acme-challenge IN NS ns.gehirn-mag.net.

Das erstellen einer eigenen Zone für _acme-challenge hat für mich den Vorteil, dass ich nur diesen Teilbereich für Änderungen editierbar machen muss und nicht die gesamte Zone. Und/oder wie oben bereits erwähnt: Die wenigsten DNS-Hoster erlauben Updates via nsupdate. Spätestens hier muss man ohnehin diese Subdomain an einen eigen verwalteten DNS-Server übergeben.

Genaue Details zur Einrichtung von bind9 würden den Rahmen dieses Beitrags sprengen. Hierfür verweise ich an dieser Stelle auf die bind9 Dokumentation.

Die Delegation ist gesetzt. Jetzt fehlt noch die passende Zonendatei dazu:

$ORIGIN .
$TTL 60 ; 1 minute
_acme-challenge.gehirn-mag.net. IN SOA ns.gehirn-mag.net. mein.gehirn-mag.net. (
   1   ; serial
   60  ; refresh (1 minute)
   120 ; retry (2 minutes)
   300 ; expire (5 minutes)
   60  ; minimum (1 minute)
)
   IN NS ns.gehirn-mag.net.

Nun die Zonendatei in der bind9 Konfig einfügen:

include "/etc/bind/_acme-challenge.gehirn-mag-net.key";

zone "_acme-challenge.gehirn-mag.net." {
   type master;
   file "master/zone._acme-challenge.gehirn-mag.net";
   allow-update { key "_acme-challenge.gehirn-mag.net"; };
};

Dieser Eintag lädt die obige Zonendatei und erlaubt das ändern via nsupdate sofern man den passenden Key hat. Dieser wird über die include Anweisung entsprechend geladen. Dafür muss dieser jedoch ebenfalls noch erstellt werden:

dnssec-keygen -r /dev/urandom -a HMAC-MD5 -b 512 -n HOST _acme-challenge.gehirn-mag.net

Den Inhalt des key-Feldes aus der .private Datei fügt man nun in der oben erwähnten include Datei wie folgt ein:

key "_acme-challenge.media-contents.de" {
   algorithm hmac-md5;
   secret "<der erstellte key in der Form viele Buchstaben mit zwei =-Zeichen am Ende ;-)";
};


Das war der erste Teil bezüglich der bind Konfiguration. Mit den üblichen Verdächtigen kann diese Konfig entsprechend geprüft werden:

# Vor dem neuladen von bind ein Konfig Check:
named-checkconf -z

# Nach dem neuladen von bind ein paar DNS-Abfragen
host -tns _acme-challenge.gehirn-mag.net
host -tsoa _acme-challenge.gehirn-mag.net
host -ttxt _acme-challenge.gehirn-mag.net

Der letzte host Befehl liefert natürlich nur dann einen Eintrag zurück wenn es ein passenden TXT record in der Zonendatei gibt. Dieser kann für Testzwecke zum Beispiel in der Form „IN TXT „1234“ in der Zonendatei eingefügt werden. Die spätere Automatisierung für die Zertifikatsverwaltung löscht diesen Eintrag selbstständig wieder raus.

certbot

Es gibt neben certbot noch weitere Programme um Let’s Encrypt Zertifikate zu verwalten. Ich selbst mag certbot weil ich die Bedienbarkeit und Funktionalität gut finde. Für andere Programme mag das folgende sinngemäß funktionieren.

Damit certbot dem DNS die notwendigen Änderungen mitteilen kann muss ein entsprechendes Skript aufgerufen werden. Die Schnittstelle hierzu findet sich in den validation hooks von cerbot: https://certbot.eff.org/docs/using.html#pre-and-post-validation-hooks. Mit diesen kann vor der Authentifizierung ein passendes Skript aufgerufen werden welches die Änderungen im DNS vornimmt.

Meine Variante dieses Skripts habe ich unter /etc/letsencrypt/scripts/dns-auth.sh wie folgt abgelegt:

#!/bin/bash

if [ -z "$CERTBOT_DOMAIN" ] || [ -z "$CERTBOT_VALIDATION" ]
then
echo "EMPTY DOMAIN OR VALIDATION"
exit -1
fi

HOST="_acme-challenge"
KEYFILE="/etc/letsencrypt/scripts/K_acme-challenge.gehirn-mag.net.+157+17834.private"

/usr/bin/nsupdate -v -k ${KEYFILE} << EOM
server ns.gehirn-mag.net
zone ${HOST}.${CERTBOT_DOMAIN}
update delete ${HOST}.${CERTBOT_DOMAIN} TXT
update add ${HOST}.${CERTBOT_DOMAIN} 300 TXT "${CERTBOT_VALIDATION}"
send
EOM

Anzupassen ist der Verweis zur mit dnssec-keygen angelegten Schlüsseldatei. Letztendlich prüft das Skript ob die von certbot gesetzte notwendigen Umgebungsvariablen definiert sind und löscht via nsupdate die alten TXT records aus der Zonendatei und setzt anschließend die neuen, benötigten Einträge für die DNS challenge.

Aufgerufen wird das ganze nun wie folgt – falls man bereits Let’s Encrypt Zertifiakte verwendet kann man –agree-tos und –email auch weglassen:

certbot certonly -n --manual-public-ip-logging-ok
  --server https://acme-v02.api.letsencrypt.org/directory
  --manual --preferred-challenges=dns
  --manual-auth-hook /etc/letsencrypt/scripts/dns-auth.sh
  -d "gehirn-mag.net" -d "*.gehirn-mag.net"
  --agree-tos --email mein@gehirn-mag.net

Zur besseren Lesbarkeit ist diese Zeile oben umgebrochen. Zur tatsächlichen Ausführung ist der Befehl in eine Zeile zusammen zu fassen. Die Verlängerung funktioniert wieder wie gehabt und kann zum Beispiel über die in vielen Paketinstallationen mitgebrachte Automatik erfolgen.

Fazit

Ok, es ist natürlich mehr Arbeit wie ein „einfaches“ Let’s Encrypt Zertifikat welches via HTTP geprüft wird. Der DNS-Server muss entsprechend konfiguriert werden. Das ist nun mal so und soll auch gar nicht schöner geredet werden als es letztendlich ist. Eine Alternative zum Wildcard ist und bleibt, sofern die Anzahl der Subdomains überschaubar ist, einfach viele Let’s Encrypt Zertifikate zu verwenden oder halt eines mit entsprechend vielen SAN Einträgen.

Sollte man dies dennoch öfters benötigen kann man über Automatisierungen via Orchestrierung ala ansible nachdenken und das auth-hook Skript über entsprechende Anpassungen für weitere Domains verwertbar machen. Das ist der Blick nach vorne in eine große Zukunft.

Mir für den Moment reicht die technische Spielerei und das gute Gefühl mit dem auth-hook wieder mal etwas automatisiert zu haben wo so zunächst eigentlich „manuell“ dabei stand. Hach, die Welt ist schön 🙂