DecaTec

Home-Server | Linux | Nextcloud | Raspberry Pi | Programmierung | Fotografie

Let’s Encrypt Zertifikate mit acme.sh und nginx

Let's Encrypt Logo

Wer eine eigene Website oder auch eine Nextcloud-Instanz betreibt, der sollte auch großen Wert auf Sicherheit legen. In der heutigen Zeit gehört dabei HTTPS zum Sicherheits-Standard, wenn es um die verschlüsselte Übertragung von Daten im Internet geht.

Um die eigene Seite mittels HTTPS abzusichern, ist zunächst einmal ein SSL-Zertifikat notwendig. Früher musste man sich ein solches Zertifikat von einer Zertifizierungsstelle für viel Geld kaufen. Im Jahr 2015 trat jedoch Let’s Encrypt auf den Plan – eine Zertifizierungsstelle, die kostenlos Zertifikate für TLS anbietet, um damit verschlüsselte Verbindungen im Web zum Standard zu machen. Gerade im privaten Bereich hat sich Let’s Encrypt zu einem de-facto Standard entwickelt.

Seitdem kann sich jedermann selbst Zertifikate für HTTPS ausstellen lassen. Dazu wird lediglich ein Let’s Encrypt Client benötigt, der die eigentliche Generierung der Zertifikate übernimmt. In meinen Tutorials habe ich bisher immer Certbot als Client für Let’s Encrypt empfohlen, da dieses Programm in den Paketquellen fast aller Distributionen enthalten ist. Dennoch könnte sich genau dieser Umstand als Nachteil erweisen. So schaltet Let’s Encrypt aus Sicherheitsgründen das Validierungsverfahren „TLS-SNI-01“ bald ab. Nutzer von Debian laufen bald Gefahr, dass die Zertifikate nicht mehr erneuert werden können, da die Version von Certbot in Debian Stable zu alt ist und keine anderen Validierungsmethoden unterstützt (siehe hier).

Ein alternativer Let’s Encrypt Client ist acme.sh. Wie der Name bereits vermuten lässt, handelt es sich hierbei um ein reines Shell-Skript, welches die Generierung der Zertifikate übernimmt. Man ist damit nicht mehr von Programmen in den Paketquellen der Linux-Distributionen abhängig. Darüber hinaus ist acme.sh ein sehr fortschrittlicher Client, der bisher alle Features von Let’s Encrypt unterstützt. Grund genug, die diesen Let’s Encrypt Client einmal genauer anzusehen.

Der Artikel basiert dabei auf Ubuntu Server 18.04 LTS, alle gezeigten Schritte sollten sich aber auch auf anderen Distributionen umsetzen lassen. Als Webserver kommt nginx zum Einsatz.

Update-Historie (letztes Update 18.06.2021)
  • 18.06.2021:
    • Konfiguration acme.sh, so dass immer Zertifikate über Let’s Encrypt bezogen werden.
  • 03.02.2020:
    • Cipher Suite DHE-RSA-AES256-GCM-SHA384 entfernt.
  • 17.08.2019:
    • acme.sh sollte nicht mit Root-Rechten ausgeführt werden, daher wird für die Ausführung von acme.sh ein spezieller User angelegt.
  • 24.07.2019:
    • Anpassungen für TLSv1.3 (ssl_ciphers und ssl_ecdh_curve).
  • 15.06.2019:
    • ECDHE-RSA-AES256-SHA384 aus den ssl_ciphers entfernt (wird beim Qualys SSL Labs Test als „weak“ eingestuft).
  • 18.05.2019:
    • Hinweis hinzugefügt, dass bei der Generierung der Zertifikate am besten Copy&Paste und „Suchen & Ersetzen“ genutzt wird.
  • 18.02.2019:
    • Hinweis für Meldung „-bash: /home/user/.acme.sh/acme.sh.env: Permission denied“ hinzugefügt.
  • 13.02.2019:
    • Hinweis für die Berechtigungen des Verzeichnisses /var/www/letsencrypt hinzugefügt.
  • 11.02.2019:
    • Anleitung zum Upgrade von acme.sh hinzugefügt.
    • Hinweis auf Überschreiben der Crontab durch Installation von acme.sh hinzugefügt/Sichern der bestehenden Crontab.
  • 09.02.2019:
    • Hinweis auf neuen Artikel zum Umstieg von Certbot auf acme.sh hinzugefügt.

Installation von acme.sh

Es handelt sich bei acme.sh nur um ein Skript, jedoch kann es in gewisser Art installiert werden. Die Installation beinhaltet hauptsächlich die Einrichtung eines Cronjobs zur automatischen Erneuerung ausgestellter Zertifikate.

acme.sh sollte nicht mit Root-Rechten ausgeführt werden, daher legen wir einen speziellen User an und fügen diesen der Gruppe www-data hinzu:

adduser letsencrypt 
usermod -a -G www-data letsencrypt

Der User braucht nun die Berechtigungen, um nginx später ohne Passwort neu laden zu können:

visudo

Am Ende wird folgende Zeile hinzugefügt:

letsencrypt ALL=NOPASSWD: /bin/systemctl reload nginx.service

Für die Installation von acme.sh wechseln wird nun den User:

su - letsencrypt

Hinweis: Wenn ein bereits bestehender User für die Generierung der Zertifikate genommen wird, sollte nun kontrolliert werden, ob bereits Conjobs für diesen User angelegt wurden, da acme.sh bei der Installation sämtliche bereits bestehenden Conjobs überschreibt.

Falls bereits Cronjobs eingerichtet sind, dann solltet ihr euch daher vorher die aktuelle Crontab sichern:

crontab -l > crontab.bak

Nach der Installation von acme.sh können mittels dieser Datei die „alten“ Cronjobs wieder mit contab -e hinzugefügt werden.

Diese Installation besteht eigentlich nur in der Ausführung des Skripts mit bestimmten Parametern und gestaltet sich recht einfach. Dazu gibt es eine spezielle Seite, die das Skript zur Installation beinhaltet: https://get.acme.sh.

curl https://get.acme.sh | sh

Hinweis: Es ist immer gefährlich, eine unbekannte Quelle direkt in die Shell zu pipen, da man nie so genau weiß, was diese Quelle (in diesem Fall das Skript genau ausführt). Man sollte sich die Quelle (in diesem Fall https://get.acme.sh) vor der Ausführung des Befehle genau ansehen und überprüfen, ob hier nichts verdächtiges passiert. Für wen das Pipen auf die Shell nicht in Frage kommt, der findet noch ein paar alternative Installationsmöglichkeiten auf der Wiki-Seite des GitHub-Projekts.

Die "Installation" von acme.sh

Die „Installation“ von acme.sh

Nach der Installation von acme.sh einmal mit dem entsprechenden User ab- und wieder anmelden:

exit
su - letsencrypt

Nun wird acme.sh noch gleich so konfiguriert, dass standardmäßig Zertifikate von Let’s Encrypt bezogen werden (ab dem 01.08.2021 nutzt acme.sh als Standard Zertifikate von ZeroSSL – siehe hier).
Dazu wird vor der Ausstellung des ersten Zertifikats folgender Befehl benötigt:

acme.sh --set-default-ca --server letsencrypt

Nach der Installation muss das Terminal einmal geschlossen und wieder geöffnet werden. Alternativ kann man den Rechner auch einfach neu starten.

Damit ist die Installation bereits abgeschlossen, so dass wir wieder auf den normalen User wechseln können:

exit

Als nächstes wird das System auf die Verwendung von SSL-Zertifikaten vorbereitet.

Vorbereitung des Systems

Zunächst richten wir die benötigten Verzeichnis ein, die später für die Zertifiakt-Generierung benötigt werden. Ebenfalls passen wir gleich die Berechtigungen entsprechend an:

mkdir -p /var/www/letsencrypt/.well-known/acme-challenge
chown -R www-data:www-data /var/www/letsencrypt
chmod -R 775 /var/www/letsencrypt
mkdir -p /etc/letsencrypt/meinedomain.de
chown -R www-data:www-data /etc/letsencrypt
chmod -R 775 /etc/letsencrypt

Ich nutze hier immer einen Unterordner, der mit der Domain übereinstimmt, für welche die Zertifikate ausgestellt werden sollen (in diesem Beispiel einfach meinedomain.de).

Als nächstes muss der Webserver in der Lage sein, über HTTP (Port 80) im Unterverzeichnis /.wellknown/acmechallenge für Let’s Encrypt erreichbar zu sein. Im virtuellen Host für die entsprechende Domain könnte dies dann folgendermaßen aussehen:

server {
	listen 80 default_server;
    listen [::]:80 default_server;
	server_name meinedomain.de 192.168.178.60;
 
	root /var/www;
	
	location ^~ /.well-known/acme-challenge {
		default_type text/plain;
		root /var/www/letsencrypt;
	}		
}

Zertifikate mit acme.sh erzeugen

Nach den Vorbereitungen sind wir nun soweit, dass wir Zertifikate über acme.sh ausstellen lassen können.

Ich gehe im folgenden davon aus, dass auf dem System bereits nginx als Webserver installiert und eingerichtet wurde. Für die Generierung der Zertifikate wird daher der sog. Webroot-Modus genutzt. acme.sh unterstützt allerdings auch andere Modi (z.B. einen Standalone-Modus, wenn kein Webserver auf dem System läuft). Es gibt auch einen speziellen Modus für nginx, allerdings werden hier die virtuellen Hosts vom Webserver bei der Generierung/Erneuerung der Zertifikate kurzzeitig verändert/ausgetauscht. Da ich meine vHosts lieber unangetastet lassen möchte, nutze ich daher immer nur den Webroot-Modus.

Zunächst wechseln wir auf den speziell angelegten User:

su - letsencrypt

Ein Zertifikat wird durch einen einzigen Befehl erzeugt.
Tipp: Am besten übernimmt man den Befehl mittels Copy&Paste in einen beliebigen Text-Editor, mit dem man dann die Domain mit „Suchen und Ersetzen“ einfach durch die tatsächlich verwendete Domain einfügt. Auf diese Weise kann man Tippfehler vermeiden.

acme.sh --issue -d meinedomain.de --server letsencrypt --keylength 4096 -w /var/www/letsencrypt --key-file /etc/letsencrypt/meinedomain.de/key.pem --ca-file /etc/letsencrypt/meinedomain.de/ca.pem --cert-file /etc/letsencrypt/meinedomain.de/cert.pem --fullchain-file /etc/letsencrypt/meinedomain.de/fullchain.pem --reloadcmd "sudo /bin/systemctl reload nginx.service"

Mit den Parametern werden die Details zu Generierung der Zertifikate angegeben:

  • -d meinedomain.de

    Gibt die Domain an, für die das Zertifikat erzeugt werden soll.

  • --server letsencrypt

    Gibt an, dass die Zertifikate über Let’s Encrypt bezogen werden sollen. Diese Option ist an dieser Stelle optional, wenn das Standard-Verhalten von acme.sh bereits vorher angepasst wurde (s.o.).

  • --keylength 4096

    Gibt die Schlüssellänge des RSA-Zertifikats an. Standardmäßig werden hier 2048 Bit verwendet. Das ist in der Regel ausreichend, jedoch sollte man für erhöhte Sicherheit auf eine Schlüssellänge von 4096 Bit setzen.

  • -w /var/www/letsencrypt

    Gibt das Verzeichnis an, in dem die Challenge-Dateien gespeichert werden. Dies muss das Verzeichnis sein, welches bei nginx als root Verzeichnis für /.well-known/acme-challenge angegeben wurde (im jeweiligen virtuellen Host).

  • acme.sh speichert die Zertifikat-Dateien im Verzeichnis ~/.acme.sh/meinedomain.de. Es wird davon abgeraten, diese Dateien direkt in den virtuellen Hosts des Webservers zu referenzieren. Aus diesem Grund wird mit den Parametern –key-file , –ca-file , –cert-file  und –fullchain-file  die Speicherorte der Dateien angegeben, wo die entsprechenden Dateien „installiert“ werden sollen. Nur diese Dateien werden dann später in den vHosts von nginx eingebunden.
  • --reloadcmd

    Gibt den Befehl an, der nach erfolgreicher Ausstellung/Erneuerung der Zertifikate ausgeführt werden soll. Damit der Webserver die neuen Zertifikate korrekt einbinden kann, wird nginx hier einfach kurz neu gestartet. Wenn hier mehrere Befehle ausgeführt werden sollen, sind diese durch ein Semikolon zu trennen.

Nach dem Generieren können alle von acme.sh ausgestellten Zertifikate auf dem System durch folgenden Befehl angezeigt werden:

acme.sh --list

Ebenfalls wird durch diesen Befehl die Gültigkeitsdauer der Zertifikate angezeigt (dazu später mehr).

Nun wechseln wir wieder auf den normalen User zurück:

exit

Diffie-Hellman-Parameter erzeugen

Die Zertifikate selbst sind der wichtigste Punkt, wenn es um die Verschlüsselung der Verbindung über HTTPS geht. Um die Sicherheit noch weiter zu erhöhen, sollten noch sog. Diffie-Hellman-Parameter genutzt werden. Ohne nun die technischen Details zu beleuchten, geht es um einen sicheren Schlüsselaustausch bei Verbindungsaufbau. Die Generierung des Schlüssels muss dabei nur einmal erfolgen.

Achtung: Die Erzeugung der Diffie-Hellman-Parameter dauert gerade auf schwächerer Hardware sehr lange. Hier muss man bis zur Fertigstellung u.U. mehrere Stunden warten. In diesem Fall kann auch eine Schlüssel mit „nur“ 2048 Bit berechnet werden (die Zahl des zweiten Befehls gibt hierbei die Länge des Schlüssels in Bit an). Auf stärkerer Hardware ist allerdings eine Schlüssellänge von 4096 Bit empfehlenswert.

mkdir -p /etc/nginx/dhparams 
openssl dhparam -out /etc/nginx/dhparams/dhparams.pem 4096

Einbinden der Zertifikate in nginx

Die Zertifikate können nun einfach mit nginx verwendet werden. Ich beschränke mich hier auf das reine Einbinden der Zertifikate und der entsprechenden SSL-Einstellungen.

Damit das Ganze übersichtlich bleibt, lagere ich die SSL-Settings immer in eine spezielle Datei aus, die dann später in die virtuellen Hosts eingebunden wird. Auf diese Weise sind alle Einstellungen bzgl. SSL nur an einem Ort zu finden.

Dazu legen wir zunächst eine neue Datei an:

mkdir -p /etc/nginx/snippets 
nano /etc/nginx/snippets/ssl.conf

Hier werden dann ausschließlich die Anweisungen für SSL aufgeführt:

# Certificates used
ssl_certificate /etc/letsencrypt/meinedomain.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/meinedomain.de/key.pem;

# This should be ca.pem (certificate with the additional intermediate certificate)
# See here: https://certbot.eff.org/docs/using.html
ssl_trusted_certificate /etc/letsencrypt/meinedomain.de/ca.pem;

# Diffie-Hellman parameter, recommended 4096 bits
ssl_dhparam /etc/nginx/dhparams/dhparams.pem;

# Not using TLSv1 will break:
#	Android <= 4.4.40
#	IE <= 10
#	IE mobile <=10
# Removing TLSv1.1 breaks nothing else!
# TLSv1.3 is not supported by most clients, but it should be enabled.
ssl_protocols TLSv1.2 TLSv1.3;

# Cipher suite from https://cipherli.st/
# Max. security, but lower compatibility 
ssl_ciphers 'TLS-CHACHA20-POLY1305-SHA256:TLS-AES-256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384';

# Cipher suite from https://wiki.mozilla.org/Security/Server_Side_TLS
#ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';

# (Modern) cipher suite from https://mozilla.github.io/server-side-tls/ssl-config-generator/
#ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';

# Use multiple curves.
ssl_ecdh_curve secp521r1:secp384r1;

# Server should determine the ciphers, not the client
ssl_prefer_server_ciphers on;

# OCSP Stapling
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;

resolver 192.168.178.1;

# SSL session handling
ssl_session_timeout 24h;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

Neben dem Einbinden der eigentlichen Zertifikate dienen alle weiteren Anweisung ebenso der Sicherheit/Verschlüsselung. Daher bitte auch die Kommentare in der Datei beachten.

Im jeweiligen virtuellen Host für HTTPS ist dann einfach diese ssl.conf einzubinden:

server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	server_name meinedomain.de 192.168.178.60;
	
	include /etc/nginx/snippets/ssl.conf;	
	
	#
	# Here you would add the statements for some web applications, e.g. Nextcloud.
	#
}

Wichtig: Die SSL-Anweisungen dürfen nicht doppelt im server-Block für HTTPS aufgeführt werden. Wenn hier bereits Optionen angegeben sind, die ebenfalls in der Datei ssl.conf aufgeführt sind, dann sind diese im server-Block für HTTPS zu entfernen.

Nach einem Neustart des Webservers sollten die Zertifikate korrekt eingebunden worden sein:

service nginx restart

Überprüfung der Zertifikate

Abschließend lohnt eine Überprüfung der Zertifikate. Hierzu ist der SSL Server Test von Qualys SSL Labs gut geeignet. Mit der gezeigten Konfiguration sollte ein Rating mit A+ und 100% in allen Kategorien erreicht werden:

Ergebnis des SSL-Tests

Ergebnis des SSL-Tests

Falls hier eine niedrigere Bewertung angezeigt werden sollte, liegt dies vermutlich an der SSL-Konfiguration des Webservers. In diesem Fall sollten die entsprechenden Anweisungen der Datei ssl.conf nochmals kontrolliert werden.

Erneuerung der Zertifikate

Die Zertifikate von Let’s Encrypt sind nur für die Dauer von 90 Tagen gültig (Begründung siehe hier) und müssen anschließend erneuert werden.

Bei der Installation von acme.sh wurde ein Cronjob angelegt, der regelmäßig prüft, ob Zertifikate erneuert werden müssen und diese Erneuerung dann ggf. automatisch ausführt. Im Grunde genommen muss man sich daher gar nicht weiter um die Zertifikate kümmern.

Dennoch sollte man nach dem initialen Ausstellen der Zertifikate und Ablauf der 90 Tage kontrollieren, ob die automatische Erneuerung auch wirklich durchgeführt wurde.

Die Gültigkeitsdauer der installierten Zertifikate kann wieder mit folgendem Befehl ermittelt werden:

su - letsencrypt 
acme.sh --list

Upgrade von acme.sh

Für acme.sh werden regelmäßig auch Updates veröffentlicht (siehe GitHub Release-Page). In diesem Fall kann das Skript mit nur einem Befehl auf den neusten Stand gebracht werden:

su - letsencrypt 
acme.sh --upgrade

Das Reizvolle daran ist nun, dass man diese Updates komplett ohne Abhängigkeiten von den Paketquellen der jeweiligen Distributionen einspielen kann.

Fazit

acme.sh macht als Let’s Encrypt Client einen sehr soliden Eindruck. Die Bedienung gestaltet sich recht einfach und da es sich um ein reines Shell Skript handelt, ist man unabhängig von Programmversionen in den Paketquellen einzelner Linux-Distributionen. Ebenfalls wird automatisch ein Cronjob für die Erneuerung der Zertifikate angelegt, so dass man sich nach der ersten Generierung der Zertifikate nicht mehr um deren Erneuerung kümmern muss. Aus genau diesen Gründen werde ich in Zukunft auch acme.sh als Let’s Encrypt Client empfehlen.

Abschließend noch ein Hinweis für Webserver-Admins, die noch einen anderen Let’s Encrypt Client verwenden und auf acme.sh umsteigen wollen: Der nächste Artikel hier im Blog wird den Umstieg zu acme.sh im Detail beleuchten…

Update: Der Artikel zum Umstieg von Certbot auf acme.sh ist online: Let’s Encrypt: Umstieg von Certbot auf acme.sh (nginx)

Weiterführende Artikel

Links

, , , , , , , , , , ,

Kommentare: 85

  • Danny sagt:

    Hallo Jan,

    wie würde so etwas aussehen, wenn man 2-3 Webseiten mit unterschiedlichen Domain am laufen hat auf einem Server?

    Gruß Danny

    • Jan sagt:

      Hi Danny,

      das ist ganz einfach: Für die weiteren Domains einfach weitere Zertifikate ausstellen lassen, pro Webseite dann ein vHost und hier die Zertifikate entsprechend einbinden.
      Man kann auch mehrere Domains in ein Zertifikat „zusammen fassen“ (dann einfach beim Aufruf von acme.sh den Parameter „-d“ mehrfach angeben), das mache ich aber persönlich nicht, da ich gerne für jede Domain ein Zertifikat habe, welches ich dann auch separat verwalten kann.

      Gruß,
      Jan

  • Frank Ewald sagt:

    Hallo!

    Vielen Dank für die ausführtliche Anleitung. Ich bin diese Punkt für Punkt durchgegangen, um Zertifikate für meine Nextcloud zu erhalten. Leider klappte es nicht.

    Ein Problem, das ich lösen konnte war, dass man zur Benutzung der acme.sh zuerst eine Emailadresse hinterlegen muss, bevor man ihren Befehl ausführen kann.

    Was ich aber nicht verstehe, ist folgendes:

    „Als nächstes muss der Webserver in der Lage sein, über HTTP (Port 80) im Unterverzeichnis /.well–known/acme–challenge für Let’s Encrypt erreichbar zu sein. Im virtuellen Host für die entsprechende Domain könnte dies dann folgendermaßen aussehen:

    […]“

    Mir ist nicht klar, was der virtuelle Host ist. Nach einiger Recherche, habe ich folgendes ausgeführt:

    mkdir /etc/nginx/sites-available/
    nano /etc/nginx/sites-available/meine.domain.conf

    und ihren server {…}-Block dort eingefügt.

    Dann stolperte ich noch über folgendes:

    „Im jeweiligen virtuellen Host für HTTPS ist dann einfach diese ssl.conf einzubinden:“

    In welche Datei gehört nun dieser server {…}-Block?

    Gruß

    Frank

    Mit freundlichem

    • Jan sagt:

      Hi Frank,

      bei acme.sh musste ich noch nie eine Mail-Adresse hinterlegen, das scheint dann wenn neu zu sein.
      Virtuelle Hosts sind die Dateien, die bestimmen, was der Webserver ausliefert. Diese lege ich immer in das Verzeichnis /etc/nginx/conf.d, du kannst aber auch das Verzeichnis /etc/nginx/sites-enabled nutzen. Das wird bei dir vermutlich der Fehler sein, wenn in „sites-available“ werden die vHosts nicht geladen, sondern quasi nur geparkt.

      Gruß,
      Jan

1 2

Schreibe einen Kommentar

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