Ich nutze bereits seit langem die kostenlosen TLS Zertifikate von StartSSL. Diese sind für ein Jahr gültig und müssen leider noch immer manuell geprüft und akzeptiert werden. Mal eben ein Cert ins leben rufen ist also nicht machbar, wenn es valide sein soll.

Vor einigen Tagen ging dann endlich Letsencrypt online. Ein Dienst, der vollkommen automatisiert valide Zertifikate generieren kann. Ich testete direkt am ersten Tag das Tool und wurde erst mal von den Abhängigkeiten erschlagen. Da ich sowieso wenige Tage später den Server wechseln wollte, habe ich es dennoch mal installiert und mir angesehen.

Das Handling ist leider alles andere als angenehm, speziell wenn man 1. kein Apache verwendet und 2. sein eigenes Setup gerne so beibehalten möchte. Das Tool war leider nicht in der Lage automatisiert über das Webroot das Zertifikat zu validieren. Einen eigenen Webserver zu starten war keine Alternative, da ich dafür immer mein Nginx hätte stoppen müssen, da Port 80 zwingend vorausgesetzt wird. Die letzte Alternative über entsprechend Records im DNS ist leider auch nicht machbar, da nicht all mein Anbieter eine API anbieten. Davon ab heißt es auch hier wieder: warten, bis die DNS Records abgeglichen sind.

Ich habe mich letztendlich für ein minimalistisches Python-Script entschieden: acme_tiny.py.

Dieses Script macht alles in etwa so, wie ich es gern möchte. Wenn auch nicht zu 100% automatisiert auf Grund meiner Konfiguration. Hier mal alle wichtigen Configs für einen Host.

redirect.conf:

location /.well-known/acme-challenge {
    root /var/www/letsencrypt;
    default_type "text/plain";
    try_files $uri =404;
}

location / {
return 301 https://$host$request_uri;
}

ssl.conf:

ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

sites/host:

server {
    listen 80;
    server_name example.com;
    include /etc/nginx/redirect.conf;
}
server {
    listen 443 ssl http2;
    server_name example.com;
    root /srv/example.com;
    index index.php index.html;
    gzip on;

    ssl_certificate /etc/nginx/ssl/example.com/example.com.bundle.pem;
    ssl_certificate_key /etc/nginx/ssl/example.com/example.com.key;
    include /etc/nginx/ssl.conf;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    include /etc/nginx/php.conf;
}

Dies ist eine Config, so wie sie am Ende steht. Die redirect.conf zum einen für die Weiterleitung von HTTP zu HTTPS zuständig und bietet gleichzeitig noch den .well-known Pfad das spätere Renewal an. Für die Registrierung muss die Config leider umgebaut werden, da TLS noch nicht funktionieren kann ohne die Cert-Chain und den Key. Grundsätzlich kann der gesamte Server-Block für TLS somit auskommentiert werden, damit es funktioniert.

Meine Scripte sind nicht sonderlich aufwändig. Sie prüfen, ob der Ordner für das Zertifikat bereits existiert. Fehlt dieser, wird das Zertifikat generiert. Die Namen werden den existierenden Hosts entnommen.

generate.sh:

#!/usr/bin/bash

SITES=/etc/nginx/sites
SSLBASE=/etc/nginx/ssl
ACMEDIR=/var/www/letsencrypt/.well-known/acme-challenge

cd $SITES
for i in *; do
    if [[ ! -d $SSLBASE/$i ]]; then
        cd $SSLBASE
        echo "Generating $i"
        mkdir $i
        openssl genrsa 4096 > $i/$i.key
        openssl req -new -sha256 -key $i/$i.key -subj "/CN=$i" > $i/$i.csr
        python $SSLBASE/acme_tiny.py --account-key $SSLBASE/account.key --csr $SSLBASE/$i/$i.csr --acme-dir $ACMEDIR > $SSLBASE/$i/$i.pem
        cat $SSLBASE/$i/$i.pem $SSLBASE/intermediate.pem > $SSLBASE/$i/$i.bundle.pem
    fi
done

renewal.sh:

#!/usr/bin/bash

SSLBASE=/etc/nginx/ssl
ACMEDIR=/var/www/letsencrypt/.well-known/acme-challenge

cd $SSLBASE
for i in *; do
    if [[ -d $i ]]; then
        echo "Renewing $i"
        python $SSLBASE/acme_tiny.py --account-key $SSLBASE/account.key --csr $SSLBASE/$i/$i.csr --acme-dir $ACMEDIR > $SSLBASE/$i/$i.pem
        cat $SSLBASE/$i/$i.pem $SSLBASE/intermediate.pem > $SSLBASE/$i/$i.bundle.pem
    fi
done

systemctl reload nginx

Nachteil an meiner generate.sh ist, dass es keine Fehler abfängt. Somit hat es mit der Verifizierung beim Xten Durchlauf leider nicht funktioniert und es wurden leere Dateien erstellt. Grundsätzlich kein Problem, wenn da nicht ein Limit wäre, welches maximal 5 Zertifikate pro Domain pro Woche erlaubt…