Let's-Encrypt-Zertifikate für interne Hosts

04.08.2021 | Jens Meißner in howto

Struktur und Voraussetzungen

Bevor Let’s Encrypt ein Zertifikat ausstellt, muss der Antragsteller nachweisen, dass er Zugriff auf die Domain hat, für welche er das Zertifikat beantragt. Dafür wird das ACME-Protokoll (RFC 8555) verwendet. Der Antragsteller teilt der Let’s-Encrypt-API zunächst mit, für welche Domain das Zertifikat ausgestellt werden soll. Daraufhin schickt die API eine Zeichenkette zurück, welche zum Lösen der Challenge verwendet werden soll. Bei der HTTP-01-Challenge muss diese Zeichenkette in einer definierten Datei auf dem Webserver abgelegt werden. Im zweiten Schritt wird der API mitgeteilt, dass die Datei jetzt vorhanden ist. Das Let’s-Encrypt-Netzwerk wird die Datei jetzt abrufen und den Inhalt mit der vorgegebenen Zeichenkette vergleichen. Stimmen beide überein, wird das Zertifikat ausgestellt und an den Antragsteller übermittelt.

Zum Lösen der DNS-01-Challenge muss ein DNS-Eintrag nach dem Muster _acme-challenge.example.com. 120 IN TXT "orOjQnAXNtgvtsekwqHcjCnJvYGMH15fxzmkadIojYg" angelegt werden. Viele DNS-Provider bieten APIs an, über welche diese Einträge angelegt und wieder gelöscht werden können. Die Benutzung dieser APIs hat jedoch zwei Nachteile: Erstens muss auf dem Host, welcher den Dienst bereitstellt, der API-Schlüssel für die Domain hinterlegt werden und zweitens sind Aktualisierungen der DNS-Einträge in der Regel langsam, da die Betreiber der Domain-Infrastruktur aus Redundanz-Gründen mehrere DNS-Server betreiben und die Änderungen repliziert werden müssen. Auf schnelle Updates ist diese Infrastruktur in der Regel nicht ausgelegt. Die hier vorgestellte Lösung verwendet daher einen weiteren DNS-Server speziell zum Lösen der Challenges. Da die Validierung über das DNS stattfindet, muss der Server, für den das Zertifikat bestimmt ist, durch das Let’s-Encrypt-Netzwerk nicht erreichbar sein.

Folgende Voraussetzungen müssen erfüllt sein:

  • Der Dienst ist über einen validen Domainnamen ansprechbar (z.B. intranet.example.com), dieser muss aber nicht öffentlich auflösbar sein
  • Es besteht die Möglichkeit für die Subdomain _acme-challenge einen NS-Record im öffentlichen DNS anzulegen
  • Es gibt einen öffentlich erreichbaren Host, auf welchem ein DNS-Server installiert werden kann

Dieser Artikel geht davon aus, dass sowohl auf dem DNS-Server als auch auf dem Endpunkt ein Debian 10 (Buster) installiert ist. Für andere Betriebssysteme können die Befehle geringfügig abweichen.

Das DNS ist ein hierarchisches System, welches in verschiedene Zonen aufgeteilt wird. In jeder Zone kann festgelegt werden, dass ein Teil des untergeordneten Namensraums eine eigene Zone bildet und von einen anderen DNS-Server verwaltet wird. Dieser Vorgang heißt Delegation. Beispielsweise könnte in der Zone example.com eine Delegation für subzone.example.com erfolgen. In diesem Artikel wird die Delegation nutzen, um die bereits erwähnte _acme-challenge-Subdomain zu delegieren. Aber zuerst wird ein DNS-Server benötigt.

DNS-Server einrichten

Der Host, welcher als DNS-Server dienen soll, benötigt eine öffentliche IP-Adresse und der Port 53 muss sowohl per UDP als auch per TCP in der Firewall aus dem Internet freigegeben sein.

Zunächst wird Knot als DNS-Server installiert: apt-get install -y knot

Zur Authentifizierung des Clients wird TSIG (RFC 2845) verwendet. Dafür generieren wir als nächstes einen TSIG-Schlüssel: keymgr -t intranet.example.com Es handelt sich dabei um einen symmetrischen Schlüssel, welcher sowohl dem DNS-Server als auch dem ACME-Client bekannt sein muss. Soll der DNS-Server für mehrere ACME-Clients verwendet werden, so sollte für jeden Client ein eigener Schlüssel generiert werden.

Der erzeugte Schlüssel wird zusammen mit einer ACL in die Konfigurationsdatei /etc/knot/knot.conf eingetragen. Zusätzlich wird die Zone im Server angelegt und der Zone die ACL zugeordnet. Damit ersichtlich ist, was zusammen gehört, bekommen Schlüssel und ACL den Domainnamen ohne das Präfix _acme-challenge. als ID. Die ACL sorgt dafür, dass ein Client, welcher im Besitz des Schlüssels ist, per DNS-Update gemäß RFC 2136 die Zone verändern kann.

Die fertige Konfigurationsdatei könnte wie folgt aussehen:

server:
    rundir: "/run/knot"
    user: knot:knot
    listen: [ 0.0.0.0@53, ::@53 ]

template:
  - id: default
    storage: "/var/lib/knot"
    file: "%s.zone"

key:
  - id: intranet.example.com
    algorithm: hmac-sha256
    secret: mdfC6fOf8ENZ3pNY0H60vpwx7nI7LvhPeQNu2ruBbBM=

acl:
  - id: intranet.example.com
    key: intranet.example.com
    action: update

zone:
  - domain: _acme-challenge.intranet.example.com
    acl: intranet.example.com

Außer der Konfigurationsdatei wird eine Zonendatei für die Domain benötigt. Bei mehreren Domains eine Datei für jede Domain. Die Zonendateien werden im Verzeichnis /var/lib/knot gespeichert und haben als Dateinamen den Namen der Zone mit dem Suffix .zone. Die Zonendatei für das Beispiel heißt _acme-challenge.intranet.example.com.zone und sieht wie folgt aus; der Hostname hinter SOA bzw. NS ist der Hostname des DNS-Servers:

@ 3600 IN SOA acme.example.com. zonemaster.intranet.example.com. ( 0 3600 1800 604800 60 )
@ 86400 IN NS acme.example.com.

Delegation der _acme-challenge-Zone

Der DNS-Server ist jetzt funktionsfähig, wird aber noch nicht verwendet. Damit er verwendet wird, muss in der übergeordneten Zone ein Verweis eingetragen werden, die untergeordnete Zone also delegiert werden.

Für den DNS-Server muss, sofern nicht bereits vorhanden, ein A-Record für die IP-Adresse angelegt werden. Der DNS-Server muss also einen Namen haben, zu einer IP-Adresse kann keine Delegation erfolgen.

acme.example.com. IN A 198.51.100.28

Jetzt kann die Delegation der _acme-challenge-Zonen erfolgen. Dafür wird ein NS-Record angelegt, welcher zum DNS-Server zeigt:

_acme-challenge.intranet.example.com. IN NS acme.example.com.

Nachdem die Records hinterlegt sind, kann es noch eine Weile dauern, bis sie auf alle autoritativen Nameserver repliziert wurden. Eine weitere Quelle für Verzögerungen können Caches sein, in welchen veraltete Daten zwischengespeichert sind. Im Zweifel ist etwas Geduld notwendig. Da es bei der _acme-challenge-Zone keine Replikation gibt, besteht das Problem der Verzögerungen nur bei der Einrichtung der Delegation.

Wenn alles funktioniert hat, sollte der Befehl dig _acme-challenge.intranet.example.com. IN ANY die beiden in der Zonendatei stehenden Records zurückgeben.

Einrichtung von acme.sh

Nachdem DNS-Server und Delegation eingerichtet sind, kann der Endpunkt eingerichtet werden. Als Let’s-Encrypt-Client soll in diesem Beispiel acme.sh verwendet werden, es funktioniert aber auch jeder andere ACME-Client, welcher die DNS-01-Challenge und DNS-Updates nach RFC 2136 unterstützt.

acme.sh kann mit folgendem Befehl installiert werden:

curl https://get.acme.sh | sh -s email=admin@example.com

Zusätzlich wird das Programm nsupdate aus dem Paket dnsutils benötigt: apt-get install -y dnsutils

Das Schlüsselmaterial muss in eine Datei gespeichert werden:

key "intranet.example.com" {
    algorithm hmac-sha256;
    secret: "mdfC6fOf8ENZ3pNY0H60vpwx7nI7LvhPeQNu2ruBbBM=";
};

Die Datei sollte für andere Benutzer nicht lesbar sein: chmod 600 nsupdate.key

acme.sh erwartet die Adresse des Nameservers und den Pfad zur Datei mit dem Schlüsselmaterial als Umgebungsvariablen, diese müssen vorher gesetzt werden:

export NSUPDATE_SERVER='acme.example.com'
export NSUPDATE_KEY='/path/to/nsupdate.key'

Jetzt sind die Vorbereitungen abgeschlossen und das Zertifikat kann bei der Zertifizierungsstelle angefordert werden:

acme.sh --issue --server letsencrypt --dns dns_nsupdate --dnssleep 0 --domain intranet.example.com

Dieser Befehl wird ein Schlüsselpaar erzeugen, eine Zertifikatsanfrage (Certificate Sign Request, CSR) erstellen und an die Zertifizierungsstelle (Let’s Encrypt) übermitteln, von dort einen Challenge-Code erhalten und diesen per nsupdate im DNS hinterlegen. Die anschließende Wartezeit kann aufgrund der zusätzlichen Zone entfallen (--dnssleep 0). Jetzt wird der Zertifizierungsstelle mitgeteilt, dass der Challenge-Code hinterlegt ist und geprüft werden kann. War die Prüfung erfolgreich, wird die Zertifizierungsstelle das Zertifikat ausstellen, zum Schluss wird dieses heruntergeladen und gespeichert.

Wenn alles funktioniert hat, wird acme.sh das Zertifikat in base64-kodierter Form ausgeben und die Pfade zu vier Dateien anzeigen, in welchen der Schlüssel und das Zertifikat gespeichert sind. Von diesen vier Dateien werden in der Regel der private Schlüssel mit der Endung .key und das Zertifikat einschließlich der CA-Zertifikate mit dem Dateinamen fullchain.cer benötigt.

.acme.sh/intranet.example.com/intranet.example.com.key
.acme.sh/intranet.example.com/fullchain.cer

Mit diesen Pfaden kann jetzt die Software konfiguriert werden, welche die Zertifikate verwenden soll, also beispielsweise ein Webserver. Das ausgestellte Zertifikat hat eine Gültigkeit von 90 Tagen, acme.sh wird aber rechtzeitig vor Ablauf des Zertifikats ein Neues anfordern, dazu wurde während der Installation ein Cronjob eingerichtet.

Fazit

Die Zertifikate von Let’s Encrypt erfreuen sich großer Beliebtheit. Der Einsatz im Intranet erfordert etwas Vorarbeit, ist aber möglich. Wenn einmal alles eingerichtet ist, erfolgt die Erneuerung der Zertifikate genauso unbemerkt wie für öffentliche Webserver.

Jens Meißner
Jens Meißner
Jens Meißner arbeitet seit 2018 als Linux-Consultant bei B1 Systems und beschäftigt sich mit den Themen Netzwerk, Virtualisierung, Automatisierung und Monitoring. Am liebsten benutzt er Debian, interessiert sich neben Linux aber auch für FreeBSD und OpenBSD.

 


Haben Sie Anmerkungen oder Nachfragen? Melden Sie sich unter blog%b1-systems.de
Col 2