Gehe zu deutscher Webseite

ViaThinkSoft CodeLib

Dieser Artikel befindet sich in:
CodeLibHowTos

Dieses Beispiel zeigt auf, wie man Let's Encrypt verwenden kann, um Zertifikate mit einer individuellen Automatik zu erstellen, anstelle sich auf die 100% Automatik von Let's Encrypt zu verlassen. Sie behalten somit stets die volle Kontrolle über Ihre Webserver-Konfiguration und die Zertifikate und müssen sich nicht sorgen, dass ein fremdes Programm Ihre Konfigurationen des Webservers stört. Das Verwenden der Zertifikate für andere Dienste (MySQL, FTP, IMAP, SMTP etc) ist dadurch einfach zu bewerkstelligen.

Diese Anleitung erfordert, dass Sie bereits übere eine fertig eingerichtete Apache-Installation verfügen, und sich mit SSL auskennen. Diese Anleitung spricht insbesondere Webmaster an, die von einer bestehenden CA zu Let's Encrypt wechseln wollen.

In unserem Beispiel speichern wir unsere Zertifikats-Relevanten Daten in /daten/ssl/letsencrypt . Alle hier gezeigten Verzeichnis-Namen und Einstellungen sind selbstverständlich nur Beispiele und können abgeändert werden.

Schritt 1: Einrichten von Certbot, Apache und dem Cronjob (einmalig)

1. Anlegen der relevanten Ordner

sudo mkdir /daten
sudo mkdir /daten/ssl
sudo mkdir /daten/ssl/letsencrypt


2. Installieren und Einrichten von Apache2-Modulen:

Führen Sie folgende Befehle aus:

sudo aptitude update
sudo aptitude install libapache2-mod-macro
sudo a2enmod macro
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod ssl


3. Einrichten einiger Macros in Apache. Erstellen Sie hierfür /etc/apache2/sites-available/000--macros.conf

Achtung! Es handelt sich hierbei um zwei Bindestriche. "000--macros.conf" muss nämlich vor "000-default.conf" einsortiert/geladen werden.

<Macro LetsEncryptProxy>
     <IfModule mod_proxy.c>
          ProxyPass "/.well-known/acme-challenge/" "http://127.0.0.1:999/.well-known/acme-challenge/" retry=1
          ProxyPassReverse "/.well-known/acme-challenge/" "http://127.0.0.1:999/.well-known/acme-challenge/"
          <Location "/.well-known/acme-challenge/">
               ProxyPreserveHost On
               Order allow,deny
               Allow from all
               Require all granted
          </Location>
     </IfModule>
</Macro>

<Macro LetsEncryptSSL $sitedirname $ssl_log>
     SSLEngine on
     SSLCertificateFile "/daten/ssl/letsencrypt/$sitedirname/certificate.pem"
     SSLCertificateKeyFile "/daten/ssl/letsencrypt/$sitedirname/private.key"
     SSLCertificateChainFile "/daten/ssl/letsencrypt/$sitedirname/intermediate_ca.pem"
     SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown
     CustomLog "$ssl_log" "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</Macro>


4. Aktivien Sie die Konfigurationsdatei:

cd /etc/apache2/sites-enabled/
ln -s ../sites-available/000--macros.conf


5. Fügen Sie die folgende Zeile in jedem <VirtualHost> Block in Ihren Webseiten-Konfigurationen (/etc/apache2/sites-available/*.conf) ein:

Use LetsEncryptProxy


Sollte die Validierung der Domain im späteren Prozess fehlschlagen, könnte eventuell eine Rewrite-Rule dies verhindern. In diesem Fall muss folgende Zeile dem jeweiligen Rewrite-Abschnitt hinzugefügt werden:

RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/


6. Neustarten von Apache:

sudo service apache2 restart


7. Erstellen Sie folgendes Script: /daten/ssl/letsencrypt/renew-all.sh (mit Ausführungs-Berechtigungen per chmod +x renew-all.sh)

#!/bin/bash

DIR=$( dirname "$0" )

"$DIR"/website1/renew.sh
"$DIR"/website2/renew.sh
"$DIR"/website3/renew.sh
...

# Falls Sie eines der Zertifikate für andere Dienste verwenden, aktivieren Sie bitte die jeweiligen Einträge durch Entfernen der Raute:
#service vsftpd restart
#service postfix restart
#service cyrus-imapd restart

#service icinga2 stop
#service webmin stop
service apache2 stop
service mysql stop

service mysql start
service apache2 start
#service webmin start
#service icinga2 start


8. Fügen Sie einen Cronjob für den Benutzer root hinzu, um die Zertifikats-Erneuerung regelmäßig durchzuführen:

sudo crontab -e


Und fügen Sie folgende Zeile an:

0   0   1   *   *    /daten/ssl/letsencrypt/renew-all.sh


9. Installieren von Certbot:

sudo aptitude update
sudo aptitude install certbot


Sofern das Paket "certbot" in Ihrer Linux-Konfiguration nicht verfügbar ist, dann führen Sie die folgenden Befehle durch:

sudo aptitude update
sudo aptitude install git
cd /daten/ssl/letsencrypt/
git clone https://github.com/letsencrypt/letsencrypt
mv letsencrypt _certbot


10. Einrichten der Linux-Benutzer:

groupadd ssl
usermod -a -G ssl www-data
chown -R root:ssl /daten/ssl/



Schritt 2: Einrichten der Scripts für eine neue Webseite, hier im Beispiel "website1":

1. Erstellen Sie die Verzeichnisse /daten/ssl/letsencrypt/website1/ und /daten/ssl/letsencrypt/website1/old/

2. Erstellen Sie /daten/ssl/letsencrypt/website1/openssl.cnf und ersetzen Sie die Domain-Namen.

[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[req_distinguished_name]
CN = www.domain1.de

[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = domain1.de
DNS.2 = www.domain2.de
DNS.3 = domain2.de
...


3. Erstellen Sie /daten/ssl/letsencrypt/website1/config und tragen Sie Ihre E-Mail-Adresse ein:

EMAIL="..."
RSASIZE=4096
SERVER="https://acme-v01.api.letsencrypt.org/directory"
#SERVER="https://acme-staging.api.letsencrypt.org/directory"


4. Erstellen Sie /daten/ssl/letsencrypt/website1/renew.sh (mit Ausführungs-Berechtigungen per chmod +x renew.sh)

#!/bin/bash

# --- Initialization

DIR=$( dirname "$0" )
cd "$DIR"

if [ ! -f openssl.cnf ]; then
     echo "Bitte das Script im jeweiligen Verzeichnis aufrufen." >&2
     exit 2
fi

. config

# --- Clean up

rm *_pkcs12.p12 2> /dev/null
rm *_private.key 2> /dev/null
rm *_cert.pem 2> /dev/null
rm *_chain.pem 2> /dev/null
rm *_req.csr 2> /dev/null
rm certbot.log 2> /dev/null

# --- Create private key

openssl genrsa -out 0000_priv.key $RSASIZE
if [ $? -ne 0 ]; then
     echo "FAILED TO CREATE PRIVATE KEY" >&2
     exit 1
fi
chown root:ssl 0000_priv.key
chmod 640 0000_priv.key

# --- Create certificate request

openssl req -new -batch -sha256 \
     -key 0000_priv.key \
     -config openssl.cnf \
     -out 0000_req.csr
if [ $? -ne 0 ]; then
     echo "FAILED TO CREATE CERTIFICATE REQUEST" >&2
     exit 1
fi

# --- Ask server to sign the certificate

if [ -f ../_certbot/certbot-auto ]; then
     EX="../_certbot/certbot-auto"
else
     EX="certbot"
fi

$EX certonly \
     --authenticator standalone \
     --preferred-challenges http-01 --http-01-port 999 \
     --server $SERVER \
     --text \
     --email $EMAIL \
     --csr 0000_req.csr
if [ $? -ne 0 ]; then
     echo "CERTBOT FAILED" >&2
     exit 1
fi

# --- Security check: check if certificate and private key are matching

a=$( openssl x509 -noout -modulus -in 0000_cert.pem | openssl sha256 )
b=$( openssl rsa -noout -modulus -in 0000_priv.key | openssl sha256 )

if [ "$a" != "$b" ]
then
     echo "FEHLER: Zertifikat stimmt nicht mit privatem Schlüssel überein!" >&2
     exit 1
fi

# --- PKCS#12 erstellen

openssl pkcs12 -export -in 0000_cert.pem -inkey 0000_priv.key -certfile 0000_chain.pem -out 0000_pkcs12.p12 -passout pass:
if [ $? -ne 0 ]
then
     echo "FEHLER bei PCKS#12-Erstellung!" >&2
     if [ -f 0000_pkcs12.p12 ]
     then
          chmod 600 0000_pkcs12.p12
          rm 0000_pkcs12.p12
     fi
     exit 1
fi

if [ ! -f 0000_pkcs12.p12 ]
then
     echo "FEHLER! PCKS#12 konnte nicht erstellt werden!" >&2
     exit 1
fi

chmod 600 0000_pkcs12.p12

# --- Activate certs

# Files created by certbot:
# 0000_cert.pem  = cert.pem (i.e., the server certificate)
# 0000_chain.pem = chain.pem (i.e., the intermediate certificate)
# 0001_chain.pem = fullchain.pem (i.e., a concatenation of cert.pem + chain.pem in one file).

mv -f 0000_pkcs12.p12 "old/$(date +%s).p12"
mv -f 0000_priv.key private.key
mv -f 0000_cert.pem certificate.pem
mv -f 0000_chain.pem intermediate_ca.pem
rm 0000_req.csr
rm certbot.log 2> /dev/null
rm 0001_chain.pem

# --- Additional security check: X509 Lint

if [ -f "../_x509lint/x509lint" ]; then
     ../_x509lint/x509lint certificate.pem
fi

# --- Delete expired archived certificates

FILES=old/*.p12
for f in $FILES
do
     openssl pkcs12 -in "$f" -clcerts -nokeys -passin pass: | openssl x509 -noout -checkend 0 > /dev/null
     if [ $? -eq 1 ]; then
          echo "$f has expired. Deleting."
          rm "$f"
     fi
done

# --- Post create: Restart servers etc.

if [ -f postcreate.sh ]; then
     ./postcreate.sh
fi


5. (Optional) Erstellen Sie folgendes Script /daten/ssl/letsencrypt/website1/recover_cert.sh, das im Notfall aufgerufen werden kann, um ein Zertifikat samt privatem Schlüssel wiederherzustellen (mit Ausführungs-Berechtigungen per chmod +x recover_cert.sh)

#!/bin/bash

DIR=$( dirname "$0" )
cd "$DIR"

if [ "$1" == "--help" ]; then
     echo "Syntax: $0 <p12file>"
     exit 2
fi

if [ ! -f "$1" ]; then
     echo "ERROR: File '$1' does not exist" >&2
     exit 1
fi

openssl pkcs12 -in "$1" -nocerts -out tmp_priv.key -passin pass: -nodes
if [ $? -ne 0 ]; then
     echo "ERROR recovering the private key" >&2
     rm tmp_priv.key 2> /dev/null
     rm tmp_cert.pem 2> /dev/null
     rm tmp_ca.pem 2> /dev/null
     exit 1
fi

openssl pkcs12 -in "$1" -clcerts -nokeys -out tmp_cert.pem -passin pass:
if [ $? -ne 0 ]; then
     echo "ERROR recovering the certificate" >&2
     rm tmp_priv.key 2> /dev/null
     rm tmp_cert.pem 2> /dev/null
     rm tmp_ca.pem 2> /dev/null
     exit 1
fi

openssl pkcs12 -in "$1" -cacerts -nokeys -out tmp_ca.pem -passin pass:
if [ $? -ne 0 ]; then
     echo "ERROR recovering the intermediate certificate" >&2
     rm tmp_priv.key 2> /dev/null
     rm tmp_cert.pem 2> /dev/null
     rm tmp_ca.pem 2> /dev/null
     exit 1
fi

mv -f tmp_priv.key private.key
if [ $? -ne 0 ]; then
     echo "ERROR moving the private key" >&2
     exit 1
fi

mv -f tmp_cert.pem certificate.pem
if [ $? -ne 0 ]; then
     echo "ERROR moving the certificate" >&2
     exit 1
fi

mv -f tmp_ca.pem intermediate_ca.pem
if [ $? -ne 0 ]; then
     echo "ERROR moving the intermediate certificate" >&2
     exit 1
fi

echo "Certificate $1 recovered."


6. Editieren Sie die jeweiligen Einstellungen der Webseite in /etc/apache2/sites-available/ .

Hier fügen Sie in den SSL (Port 443) VirtualHosts jeweils folgenden Befehl ein, um SSL zu aktivieren:

Use LetsEncryptSSL website1 /var/log/.../website1/ssl_request.log



Schritt 3: Testen

Rufen Sie /daten/ssl/letsencrypt/renew-all.sh das erste Mal manuell auf und folgen den Bildschirm-Anweisungen bzw. achten auf etwaige Fehlermeldungen.
Unter anderem müssen Sie einmalig den Regeln zustimmen und angeben, ob Sie in die EFF-Mailingliste aufgenommen werden möchten.

Ersetzen Sie in den config-Dateien "acme-v01" durch "acme-staging", um die Zertifikate von einer Test-CA zu erhalten. Dadurch wird das Erstellen unnötiger Test-Zertifikate verhindert.


Mögliche Fehlerquellen

Tritt ein Timeout auf, obwohl die Webseite öffentlich erreichbar sein kann, so kann es sein, dass IPv6 gestört ist. Let's Encrypt bevorzugt IPv6 (AAAA) DNS-Records.


Getestet mit

- Debian Stretch (amd64)
- Raspberry Pi 3 "Raspbian" (armv7l)
Daniel Marschall
ViaThinkSoft Mitbegründer, Security Management
ViaThinkSoft Co-Founder, Security Management