X.509-Zertifikate mit acme.sh und Let’s Encrypt

acme.sh ist ein alternativer Client für Let’s Encrypt, der alle Funktionen zur Erzeugung von SSL-Zertifikaten in einem Shellskript zur Verfügung stellt

Let’s Encrypt ist das Mittel der Wahl für jeden, der schnell und kostenfrei X.509-Zertifikate für seinen Server erstellen möchte. Neben dem offiziellen Client gibt es eine Vielzahl alternativer Tools, die über das ACME-Protokoll Kontakt zur Zertifizierungsstelle aufnehmen. acme.sh ist ein Ansatz, der komplett als Shellskript geschrieben ist. In diesem Beitrag zeige ich, wie man damit Zertifikate erstellt, verlängert und verwaltet.

Zum offiziellen Let’s Encrypt-Client gibt’s hier im Blog bereits einen Beitrag in englischer Sprache, ebenso zur Funktionsweise von Wildcard-Zertifikaten und zum Zusammenspiel mit RouterOS.

Auch mein Blog nutzt ein Zertifikat von Let's Encrypt, das mit acme.sh erstellt wurde
Auch mein Blog nutzt ein Zertifikat von Let’s Encrypt, das mit acme.sh erstellt wurde

In diesem Beitrag stelle ich die Nutzung von acme.sh vor, das im Gegensatz zum offiziellen Tool komplett als Shellskript programmiert wurde und keinen Python-Interpreter voraussetzt.

Die Installation von acme.sh

Am einfachsten installiert man das Paket direkt aus den offiziellen GitHub-Quellen. Grundsätzlich lässt sich acme.sh ohne Root-Rechte nutzen – sollen allerdings Zertifikate eingespielt und Serverdienste neu gestartet werden, sind entsprechende Rechte vonnöten. Im folgenden Beispiel wird der Client durch ein einleitendes

sudo su

daher als root installiert. Zur Installation aus dem Git-Repository ist der entsprechende git-Client vonnöten, zudem wird die Installation von socat empfohlen, falls der Standalone-Modus für acme.sh genutzt wird – dazu später mehr. Unter Debian oder Ubuntu finden diese beiden Pakete mittels

apt-get install git socat

ihren Weg aufs System. Das eigentliche Repository wird dann ins Root-Verzeichnis ausgecheckt:

cd ~
git clone https://github.com/Neilpang/acme.sh.git

In dieser Form kann der Client allerdings noch nicht benutzt werden, da unter anderem die Integration per Cron fehlt. Diese erledigt das integrierte Installationsprogramm:

cd acme.sh
./acme.sh --install --home /opt/acme.sh --config-home /etc/acme.sh --accountemail "mail@adresse"

Damit wird acme.sh nach /opt/acme.sh installiert, die Konfiguration unter /etc/acme.sh angelegt und die E-Mail-Adresse für Informationen zum Ablauf von Zertifikaten gesetzt. Anschließend lässt sich das Repository durch ein einfaches

cd ..
rm -rf acme.sh

vom System entfernen, es wird nicht mehr benötigt, da der Client nun installiert ist. Bevor’s weitergeht sollte man sich einmal neu am System anmelden, um die aktualisierte Bash-Konfiguration zu aktivieren, die einen Alias für acme.sh setzt.

Als letzten Schritt der Installation deaktiviere ich das automatische Update mittels

acme.sh --upgrade --auto-upgrade 0

Das ist sicher Geschmackssache – mir gefällt der Gedanke nicht, ein mit root-Rechten laufendes Skript nicht über die offiziellen Paketquellen meiner Distribution zu aktualisieren. Von Zeit zu Zeit empfiehlt es sich daher, das Paket händisch über

acme.sh --upgrade

auf die aktuelle Version zu bringen.

Das Erstellen von Zertifikaten

Jetzt ist acme.sh bereit, das erste Zertifikat zu erstellen! Das Tool übernimmt dabei auf Wunsch alle notwendigen Schritte:

  • das Erstellen des privaten Schlüssels samt Certificate Signing Request
  • die Validierung der Domain
  • die Beantragung des Zertifikats
  • die Installation in den gewünschten Zielpfad
  • das Neustarten der Serverdienste
  • sowie die automatische Verlängerung

Um überhaupt ein Zertifikat zu erstellen, muss die Inhaberschaft an der Domain nachgewiesen werden, wofür diverse Betriebsmodi zur Verfügung stehen.

Der Webroot-Modus

Die vermutlich gängigste Variante führt über das so genannte .well-known-Verzeichnis: Dabei erwartet Let’s Encrypt einen speziellen, zufällig gewählten Dateinamen in ebendiesem Verzeichnis. Existiert diese Datei mit dem korrekten Inhalt, kann das Zertifikat ausgestellt werden – diese Variante wird auch Webroot-Modus genannt und erfordert Schreibzugriff auf den Webserver. Ein Beispiel:

acme.sh --issue --test --keylength 4096 --domain meine.domain --webroot /srv/www/meine.domain --cert-file /etc/ssl/certs/meine.domain.cert.pem --key-file /etc/ssl/private/meine.domain.key.pem --ca-file /etc/ssl/certs/meine.domain.ca.pem --fullchain-file /etc/ssl/certs/meine.domain.chain.pem --reloadcmd "service nginx force-reload"

Dieser Befehl bedeutet Folgendes:

  • Es soll ein Zertifikat ausgestellt werden (–issue).
  • Die Anfrage erfolgt nur im Testbetrieb über den so genannten Staging-Server (–test). Somit kann gefahrlos getestet werden, ohne ein gültiges Zertifikat zu erzeugen oder die Rate Limits auszulösen.
  • Es wird ein Schlüssel mit 4096 Bits erstellt (–keylength 4096). acme.sh erstellt auf Wunsch auch ECC-Schlüssel (–keylenght ec-384), die jedoch noch nicht von jedem Server und Client unterstützt werden, weshalb ich sie an dieser Stelle nicht näher behandle.
  • Die zu validierende Domain lautet meine.domain (–domain meine.domain). Ausschließlich dieser konkrete Name ist im Zertifikat abgedeckt.
  • Das Root-Verzeichnis der Domain, in der acme.sh die temporäre .well-known-Datei erzeugt, liegt in /srv/www/meine.domain. Der Webserver muss Dateien aus diesem Verzeichnis ausliefern können. Probleme gab es in meinen Tests unter anderem bei Joomla, da das CMS die Adresse als interne Seite interpretiert. Abhilfe schaffen entweder spezielle Regeln im Webserver, oder die Nutzung im Standalone-Modus (siehe unten).
  • Das erzeugte Zertifikat speichert acme.sh standardmäßig in /etc/acme.sh/domainname. Es wird aber ausdrücklich davon abgeraten, dieses Verzeichnis in den jeweiligen Diensten anzugeben. Stattdessen kopiert acme.sh auf Wunsch alle nötigen Dateien in ein definiertes Zielverzeichnis, so auch hier (–cert-file, –key-file, –ca-file, –fullchain-file). In der Regel genügt es, beispielsweise in nginx das Keyfile und das Fullchain-File zu nennen, um das Zertifikat samt Zertifizierungskette korrekt auszuliefern. Die anderen Dateien sind meist nicht nötig und können bei Bedarf auch weggelassen werden.
  • acme.sh beherrscht zudem den Neustart von Serverdiensten nach erfolgreicher Zertifikatserneuerung. Im obigen Beispiel wird nginx neu gestartet, sobald das Zertifikat erneuert wurde.

War dieser Test erfolgreich, geht es an die eigentliche Erstellung des Zertifikats. Ein Beispiel, mit Änderungen zur vorherigen Befehlszeile in Fettdruck:

acme.sh --issue --force --keylength 4096 --domain meine.domain --webroot /srv/www/meine.domain --cert-file /etc/ssl/certs/meine.domain.cert.pem --key-file /etc/ssl/private/meine.domain.key.pem --ca-file /etc/ssl/certs/meine.domain.ca.pem --fullchain-file /etc/ssl/certs/meine.domain.chain.pem --reloadcmd "service nginx force-reload ; service postfix force-reload ; service dovecot force-reload" --domain www.meine.domain

Was ist anders?

  • Der Befehl –test ist einem –force gewichen. Es wird nun kein Testzertifikat mehr angefordert, sondern ein gültiges von der “richtigen” Zertifizierungsstelle. Das –force ist nur nötig, weil auch das Testzertifikat im System hinterlegt ist und jetzt überschrieben werden muss. Wer direkt und ohne Testzertifikat ans Werk geht, der lässt –force sicherheitshalber weg, um nicht ungewollt etwas zu überschreiben.
  • Ist ein Zertifikat in mehreren Diensten genutzt, so lassen sich diese der Reihe nach neustarten. Im Beispiel startet acme.sh neben nginx auch Dovecot und Postfix neu, wenn ein Zertifikat erfolgreich verlängert wurde.
  • Let’s Encrypt erlaubt auch mehrere Domains bzw. Hostnamen. Im Beispiel wird neben meine.domain auch die Adresse www.meine.domain abgedeckt, eine in der Praxis vermutlich sehr häufig auftretende Konstellation. acme.sh versucht die Validierung dieser Adressen jeweils im selben Verzeichnis. Theoretisch ist im so genannten Hybrid-Modus auch eine Kombination verschiedener Validierungen möglich.

Der Standalone-Modus

In manchen Fällen führt der Webroot-Modus nicht ans Ziel. Das ist unter anderem dann der Fall, wenn der genutzte Server die .well-known-Adresse nicht unterstützt. Joomla hat die URL in meinen Tests beispielsweise immer als Seitentitel interpretiert und seine eigene Fehlerseite zurückgeliefert, vermutlich dürften sich aber auch Redmine und Co. ähnlich verhalten. Eine Möglichkeit, dem Problem zu begegnen ist die Anpassung im Webserver, oder aber die Nutzung des Standalone-Modus. Der hilft auch dann, wenn auf der Maschine beispielsweise gar kein Webserver läuft.

Im Standalone-Modus bedient sich acme.sh des zuvor installierten Tools socat und lauscht beim Erstellen und Verlängern des Zertifikats kurzzeitig auf Port 80, um die .well-known-Anfrage zu beantworten. Ein Beispiel:

acme.sh --issue --force --keylength 4096 --domain meine.domain --standalone --pre-hook "service nginx stop" --post-hook "service nginx start" --cert-file /etc/ssl/certs/meine.domain.cert.pem --key-file /etc/ssl/private/meine.domain.key.pem --ca-file /etc/ssl/certs/meine.domain.ca.pem --fullchain-file /etc/ssl/certs/meine.domain.chain.pem --reloadcmd "service nginx force-reload"

Im Vergleich zum vorherigen Befehl die Änderungen wieder fett hervorgehoben:

  • Der Standalone-Modus ist aktiviert (–standalone).
  • Sofern ein Webserver läuft, wird dieser vorher beendet (–pre-hook) und nach erfolgter Zertifikatserstellung wieder gestartet (–post-hook).
  • Auch hier lassen sich wieder mehrere Domains und verschiedene Befehle zum Neustart der Dienste kombinieren, wie weiter oben gezeigt.

Der DNS-Modus

Eine weitere Möglichkeit, die für die Erstellung von Wildcard-Zertifikaten sogar zwingend ist, ist die Validierung der Domain via DNS-Eintrag. Dabei gibt acme.sh einen Text aus, der zur Zonendatei hinzugefügt werden muss. Großer Nachteil ist, dass Verlängerungen hier nur händisch möglich sind, denn der Text ändert sich regelmäßig. Das sieht dann beispielsweise so aus:

_acme-challenge IN TXT 123a3aBxz1tLPow0x7J3BL8Dj9atPkO3rjDo9J-PAtl

Die genannte Challenge erhält man durch folgende Befehlszeile:

acme.sh --issue --test --keylength 4096 --domain meine.domain --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please --cert-file /etc/ssl/certs/meine.domain.cert.pem --key-file /etc/ssl/private/meine.domain.key.pem --ca-file /etc/ssl/certs/meine.domain.ca.pem --fullchain-file /etc/ssl/certs/meine.domain.chain.pem --reloadcmd "service nginx force-reload"

Die wichtigsten Punkte sind wiederum fett hervorgehoben:

  • Als Modus wird die DNS-Validierung aktiviert (–dns).
  • Man bestätigt ausdrücklich, die Hinweise zu diesem Verfahren gelesen und verstanden zu haben (–yes-I-know-dns-manual-mode-enough-go-ahead-please).

Eine Besonderheit: Trotz mehrerer Versuche bei korrektem TXT-Rekord gelang es mir zunächst nicht, die Validierung mit –force durchzuführen. Der Client gab dann aber den entscheidenden Hinweis – nach dem ersten Versuch muss statt –issue der Befehl –renew genutzt werden, dann funktioniert es auch.

Weitere Modi

acme.sh stellt noch eine Vielzahl weiterer Modi zur Verfügung, die in der offiziellen Dokumentation vorgestellt werden. Besonders interessant, aber von mir leider noch nicht getestet, ist dabei der Stateless-Modus. Er scheint zu ermöglichen, die Validierung nochmals wesentlich einfacher über den Webserver zu erzeugen, selbst wenn kein Schreibzugriff auf das Webroot besteht. Dabei wird der Fingerprint des Let’s Encrypt-Accounts als .well-known-Adresse veröffentlicht, was das ganze Verfahren nochmals vereinfachen dürfte.

Die Verlängerung

Die Verlängerung konnte ich noch nicht testen, da ich erst vor einigen Tagen auf acme.sh gestoßen bin. Der folgende Absatz ist also mit etwas Vorsicht zu genießen. ;-)

Bei der Installation richtet acme.sh einen Cronjob ein, der jeden Tag nachsieht, ob Domains zur Verlängerung anstehen (siehe als root: crontab -l). Die Zertifikate von Let’s Encrypt gelten 90 Tage, nach 60 Tagen führt der Client in der Regel eine Verlängerung durch, sodass bei Problemen genug Zeit bleibt um zu reagieren. Zumindest Fehler sollten vom Client per E-Mail an den Root-Nutzer gemeldet werden.

Die derzeit aktiven Zertifikate und die Ablaufzeiten listet folgender Befehl auf:

acme.sh --list

Es kann also nicht schaden, regelmäßig nachzusehen, ob auch wirklich alle Zertifikate rechtzeitig erneuert wurden oder ob es Probleme gab. Die bei der Beantragung des Zertifikats übermittelten Werte, insbesondere für den Neustart der Serverdienste, speichert acme.sh unter /etc/acme.sh/meine.domain/meine.domain.conf in den Variablen Le_Webroot, Le_PreHook, Le_PostHook, Le_RenewHook und Le_ReloadCmd, wo sich diese auch ändern oder ergänzen lassen.

Eines der Argumente für acme.sh ist, dass der private Schlüssel des Zertifikats im Gegensatz zum offiziellen Let’s Encrypt-Client gerade nicht bei jeder Verlängerung neu erzeugt wird. Das ermöglicht, die Zertifikate auch für TLSA-Records (DANE) und HPKP (Key Pinning) einzusetzen und war mein primärer Grund, den Client zu wechseln.

Eine manuelle Verlängerung von Zertifikaten ist natürlich ebenfalls möglich:

acme.sh --renew --domain meine.domain

Unterstützt wird auch eine Verlängerung aller Zertifikate:

acme.sh --renew-all --stopRenewOnError

Das Entfernen von Zertifikaten geht mittels

acme.sh --remove --domain meine.domain

Die Dateien werden dabei jedoch nicht automatisch gelöscht – im Beispielsfall sollten das Verzeichnis /etc/acme.sh/meine.domain sowie die Dateien /etc/ssl/certs/meine.domain.cert.pem, /etc/ssl/certs/meine.domain.chain.pem, /etc/ssl/certs/meine.domain.ca.pem und /etc/ssl/private/meine.domain.key.pem händisch gelöscht werden.

Florian Effenberger

Autor: Florian Effenberger

Florian engagiert sich seit über 14 Jahren für freie Software und ist einer der Gründer der The Document Foundation, der Stiftung hinter LibreOffice

Schreibe einen Kommentar