Fake-Wildcard mit Lets Encrypt

Beitrag vom 25.03.2016

Lets Encrypt stellt ja bekannterweise keine Wildcard Zertifikate aus.

Mein "dies und das" Webserver benötigt allerdings eines. Für jedes Projekt was darauf läuft wird ein eigener Linux Benutzer angelegt, welcher dann auch für PHP-FPM Pools usw benutzt wird. Sprich user.server.anyserver.net wäre für den Linux Benutzer user zuständig.

Nun haben mich die Jungs von uberspace.de in einem ihrer Blog Posts darauf aufmerksam gemacht, dass Lets Encrypt SAN Zertifikate (Multi-Domain-Zertifikate) ausstellen kann. Wieso also das ganze nicht einfach dynamisch-automatsiert machen?

NGINX Konfiguration

Die NGINX Konfiguration arbeitet mit einem Regex Server Namen. Der erste Teil des Server Namens repräsentiert dabei den Namen des Homeverzeichnisses des jeweiligen Benutzers.

Der Benutzername wird in die Variable $userfolder gespeichert und bei den Pfadangaben und der FastCGI Konfiguration für PHP benutzt.

server {
   listen 80;
   server_name ~^(?<userfolder>[a-z]+)\.some\.example\.com$;
   location / {
      rewrite ^(.*) https://${userfolder}.some.example.com$1 redirect;
   }

   # lets encrypt host validation files
   location ~ ^/\.well-known/ {
      root /var/www/letsencrypt/;
   }
}

server {
   listen 443;
   server_name ~^(?<userfolder>[a-z]+)\.some\.example\.com$;

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

   root /var/www/virtual/$userfolder/html/;
   index index.html index.php;
   try_files $uri $uri/ /index.php$is_args$args;

   # lets encrypt host validation files
   location ~ ^/\.well-known/ {
      root /var/www/letsencrypt/;
   }

   # PHP files inside home
   location ~ ^/.+\.php {
      include fastcgi_params;
      fastcgi_index index.php;
      fastcgi_split_path_info ^(.+\.php)(/.*)$;
      fastcgi_pass unix:/var/run/php5-fpm-${userfolder}.sock;
   }
}

Außerdem ist ein entsprechender location {} Block für den Validationsprozess von Lets Encrypt in den Server Block eingebaut. Lets Encrypt prüft nur, ob die entsprechende Domain auch auf den Host zeigt, auf dem der Lets Encrypt Client ausgeführt wird. Ob die Domain meine ist, ist egal.

Zertifikat erstellen / aktualisieren

Ein recht simples Shellscript liest nun aus /etc/passwd alle Benutzerkonten aus, welche mit www- beginnen. Aus diesen werden dann die Domains für den Lets Encrypt Aufruf generiert.

Lets Encrypt wird dabei wie folgt aufgerufen:

  • Nur das SAN Zertifikat erstellen, Finger weg vom Webserver!
  • SAN Zertifikat nur neu erstellen, wenn dieses abgelaufen ist oder eine neue Domain dazu gekommen ist
  • Validationsdateien in den angegebenen Ordner schreiben
#!/bin/bash

# get usernames
webusers=$(cat /etc/passwd | grep -E "^www-[a-z]+" | grep -v -E "^www-data:" | cut -d ':' -f 1)

dargs=""
while read -r line; do
   # build domain name
   domain="$(echo $line | sed 's/^www-//g').p.anyserver.net"
   # append to arg string
   dargs="$dargs -d $domain"
done <<< "$webusers"

# call lets encrypt client
echo "Letsencrypt Domain Parameters: $dargs"
./letsencrypt/letsencrypt-auto certonly \
   -a webroot \
   --webroot-path=/var/www/letsencrypt \
   --keep-until-expiring \
   --expand \
   --agree-tos \
   -d some.example.com $dargs

# restart server
/etc/init.d/nginx restart

Die fest definierte erste Domain in dem Aufruf sorgt dafür, dass das Zertifikat bzw der Pfad zum Zertifikat immer gleich lautet. Ich habe hier einfach die Hauptdomain des Servers benutzt. Auch diese muss natürlich den location {} Block für die Validation enthalten.

Dieses kleine Script kann natürlich beliebig angepasst werden. Für meine Installation funktioniert das so sehr gut.

Ich war wirklich kurz davor mir wieder eine extended Validation bei StartSSL zuzulegen, damit ich mir da Wildcard Zertifikate erstellen kann. Aber so geht es auch.

Der einzige fade Beigeschmack ist der Lets Encrypt Client, welcher weiterhin nur als root ausführbar ist. Eventuell sperre ich den noch mal in einen Container ein, sodass der keinen Mist anstellen kann.

Einen alternativen Client, welcher den gleichen Funktionsumfang wie der original Client hat, habe ich bisher nicht gefunden. Bei alternativen Clients müsste man immer die ganzen Checks (Zertifikat abgelaufen, neue Domain, ...) selbst durchführen.

Have fun.

Update vom 07.04.2016

In einem Forum wurde ich darauf hingewiesen, dass es noch den alternativen Client letsencrypt.sh gibt. Dieser soll das ganze auch ohne root Rechte beherrschen.

Test folgt.

Hallo Internet

Mein Name ist Christian, vom Beruf bin ich Anwendungsentwickler.

In meiner Freizeit beschäftige ich mich mit verschiedensten Technologien. Hier sammele ich Dinge, die für mich interessant waren oder sind.