Seit etwa einem Jahr ist die Zertifizierungsstelle „Let’s Encrypt“ nun schon in Betrieb und hat einen Grad an Stabilität und Unterstützung in Client-Systemen erreicht, der ein breiten Einsatz möglich macht. Let’s Encrypt ist aber mehr als nur eine CA, die kostenfrei domainvalidierte Zertifikate ausgibt, sondern ein ganzes Framework, mit der auch die regelmäßige, automatisierte Erneuerung dieser Zertifikate durchgeführt wird. Und das ist bei den LE-Zertifikaten auch nicht ganz unwichtig, da die Laufzeit auf drei Monate begrenzt ist.

Bisher habe ich für einige Domains und Server Zertifikate von StartSSL genutzt. Nach der Übernahme durch WoSign und der Ankündigung von Mozilla und Apple zukünftig StartSSL-signierten Zertifikaten nicht mehr zu vertrauen muss eine Alternative her. Warum nicht Let’s Encrypt?

Let’s Encrypt Client installieren

Unter OpenBSD braucht es dafür einen Let’s Encrypt-Client, der über das ACME-Protokoll die Authentifizierung und die Beantragung des Zertifikats erledigt. Unter OpenBSD 5.9 und 6.0 (zum Zeitpunkt dieses Artikels ist OpenBSD 6.0 das aktuellste -stable-Release) kann man sich aus den Ports das Paket „letskencrypt“ installieren.

letskencrypt ist ein Projekt zur Entwicklung eines stabilen und vor allem sicheren ACME-Clients in C, der es mittlerweile auch schon in den -current Entwicklerzweig des OpenBSD-Basissystems unter dem Namen „acme-client“ geschafft hat. Mit dem nächsten Release 6.1 kann man also mit hoher Wahrscheinlichkeit auf die Installation des letskencrypt-Ports verzichten.

Script zur Automatisierung

Auf dem OpenBSD-System, das ich mit Zertifikaten versehen möchte sind einige mit nginx betriebene VirtualHosts (unter anderem dieses Blog), ein OpenSMTPD-Server für Empfang und Versand von E-Mails (ebenfalls über mehrere Hostnamen angesprochen) und ein Dovecot IMAP-Server (auch hier mehrere Hostnamen, die per SNI angesprochen werden). All diese Systeme sollen in Zukunft vollautomatisch frische Let’s Encrypt-Zertifikate erhalten, damit die Client-Kommunikation weiterhin ohne Probleme und lästige Fehler- und Warnmeldungen auf Client-Seite ablaufen kann.

Sowohl für das Beantragen neuer als auch für die Erneuerung der schon bestehenden Zertifikate habe ich ein kleines Shell-Script erstellt, das per cron(8) einmal pro Woche ausgeführt wird.

Das Script liest die verwalteten Domains (und Subdomains) aus einer einfachen Textdatei (/etc/letsencrypt/domains.txt). In jeder Zeile steht die Hauptdomain (CommonName/CN des Zertifikats) sowie dahinter durch Leerzeichen getrennt alle Subdomains/AlternativeNames. Let’s Encrypt erlaubt hier bis zu 100 Einträge pro Zertifikat. Eine solche Liste könnte so aussehen:

Wie man sieht muss es sich bei den Alternativdomains nicht zwingend um Subdomains der Hauptdomain handeln. Jede andere Domain ist genauso erlaubt und wird von Let’s Encrypt bei der Ausstellung oder Erneuerung des Zertifikats ebenfalls genau geprüft.

Vorbereitungen vor dem ersten Zertifikat

Das Script prüft selbstständig, ob nötige Verzeichnisse wie /etc/letsencrypt oder /etc/ssl/letsencrypt vorhanden sind, erstellt sie bei Bedarf und setzt die Berechtigungen richtig. Im ersten Verzeichnis wird der Account-Schlüssel abgespeichert, im zweiten Verzeichnis landen für alle in der Domains-Datei definierten Domains die privaten und signierten öffentlichen Schlüssel (das Zertifikat also).

Das im Script über die Variable CHALLENGEDIR definierte Verzeichnis für die Challenge-Response-Token muss allerdings noch angelegt werden. In diesem Beispiel also /var/www/letsencrypt.

Dieses Verzeichnis muss jedem Virtual Host, der ein Zertifikat erhalten soll unter einem bestimmten Pfad zugänglich gemacht werden. Zudem muss zu jeder Domain in der Domains-Datei ein Virtual Host gehören, der auf diese Domain reagiert, denn die LE-Server prüfen alle Domains, ob sie darüber die erwarteten Token-Dateien finden können.

Für den Webserver nginx habe ich mir der Einfachheit wegen eine generelle Konfigurationsdatei unter /etc/nginx/letsencrypt-auth.conf abgelegt mit folgendem Inhalt:

Diese Datei wird nun per include-Statement in alle Virtual Hosts-Konfigurationen eingebaut, so dass die Authentifizierungsanfragen der LE-Server auf der Suche nach den Tokendateien fündig werden. Abgefragt werden die Token-Dateien per HTTP, also muss die Konfiguration für den Server der auf Port 80 hört angepasst werden:

Versuchen die LE-Server nun die Challenge-Response-Token aufzurufen, die letskencrypt bei der Beantragung oder Verlängerung eines Zertifikats im Challenge-Response-Verzeichnis ablegt, leitet nginx diese Anfrage per 301-Redirect passend um und ermöglicht den Abruf. Aber halt, da fehlt natürlich noch ein Virtual Host in nginx, auf den umgeleitet wird. Im Beispiel oben leite ich alle „acme-Anfragen“ an den Host leauth.phasenraumorb.it um. Also braucht es noch einen nginx-server, der auf diesen Hostnamen hört und die Anfragen abarbeiten kann. Die Konfiguration könnte wie folgt aussehen:

Nach den Konfigurationsänderungen sollte man diese dem laufenden nginx-Prozess bekannt machen. Dazu reicht ein „reload“ über das Init-Script:

Das erste Zertifikat beantragen

Jetzt kann man das Script das erste Mal starten, mit der ersten Domain (und Subdomains) in der domains.txt, für die man ein Zertifikat beantragen möchte. Wenn alles klappt sollte man im Anschluss fünf Dateien vorfinden: den Private Key und die Zertifikatsdateien, fertig unterschrieben von Let’s Encrypt:

Das Zertifikat bzw. die komplette Zertifikatskette und der private Schlüssel müssen jetzt dem Webserver bekannt gemacht werden. Dazu muss erneut der VirtualHost angepasst werden, diesmal der für SSL verantwortliche Teil:

Jetzt nginx einmal die Konfiguration neu laden lassen (reload) und die Webseite sollte mit dem neuen Zertifikat gesichert sein.Let's Encrypt Zertifikat

Automatische Verlängerung per cron

Ruft man das Script erneut auf, dann wird das Vorhandensein eines Zertifikats erkannt und es wird, falls nötig, die Verlängerung des Zertifikats beantragt und die nginx-Konfiguration im Anschluss neu geladen. Genau das Verhalten, was man für den Aufruf per cron möchte. Ein passender Eintrag in der cron-Tabelle könnte so aussehen und ruft das Script einmal pro Woche auf:

OpenSMTPD

Hat man erstmal den Webserver wie oben beschrieben soweit am Laufen, dass das Beantragen und Verlängern von Zertifikaten klappt, dann kann man diese Zertifikate auch als TLS-Zertifikate für OpenSMTP nutzen. Hier ein Auszug aus der /etc/mail/smtpd.conf:

Verbindet sich ein SMTP-Client oder ein anderer SMTP-Server und übermittelt per SNI den Hostnamen, dann kann OpenSMTPD mit dem richtigen Zertifikat antworten. In diesem Beispiel würde das für die FQDNs mail.phasenraumorb.it, mta.example.org und mail.mydomain.com funktionieren. Damit die Zertifikate bei Let’s Encrypt beantragt werden können muss es aber, wie oben beschrieben, in nginx auch passende Virtual Hosts geben, die für diese Hostnamen verantwortlich sind und die ACME-Anfragen beantworten können.

Dovecot

Damit auch IMAP-Clients in den Genuss eines „vertrauenswürdigen“ Zertifikats kommen lässt sich auch in der Dovecot-Konfiguration ein oder mehrere Zertifikate hinterlegen. Auch Dovecot kann mit SNI umgehen, so dass mehrere Hostnamen möglich sind. Der passende Abschnitt der Konfiguration findet sich in der Datei /etc/dovecot/conf.d/10-ssl.conf und sieht beispielsweise so aus:

Zum Schluss

Diese Anleitung soll einen kleinen Hinweis geben, wie man sich ein Let’s Encrypt-Setup bauen kann, damit erstens die Beantragung der Zertifikate klappt und weiter auch die Verlängerung automatisch abläuft. Je nach vorhandener Konfiguration oder eingesetzter Software muss man hier und da sicher etwas Transfer leisten, aber der Text sollte einen groben Eindruck vermitteln, wie das Zusammenspiel zwischen letskencrypt/acme-client und dem Webserver etc. funktioniert. Wie man die Zertifikate dann am Ende einsetzt bleibt einem selbst überlassen. Die Beispiele OpenSMTPD und Dovecot zeigen, dass es nicht sonderlich schwer ist. So lassen sich auch andere Server mit den Let’s Encrypt-Zertifikaten absichern. Viel Spaß beim Umsetzen und Let’s Encrypt!