www.netz39.de/_posts/2018-01-25-howto-lets-encrypt-fuer-prosody-0-9.md

8.8 KiB
Raw Blame History

layout title date categories tags
post HowTo: Let's Encrypt für prosody 0.9 2018-01-25
maschinenraum
debian
dehydrated
howto
jabber
lets-encrypt
nginx
prosody

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 und popd 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:-)