9 KiB
author | layout | title | date | categories | tags | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
lespocky | post | HowTo: Let's Encrypt für prosody 0.9 | 2018-01-25 |
|
|
Für unseren Jabber- bzw. XMPP-Server unter jabber.n39.eu brauchen wir ein Zertifikat für die verschlüsselte Kommunikation zwischen Clients und Server bzw. für die Kommunikation der Server untereinander. Früher benutzten wir da eins von CAcert, was ab und an zu Nachfragen der Nutzer führte. Seit einiger Zeit kann man über Let's Encrypt kostenfrei SSL-/TLS-Zertifikate bekommen und genau so eins kommt nun für unseren Jabber-Server zum Einsatz. Wie? So:
Auf der virtuellen Maschine läuft als XMPP-Server prosody, noch in Version 0.9.x aus Debian 8 (jessie) bzw. 9 (stretch). Als Client für das ACME-Protokoll zur Kommunikation mit den Servern von Let's Encrypt kommt dehydrated zum Einsatz, das mittlerweile auch einfach über die Debian-Paketverwaltung installiert werden kann. Für das Challenge-Response benutzen wir die webroot-Methode zusammen mit dem Webserver nginx.
prosody
Wir fahren hier eine Konfiguration, die sich an den Vorgaben vom Debian-Paket orientiert. Für den VirtualHost jabber.n39.eu haben wir folgenden Ausschnitt aus der Konfiguration:
VirtualHost "jabber.n39.eu"
ssl = {
key = "/etc/prosody/certs/jabber.n39.eu.key";
certificate = "/etc/prosody/certs/jabber.n39.eu.pem";
dhparam = "/etc/prosody/certs/dh-2048.pem";
-- TODO for post 0.10 see http://prosody.im/doc/advanced_ssl_config again
options = { "no_sslv2", "no_sslv3", "no_ticket", "no_compression", "cipher_server_preference", "single_dh_use", "single_ecdh_use" };
}
Das dient hier im wesentlichen zu zeigen, wo bei uns Key und Zertifikat liegen. Die Zertifikatsdatei muss auch Root-Zertifikat und Intermediate-Zertifikate in der richtigen Reihenfolge enthalten. Details dazu gibt es in der Doku zu prosody (Certificates), aber wir bekommen das später fertig von dehydrated.
nginx
Hier haben wir uns eng an die Doku von dehydrated (wellknown.md) gehalten und in unser bestehenden nginx config für jabber.n39.eu den alias für das well-known-Verzeichnis ergänzt:
location /.well-known/acme-challenge {
alias /var/www/dehydrated;
}
Auch dieser Pfad wird gleich bei der Konfiguration von dehydrated noch wichtig sein. Das Verzeichnis /var/www/dehydrated
hatten wir zuvor angelegt und die Rechte passend für den Webserver gesetzt:
mkdir -p /var/www/dehydrated
chown -R www-data:www-data /var/www/dehydrated
chmod 2775 /var/www/dehydrated
Wer hier noch gar keine Konfiguration hat, legt sich einen neuen virtual host in nginx an.
dehydrated
Basis-Setup
Hier fahren wir eine Mischung aus den Vorgaben von dehydrated und vom Debian-Paket, die Konfiguration ist aber sehr übersichtlich. Die Datei /etc/dehydrated/domains.txt
wenig überraschend:
jabber.n39.eu conference.jabber.n39.eu
Interessanter möglicherweise /etc/dehydrated/config
– hier haben wir die Variable WELLKNOWN
angepasst und die Variablen CONTACT_EMAIL
und LICENSE
ergänzt. Letzteres ist notwendig, weil Let's Encrypt hier mittlerweile eine neuere Version abnicken lässt, als noch im alten Debian-Paket voreingestellt ist. Lässt man das auf default, wird der Aufruf von dehydrated aber fehlschlagen. Die passende URL bekommt man dann in der Ausgabe oder in einem Logfile (das weiß ich nicht mehr genau).
#############################################################
# This is the main config file for dehydrated #
# #
# This is the default configuration for the Debian package. #
# To see a more comprehensive example, see #
# /usr/share/doc/dehydrated/examples/config #
# #
# For details please read: #
# /usr/share/doc/dehydrated/README.Debian #
#############################################################
CONFIG_D=/etc/dehydrated/conf.d
BASEDIR=/var/lib/dehydrated
#WELLKNOWN="${BASEDIR}/acme-challenges"
WELLKNOWN="/var/www/dehydrated"
DOMAINS_TXT="/etc/dehydrated/domains.txt"
CONTACT_EMAIL="admin@netz39.de"
LICENSE="https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf"
#CA="https://acme-staging.api.letsencrypt.org/directory"
Bevor wir die notwendigen Hooks erstellen, rufen wir dehydrated einmal auf:
dehydrated -c
Das sollte erfolgreich durchlaufen. Danach sind unterhalb von /var/lib/dehydrated
allerlei neue Dateien zu finden, unter anderem auch unser Key und Zertifikat. Zum ersten Test hatte ich diese dann per Hand ins Verzeichnis von prosody geschoben und den neu gestartet. Funktionierte auf Anhieb. :-)
Automatisierung
Da die von Let's Encrypt ausgegebenen Zertifikate nur eine Gültigkeitsdauer von 90 Tagen haben, will man das Erneuern derselben natürlich automatisieren. Folgende Schritte sind notwendig:
- vor Ablauf der 90 Tage neues Zertifikat holen
- Zertifikat zu prosody kopieren und Rechte setzen
- prosody Zertifikate neu laden lassen
Den ersten Punkt erschlagen dehydrated und cron. Man lässt cron einfach regelmäßig dehydrated aufrufen. Einmal crontab -e
(als root) eintippern und folgende Zeile ergänzen:
39 4 \* \* mon,thu dehydrated -c
Hier wird dehydrated jetzt also Montag und Donnerstag um 4:39 Uhr aufgerufen, sollte hoffentlich reichen um rechtzeitig vor Ablauf des Zertifikats ein neues zu bekommen.
Fehlt noch der Teil »Zeug aus /var/lib/dehydrated
nach /etc/prosody/certs
bekommen«. Wir nutzen dazu Config on per-certificate base um ein nur für das Zertifikat des Jabber-Servers gültiges Hook-Skript nutzen zu können. Dazu wurde im Pfad /var/lib/dehydrated/certs/jabber.n39.eu
die Datei config
angelegt und wie folgt befüllt:
HOOK="${CERTDIR}/jabber.n39.eu/hook.sh"
Ins selbe Verzeichnis haben wir dann hook.sh
aus /usr/share/doc/dehydrated/examples
kopiert und angepasst. (Für alle Nicht-Debian-Nutzer gibt es das komplette Skript (ohne Anpassungen) auch upstream: hook.sh)
function deploy_cert {
local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}"
# This hook is called once for each certificate that has been
# produced. Here you might, for instance, copy your new certificates
# to service-specific locations and reload the service.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - KEYFILE
# The path of the file containing the private key.
# - CERTFILE
# The path of the file containing the signed certificate.
# - FULLCHAINFILE
# The path of the file containing the full certificate chain.
# - CHAINFILE
# The path of the file containing the intermediate certificate(s).
# - TIMESTAMP
# Timestamp when the specified certificate was created.
PROSODY_CERT_DIR='/etc/prosody/certs'
service prosody stop
pushd ${PROSODY_CERT_DIR}
mv "${DOMAIN}.key" "${DOMAIN}.$(date --iso-8601).key"
cp "${KEYFILE}" "${DOMAIN}.key"
chmod 600 "${DOMAIN}.key"
chown prosody:prosody "${DOMAIN}.key"
mv "${DOMAIN}.pem" "${DOMAIN}.$(date --iso-8601).pem"
cp "${FULLCHAINFILE}" "${DOMAIN}.pem"
chmod 600 "${DOMAIN}.pem"
chown prosody:prosody "${DOMAIN}.pem"
popd
service prosody start
}
Wie man leicht sieht, tun wir hier folgendes:
- eine Variable mit dem Pfad für die certs von prosody anlegen
- prosody stoppen (hier nutzen wir aus den Prosody Community Modules übrigens mod_graceful_shutdown)
- alten Key sichern
- neuen Key hinkopieren
- Rechte setzen
- das gleiche für das Cert nochmal, wichtig das
${FULLCHAINFILE}
nutzen! - prosody wieder starten
- zwischendrin werden die Funktionen
pushd
undpopd
der Bash genutzt
Das hook.sh
dann noch mit chmod
ausführbar machen, fertig. :-)
Update: hier wurde bewusst der Neustart des prosody gewählt, weil der leider ein hässliches Speicherleck hat. So wird er alle paar Wochen mal neu gestartet. Das ist ggf. für die Nutzer unschön, aber uns geht unsere VM nicht krachen. O:-)