getssl: Let’s Encrypt ohne Python-Client

Vorwort

Nachdem Mozilla starke Kritik an Wosign und Startcom geübt hat, entfernte nun Apple als erstes die Zertifizierungsstelle aus seinen Trust-Stores. Für den Otto-Normal-Verbraucher heißt das nun, dass die Zertifikate von StartCom und Wosign faktisch wertlos sind; es ist ungewiss, ob sich die CA davon wieder erholt, auch wenn die Maßnahme vorerst auf ein Jahr begrenzt ist, und sich WoSign dann erneut einer unabhängigen Überprüfung unterziehen lassen kann.

Damit bleibt Let’s Encrypt die letzte mir bekannte Möglichkeit, kostenlos SSL-Zertifikate zu nutzen. Und bei aller berechtigten Kritik an dem Vorgehen, Zertifikte nur für 90 Tage auszustellen und diese mit einem Client automatisiert erneuern zu lassen, ist das Verfahren zumindest transparent und quelloffen.

Wem der native Python-Client nicht zusagt, der kann aus einer Vielfalt von alternativen Clients wählen. Ich habe mich für getssl entschieden, und möchte hier kurz eine Einführung geben.

Installation

Getssl ist ein simples Bash-Skript – daher kann auch ein unerfahrener Entwickler in den Code schauen und nachvollziehen, was dort eigentlich passiert. Installiert wird das Skript folgendermaßen:

curl --silent https://raw.githubusercontent.com/srvrco/getssl/master/getssl > /usr/local/sbin/getssl
chmod 700 /usr/local/sbin/getssl

Anschließend kann man das Skript mit -h aufrufen, um sich verfügbare Parameter ausgeben zu lassen:

$ getssl -h
getssl ver. 1.61
Obtain SSL certificates from the letsencrypt.org ACME server

Usage: getssl [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-q|--quiet] [-Q|--mute] [-u|--upgrade] [-U|--nocheck] [-w working_dir] domain

Options:
  -h, --help      Display this help message and exit
  -d, --debug     Outputs debug information
  -c, --create    Create default config files
  -f, --force     Force renewal of cert (overrides expiry checks)
  -a, --all       Check all certificates
  -q, --quiet     Quiet mode (only outputs on error, success of new cert, or getssl was upgraded)
  -Q, --mute      Like -q, but mutes notification about successful upgrade
  -u, --upgrade   Upgrade getssl if a more recent version is available
  -U, --nocheck   Do not check if a more recent version is available
  -w working_dir  Working directory

Konfiguration

Nun kann man die erste Konfiguration für eine Domain anlegen. Für example.org würde der Aufruf so aussehen:

getssl -c example.org

Wenn das der erste Aufruf des Skripts überhaupt ist, wird automatisch das Verzeichnis ~/.getssl angelegt. Darin befindet sich die globale getssl.cfg (die ebenfalls beim ersten Lauf angelegt wird), sowie ein Konfigurationsverzeichnis für jede Domain.

Globale Konfiguration

Hier eine Übersicht über die Konfigurationsvariablen:

CA Let’s Encrypt bietet zwei unterschiedliche APIs. Einmal das Staging-API, welches für Tests vorgesehen ist, und das Live-API, welches die echten Let’s Encrypt Zertifikate ausstellt. Der Unterschied ist, dass das Staging-API keine vertrauenswürdigen Zertifikate erstellt. Dafür gibts dort aber keine Rate-Limits, wie im Live-API. Zum Testen sollte man also unbedingt das Staging-Environment nutzen. Mehr Informationen dazu gibt es hier.

Per Default wird das Staging-API verwendet, was auch sinnvoll ist, da man am anfang sicherlich noch den ein oder anderen Fehler machen wird. Ich persönlich habe das nach wie vor das Staging-API global aktiviert, und überschreibe diese Einstellung erst in den Domain-Konfigurationen. Dann hat man nicht das Problem, dass wenn eine neue Domain dazu kommt, man gleich beim Test das Live-API verwendet.

AGREEMENT Hier ist der Link zur Lizenzvereinbarung hinterlegt. Mit dem Nutzen von Let’s Encrypt stimmt man dieser zu – man sollte Sie daher zumindest mal überflogen haben.
ACCOUNT_EMAIL Die Email-Adresse für den Let’s Encrypt Account.
ACCOUNT_KEY_LENGTH Die Länge des zu generierenden Account-Keys. Dieser wird zur Authentifizierung verwendet.
ACCOUNT_KEY Speicherort des Account-Keys.
PRIVATE_KEY_ALG Algorithmus für den privaten Key. Aktuell kann man dort rsa oder prime256v1 nutzen.
RELOAD_CMD Hier kann ein Befehl definiert werden, der einen bestimmten Dienst (Apache2, nginx, …) automatisch neu startet, wenn ein Zertifikat erneuert wurde.
RENEW_ALLOW Dieser Wert legt fest, wie viele Tage vor Ablauf ein Zertifikat frühestens erneuert werden darf. Ein Zertifikat bei Let’s Encrypt hat eine Laufzeit von 90 Tagen. RENEW_ALLOW steht standardmäßig auf 30, das heißt frühestens 30 Tage vor Ablauf würde das Zertifikat neu ausgestellt. Wenn man getssl täglich als Cronjob laufen lässt kann man diesen Wert auch noch etwas verringern.
SERVER_TYPE Der Server-Typ ist ausschließlich für getssl selbst interessant und hat nichts mit Let’s Encrypt oder dem Zertifikat an sich zu tun. Es definiert den Weg, über den geprüft wird, ob und wie lange das entsprechende Zertifikat noch gültig ist. Ist hier also smtp definiert, wird getssl das Zertifikat von <domain>:25 holen, die Restlaufzeit prüfen, und ggf. neu ausstellen. Steht der Wert CHECK_REMOTE auf false, wird SERVER_TYPE ignoriert.
CHECK_REMOTE Kann „true“ oder „false“ annehmen. Der Wert definiert, ob das Zertifikat überprüft werden soll.
SSLCONF Hier kann eine eigene OpenSSL-Konfiguration mitgegeben werden.
VALIDATE_VIA_DNS Prinzipiell kann Let’s Encrypt auf zwei Wegen eine Domain-Validierung durchführen: Via DNS oder via einer HTTP-Ressource (mehr Infos). Auch getssl unterstützt beide Wege. Möchte man das DNS-Verfahren nutzen, muss hier „true“ eingetragen werden.
DNS_ADD_COMMAND Diese Direktive beinhaltet den Befehl zum setzen von DNS-Einträgen, beispielsweise nsupdate.
DNS_DEL_COMMAND Kommando, um den DNS-Eintrag wieder zu löschen
AUTH_DNS_SERVER Definiert den authoritativen Nameserver
DNS_WAIT Zeit in Sekunden, die gewartet wird, bis der DNS-Eintrag erfolgreich gesetzt wurde
DNS_EXTRA_WAIT Nachdem das Skript selbst erfolgreich geprüft hat, dass der DNS-Eintrag korrekt war, sendet es die Informationen an den ACME-Server, der die Verifikation seinerseits durchführt. Mit der Option DNS_EXTRA_WAIT kann man noch eine Wartezeit einbauen, zwischen der skripteigenen Verifikation und dem Senden der Infos an den ACME-Server.

Für die meisten Leute sollte die Standardeinstellung zureichend sein.

Domain Konfiguration

Wie gesagt, existiert für jede angelegte Domain ein Verzeichnis unterhalb von ~/.getssl. Darin befindet sich eine weitere getssl.cfg, in welcher Einstellungen der globalen Konfiguration überschrieben werden können, und weitere Einstellungen getätigt werden müssen. In der Liste gehe ich nur auf Variablen ein, die nicht in der globalen Konfiguration auftauchen.

SANS Hier kann man SubjectAlternativeNames definieren. Es ist zu beachten, dass Let’s Encrypt für jeden Namen eine separate Prüfung verlangt. Man braucht also für alle Namen administrative Rechte. Wildcard-Einträge sind bei Let’s Encrypt nicht möglich.
ACL Dieser Eintrag ist ein Bash-Array mit Pfaden, an denen das Challenge-File angelegt werden soll. Der erste Eintrag gilt für den eigentlichen Domainname. Wenn es SANS gibt, muss für jeden Name ein Pfad hinzugefügt werden (Reihenfolge entscheidet!). Let’s Encrypt prüft dieses Challenge indem es die Ressource per HTTP(S) abruft:

http(s)://<domain.tld>/.well-known/acme-challenge/<CHALLENGE>

Hat das Zertifikat keine weiteren Namen, enthält ACL logischerweise nur einen Eintrag.

Wenn nicht der lokale Webserver für die Domain zuständig ist, kann man das Challenge auch per SSH oder FTP auf einen entfernten Server schaffen lassen. Die Syntax dazu findet man in der Beispielkonfiguration.

USE_SINGLE_ACL Wenn der lokale Webserver für alle Domains innerhalb des Zertifikats zuständig ist, kann man sich es auch einfach machen und für alle Zertifikate den gleichen ACL-Pfad nutzen. Im Apache kann man beispielsweise einen Alias definieren, der VHost-unabhängig einen bestimmten URL an einen definierten Ort umleitet:

$ cat &lt;&lt;EOF &gt; /etc/apache2/conf-available/letsencrypt.conf
&gt; Alias /.well-known /var/www/letsencrypt/.well-known
&gt; EOF
$ a2enconf letsencrypt
$ apache2ctl configtest &amp;&amp; apache2ctl graceful

Anschließend kann man in der Domain-Konfiguration von getssl den Pfad hinterlegen und für alle Namen nutzen:

SANS=example.org,example.net,example.com
ACL=('/var/www/letsencrypt/.well-known/acme-challenge')
USE_SINGLE_ACL="true"

Somit „versaut“ man sich nicht das DocumentRoot der VHosts. Für andere Webserver ist das natürlich analog möglich.

DOMAIN_CERT_LOCATION
DOMAIN_KEY_LOCATION
CA_CERT_LOCATION
DOMAIN_CHAIN_LOCATION
DOMAIN_KEY_CERT_LOCATION
DOMAIN_PEM_LOCATION
Diese Direktiven geben an, wohin die entsprechenden Bestandteile kopiert werden sollen. Auch hier ist es wieder möglich, einen entfernten Ort per SSH oder FTP anzugeben. Wenn die Variablen nicht gesetzt sind, wird nichts kopiert. Dann existieren nur die lokalen Files in ~/.getssl/<domain>.

Workflow

Nachdem man sowohl die globale als auch die domain-spezifische Konfiguration erstellt und angepasst hat, kann man das Zertifikat ausstellen lassen:

getssl -f &lt;domain&gt;

Der Parameter -f forciert dabei das Erstellen des Zertifikats, unabhängig davon, ob das aktuelle Zertifikat noch gültig ist oder nicht. Wenn es einen Fehler gibt, kann man auch noch -d angeben. Dann läuft getssl im Debug-Modus und ist gesprächiger. Wenn die Ausstellung dann geklappt hat, kann man die Domain auf das Live-API umstellen.

Um getssl periodisch laufen zu lassen kann man einen Cronjob einrichten:

# Zertfikate erneuern
0  3  *  *  *  root      getssl -q -a

Der Parameter -a bewirkt, dass alle Zertifikate überprüft werden, -q heißt, dass getssl keine unnötigen Ausgaben produziert. Der Entwickler empfiehlt noch die Option -u mit anzugeben, welche das Skript selbstständig updated. Das ist natürlich nicht unbedenklich, bedenkt man, was passieren könnte, wenn der Github-Account des Entwicklers kompromittiert würde, und das Update statt einer neuen Version einen Schadcode herunterladen würde. Aber nicht nur das wäre ein Problem, sondern auch eine Änderung im Verhalten des Skripts wäre möglich. Im schlimmsten Fall kriegt man das erst mit, wenn ein Zertifikat abgelaufen ist. Ob man die Auto-Update-Funktionalität also nutzen will, hängt von einem selbst ab.

Fazit

getssl ist eine schöne und saubere Möglichkeit Let’s Encrypt auf dem eigenen Server auszurollen. Auch der Entwickler ist sehr nett und kompetent, und hat mir bei meinen 2 Problemen innerhalb kürzester Zeit geholfen.

2 Gedanken zu „getssl: Let’s Encrypt ohne Python-Client“

  1. Hi,

    vielen Dank für die vielen Infos. Ich habe die Diskussion um WoSign am Rande mitverfolgt und bin ein wenig verunsichert. Ist es, um auf Nummer sicher zu gehen, nicht sinnvoller, lieber ein wenig Geld für die Zertifizierung in die Hand zu nehmen? SSL-Zertifikate kosten ja nun auch nicht die Welt. Bei PSW ( https://www.psw-group.de/ssl-zertifikate/ ) erhalte ich beispielweise die günstigsten Zertifikate für 15 € jährlich. Die meisten Anwender dürften sich mit dem Leistungsumfang dieser Zertifikate auch zufriedengeben.

    Wie siehst Du das?

    Viele Grüße
    Patrick

    1. Auf Nummer sicher muss man eigentlich nicht gehen. Let’s Encrypt ist eine vollwertige CA und auch in den Browsern drin. Mittlerweile sind die Zertifikate drei Monate gültig, und die Erneuerung lässt sich sehr einfach automatisieren. Nicht nur durch den offiziellen Python-Client, sondern auch durch solche einfachen Tools wie getssl.

      Ich würde daher, wenn ein domain-validiertes Zertifikat genügt, kein Geld ausgeben.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.