letsencrypt ist ein kostenloser Service für SSL-Zertifikate. Die Zertifikate sind 90 Tage gültig und müssen daher regelmäßig aktualisiert werden, z.B. durch einen systemd timer/service.
letsencrypt SSL Zertifikat automatisch erneuern
Es wird ein systemd Service und systemd Timer benötigt. Der Timer soll zweimal täglich den Service starten und prüfen ob das Zertifikat erneuert werden muss. Mit dem Programm certbot kann man bei letsencrypt Zertifikate anlegen, verändern oder erneuern. Damit certbot gegenüber letsencrypt nachweisen kann das es Zugriff auf die Domain hat, für die ein Zertifikat ausgestellt werden soll, braucht man entweder Zugriff auf die Ports oder Zugriff auf das Verzeichnis /.well-known auf dem Webserver. Um Zugriff auf die Webserver Ports (80, 443) zu bekommen müsste man diesen anhalten, was etwas unpraktisch ist. Alternativ kann man auch das Webroot Verzeichnis für die jeweilige Domain übergeben.
Update mittels anhalten des Webservers
Mittels pre-hook wird der apache (und in meinem Fall auch der openvpn) Dienst gestoppt. Damit certbot Zugriff auf die Ports zum authentifizieren bekommt. Nach dem Update der Zertifikate werden die Dienste durch den post-hook wieder gestartet. Der pre- und post-hook werden nur ausgeführt, wenn das Zertifikat erneuert werden muss!
#!/bin/bash
certbot renew --pre-hook "systemctl stop httpd openvpn@server.service" --post-hook "systemctl start httpd openvpn@server.service"
Update bei laufendem Webserver
Leider unterstützt certbot renew aktuell nicht die Angabe mehrerer webroots (In apache2 entspricht das den vhosts), daher muss man certbot certonly nutzen.
#!/bin/bash
certbot --non-interactive certonly --keep-until-expiring --webroot -w /srv/http/root/ -d example.de,ftp.example.de -w /srv/http/blog/ -d blog.example.de
Systemd Service und Timer anlegen
[Unit]
Description=LetsEncrypt Update Service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/letsencrypt_renew
[Unit]
Description=LetsEncrypt Update Timer
[Timer]
OnCalendar=04,16:22
Persistent=true
[Install]
WantedBy=basic.target
Nun noch den systemd Timer beim booten automatisch aktivieren und starten.
sudo systemctl enable letsencrypt.timer
sudo systemctl start letsencrypt.timer
Zertifikat bei letsencrypt registrieren
sudo certbot certonly -a standalone --pre-hook "systemctl stop httpd openvpn@server.service" --post-hook "systemctl start httpd openvpn@server.service" -d example.de,ftp.example.de
oder über einen laufenden Webserver
certbot certonly --webroot -w /srv/http/root/ -d example.de,ftp.example.de
Weitere SubDomain zum Zertifikat hinzufügen
Ist der gleiche Befehl wie zum registrieren, nur um weitere SubDomains ergänzt.
sudo certbot certonly -a standalone --pre-hook "systemctl stop httpd openvpn@server.service" --post-hook "systemctl start httpd openvpn@server.service" -d example.de,ftp.example.de,blog.example.de
oder über einen laufenden Webserver
certbot certonly --webroot -w /srv/http/root/ -d example.de,ftp.example.de -w /srv/http/blog/ -d blog.example.de
Das letsencrypt Zertifikat für den Quassel-IRC Server nutzen
Die eben erstellten Zertfikate liegen unter /etc/letsencrypt/live/example.de/ und können auch für die Verschlüsselung der Kommunikation von Quassel Server und Quassel Client genutzt werden. Quassel braucht allerdings ein PEM Bundle aus dem privkey.pem und fullchain.pem und das in /var/lib/quassel/ abgelegt werden muss. Zum Beispiel so:
cat /etc/letsencrypt/live/example.de/privkey.pem /etc/letsencrypt/live/example.de/fullchain.pem > /var/lib/quassel/quasselCert.pem
Das ganze kann man dann mittels post-hook so ausführen.
certbot --non-interactive certonly --keep-until-expiring --webroot -w /srv/http/root/ -d example.de --post-hook "cat /etc/letsencrypt/live/example.de/privkey.pem /etc/letsencrypt/live/example.de/fullchain.pem > /var/lib/quassel/quasselCert.pem && systemctl restart quassel"
Django und certbot webroot über apache2
Damit certbot Zugriff auf das /.well-known Verzeichnis in einer Django Anwendung hinter dem apache2 bekommt hab ich das /.well-known Verzeichnis nochmal explizit in die apache2 Konfiguration geschrieben. Mit alias wird das Verzeichnis nicht durch django geleitet, sondern direkt von apache2 verarbeitet.
WSGIPythonPath /srv/http/django
<VirtualHost *:443>
ServerAdmin webmaster@example.de
DocumentRoot "/srv/http/django/django"
ServerName django.example.de
ErrorLog "/var/log/httpd/django.example.de_error_ssl.log"
CustomLog "/var/log/httpd/django.example.de_access_ssl.log" common
SSLEngine On
SSLProtocol All -SSLv2 -SSLv3
SSLCertificateFile /etc/letsencrypt/live/django.example.de/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/django.example.de/privkey.pem
WSGIScriptAlias / /srv/http/django/django/wsgi.py
# Authentication directory for letsencrypt certificate
Alias /.well-known /srv/http/django/.well-known
<Directory "/srv/http/django/.well-known">
Options -Indexes
Require all granted
</Directory>
Alias /static/ /srv/http/django/static/
<Location "/static/">
Options -Indexes
Require all granted
</Location>
<Directory /srv/http/django/django>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
</VirtualHost>