ACME-Client in Shell Script

Das Protokoll, welches Let’s Encrypt zur Anforderung und Übertragung der Zertifikate nutzt, hört auf den Namen ACME. Das Kürzel steht für Automatic Certificate Management Environment. Neben dem offiziellen Client Certbot existieren viele weitere Clients. Einer der Clients, welcher hervorsticht, ist acme.sh. Dieser Client wurde komplett in Shell Script für die Bash geschrieben.

Auf letsencrypt.org ist eine Liste der Clients zu finden

Neben dem ursprünglichen ACME v1-Protokoll unterstützt der Client ebenfalls ACME v2 und kann somit für den produktiven Einsatz genutzt werden. Der Client kann über GitHub bezogen werden. Lizenziert ist er unter der GPL in Version 3 und damit freie Software.

Reverse Proxy für Gitea konfigurieren

Ein Reverse Proxy liefert eine Ressource, welche er von einem oder mehreren Servern holt, an einen Client aus. Bei Gitea kann es durchaus sinnvoll sein, dieses hinter einem Reverse Proxy zu betreiben. Standardmäßig läuft der Dienst auf dem Port 3000. Möchte der Nutzer ihn über die normalen Ports für HTTP (80) bzw. HTTPS (443) erreichbar machen, könnte das Ganze durchaus über die Konfiguration von Gitea in Verbindung mit der systemd-Unit geschehen.

Allerdings ist ein weiterer Vorteil bei einem Reverse Proxy, das die angefragte Infrastruktur aus der Sicht der Clients versteckt wird. Mittels Nginx kann es solcher Reverse Proxy realisiert werden. Dazu muss die Nginx-Konfiguration für die Domain angepasst werden:

nano /etc/nginx/sites-available/example

In diesem Fall befasst sich die Konfiguration mit der verschlüsselten Kommunikation per HTTPS und der Weiterleitung von unverschlüsselten Verbindung in Richtung der verschlüsselten Verbindung.

server {
  listen 80;
  listen [::]:80;

  server_name example.org;

  return 301 https://$host$request_uri$is_args$args;
}

server {
  listen 443;
  listen [::]:443 default_server;

  ssl on;
  ssl_certificate        /etc/letsencrypt/live/example.org/fullchain.pem;
  ssl_certificate_key    /etc/letsencrypt/live/example.org/privkey.pem;

    server_name org;

    location / {
        proxy_pass http://localhost:3000;
    }
}

Anhand der Konfiguration wird ersichtlich das Gitea auf dem Server unverschlüsselt betrieben werden kann, da die eigentliche Verschlüsselung über HTTPS vom Reverse Proxy, in diesem Fall Nginx, übernommen wird. Nachdem die Konfiguration gespeichert wurde, muss Nginx neugestartet werden:

service nginx restart

Anschließend muss die Gitea-Konfiguration nochmals angepasst werden:

nano /home/git/gitea/custom/conf/app.ini

Dort muss die ROOT_URL nun so definiert werden, wie der Client sie nun sieht. Die ROOT_URL kann von:

ROOT_URL         = https://example.org:3000/

zu:

ROOT_URL         = http://example.org/

geändert werden. Die Werte PROTOCOL, CERT_FILE und KEY_FILE können entfernt werden, da die Verschlüsslung nun von Nginx übernommen wird. Nach der Änderung der Konfiguration muss Gitea ebenfalls neugestartet werden:

service gitea restart

Nachdem die Konfiguration durch geführt wurde, ist Gitea unter zwei URLs erreichbar:

http://example.org:3000/
https://example.org/

Intern läuft Gitea auf dem Port 3000. Damit dieser nicht von außen erreichbar ist, sollte eine entsprechende Firewall-Regel konfiguriert werden.

Let’s Encrypt Zertifikate in nicht privilegierten Prozessen nutzen

Wenn man Let’s Encrypt Zertifikate erzeugt, so landen diese im Ordner /etc/letsencrypt/. Die Rechte sind dabei so gewählt das nicht privilegierte Prozesse auf diese Zertifikate nicht zugreifen können. Läuft nun z.B. ein Server mit solchen Rechten, so kann er das Zertifikat nicht ohne weiteres nutzen. Um diesem Umstand zu beseitigen sollte eine neue Nutzergruppe angelegt werden:

groupadd tls-certificates

Dieser Gruppe wird nun der Nutzer hinzugefügt, welcher den Serverdienst betreibt:

usermod -a -G tls-certificates git

Damit wird der Nutzer git der Gruppe tls-certificates hinzugefügt. Nun müssen nach der Zertifikatsgenerierung die Berechtigungen angepasst werden:

#!/bin/sh
service gogs stop
letsencrypt renew --agree-tos
chgrp -R tls-certificates /etc/letsencrypt
chmod -R g=rX /etc/letsencrypt
service gogs start

In diesem Skript wird im ersten Schritt der Service gestoppt. Anschließend werden neue Zertifikate erzeugt und die Berechtigungen angepasst. Damit kann die Gruppe tls-certificates auf die Zertifikate zugreifen. Danach wird der Service wieder gestartet, was nun dank Zugriff auf die Zertifikate ohne Probleme funktioniert.

Let’s Encrypt unter Ubuntu und Nginx einrichten

Vor einigen Tagen habe ich damit begonnen alle von mir betriebenen Webseiten auf TLS umzustellen. Dabei nutzte ich Zertifikate der Zertifizierungsstelle Let’s Encrypt. Let’s Encrypt ging dabei im letzten Jahr in Betrieb und liefert kostenlose Zertifikate für TLS. Die CA wird dabei unter anderem von der EFF und Mozilla unterstützt. Im Gegensatz zu anderen Lösungen ist der Prozess bei Let’s Encrypt hochgradig automatisiert, so das die Einrichtung schnell von statten geht.

Der offizielle Client hört dabei auf den Namen Certbot (früher Let’s Encrypt Client) und implementiert das ACME-Protokoll (Automated Certificate Management Environment) über welches der Prozess abgewickelt wird. Neben dem offiziellen Client gibt es viele weitere Clients welche das ACME-Protokoll implementieren. Um Let’s Encrypt unter Ubuntu zu nutzen, muss im ersten Schritt der Client installiert werden:

apt-get install letsencrypt

Nachdem der Client installiert wurde, kann mit der Erzeugung der Zertifikate begonnen werden. Im Gegensatz zu Apache wird unter Nginx die automatische Einrichtung nicht unterstützt. Aus diesem Grund werden nur die Zertifikate mit dem Client erzeugt. Dies geschieht mit dem Befehl:

letsencrypt certonly

Damit wird der interaktive Modus gestartet in welchem das Zertifikat erzeugt werden kann. Zuerst wird nach einer Mailadresse gefragt, mit welcher das Recovery in Notfällen möglich ist. Anschließend müssen die allgemeinen Geschäftsbedingungen akzeptiert werden. Nun wird nach den Domains gefragt, für welche ein Zertifikat erstellt werden soll. Hier kann man mehrere Domains per Komma bzw. Leerzeichen getrennt angeben – allerdings scheint dies in der aktuellen Version nicht zu funktionieren. Stattdessen wird nur für die erste angegebene Domain ein Zertifikat erzeugt. Standardmäßig benötigt Certbot während der Generierung der Zertifikate Zugriff auf den Port 80. Hintergrund für dieses Verhalten ist das der Client kurz einen Webserver aufsetzt um die Kommunikation mit der CA durchzuführen.

Abgelegt werden die erzeugten Zertifikate dabei im Ordner /etc/letsencrypt/. In diesem Ordner liegen neben den Stammzertifikaten auch die eigentlichen Zertifikate für die einzelnen Domains. Nun kann dieses Zertifikat in Nginx eingebunden werden. Dazu muss die Konfigurationsdatei der jeweiligen Seite (z.B. /etc/nginx/sites-available/example) geöffnet werden. Im ersten Schritt wurde dazu in der Konfiguration eine Weiterleitung eingerichtet:

server {
        listen 80;
        listen [::]:80;

        server_name .example.org;

        return 301 https://$host$request_uri$is_args$args;
}

Diese Weiterleitung sorgt dafür das eine Verbindung über unverschlüsseltes HTTP automatisch auf die verschlüsselte Variante umgeleitet wird. Weiter geht es mit der Konfiguration der verschlüsselten Verbindung:

server {
        listen   443 ssl;
        listen [::]:443 ssl;

        ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;

        ...

Jedes erzeugte Zertifikat von Let’s Encrypt ist 90 Tage lang gültig, so das ein automatischer Prozess eingerichtet werden sollte um die Zertifikate automatisch zu erneuern. Mit dem Befehl:

letsencrypt renew --agree-tos

kann dabei der Erneuerungsprozess angestoßen werden. Möchte man das ganze ohne Risiko testen, so sollte der Parameter –dry-run angefügt werden.

letsencrypt renew --agree-tos --dry-run

Bei der Erneuerung der Zertifikate kann es nun vorkommen das man den Nginx-Server vorher beenden muss. Das ganze kann man in ein Skript gießen:

#!/bin/sh
service nginx stop
letsencrypt renew --agree-tos
service nginx start

Dieses Skript kann man nun zum Beispiel einmal in der Nacht per Cronjob ausführen. Der Client überprüft dabei ob eine Erneuerung notwendig ist und führt diese dann automatisch durch.