Gestion de LetsEncrypt sous FreeBSD avec nginx et une jail

LetsEncypt est une autorité de certification utilisant une API basée sur des appels HTTP et un client côté serveur qui va générer des tokens lus par les serveurs LetsEncrypt. Nous allons ici voir une architecture LetsEncrypt typique dans laquelle nous allons utiliser le client acme-client d'OpenBSD disponible dans les ports et les packages FreeBSD.

Installation

Dans un premier temps installez le client sur votre machine hôte (et pas votre jail web):

pkg install acme-client

Configuration de nginx

Nous allons maintenant configurer nginx afin de pouvoir interagir avec l'API letsencrypt. Créez tout d'abord le répertoire suivant dans votre jail avec l'utilisateur et le groupe nobody

mkdir /usr/local/www/.well-known && chown nobody:nobody /usr/local/www/.well-known

Ensuite configurez vos virtual hosts afin de faire pointer les appels vers /.well-known dans ce répertoire:

server {
        listen 80;
        server_name www.unix-experience.fr;
        location /.well-known {
            alias /usr/local/www/.well-known;
        }
        location / {
            return 301 https://www.unix-experience.fr$request_uri;
        }
        add_header X-Frame-Options "DENY";
        add_header Strict-Transport-Security "max-age=86400; preload";
}

Note: nous ajoutons ici le support du protocole HSTS avec un TTL d'1 journée spécifiant aux navigateurs que le site supporte SSL et qu'il faudrait passer dessus. Vous pouvez sans souci faire pointer plusieurs de vos virtual hosts dans ce même répertoire.

Configuration du client LetsEncrypt

Retournez maintenant sur votre hôte et créez le fichier /usr/local/etc/acme/domains.txt puis ajoutez y un domaine par ligne:

www.unix-experience.fr
ftp.unix-experience.fr

Créez ensuite le script ci-dessous (basé sur le script présent dans le même répertoire sous forme d'exemple) dans /usr/local/etc/acme/acme-client.sh

#!/bin/sh -e
WEBJAIL="web"
BASEDIR="/usr/local/etc/acme"
SSLDIR="/usr/local/etc/ssl/acme"
DOMAINSFILE="${BASEDIR}/domains.txt"
CHALLENGEDIR="/jails/${WEBJAIL}/usr/local/www/.well-known/acme-challenge"

[ ! -d "${SSLDIR}/private" ] && mkdir -pm700 "${SSLDIR}/private"

cat "${DOMAINSFILE}" | while read domain line ; do
   CERTSDIR="${SSLDIR}/${domain}"
   [ ! -d "${CERTSDIR}" ] && mkdir -pm755 "${CERTSDIR}"
   set +e # RC=2 when time to expire > 30 days
   acme-client -b -C "${CHALLENGEDIR}" \
               -k "${SSLDIR}/private/${domain}.pem" \
               -c "${CERTSDIR}" \
               -n -N -v \
               ${domain} ${line}
   RC=$?
   set -e
   [ $RC -ne 0 -a $RC -ne 2 ] && exit $RC
done

Ce script lit les domaines présents dans le fichier domains.txt puis il va s'occuper de créer vos clefs privées et de les faire signer par LetsEncrypt, en se basant sur le répertoire .well-known présent dans votre jail. Une fois ce script exécuté, si tout s'est passé correctement vous trouverez votre clef privée dans le répertoire /usr/local/etc/ssl/acme/private/example.org et votre clef publique et les chaînes de certificats dans le répertoire /usr/local/etc/ssl/acme/example.org. Ces répertoires sont situés sur votre hôte et non dans votre jail web. Pour déployer les scripts un script d'exemple est présent dans le répertoire /usr/local/etc/**acme, mais nous allons légèrement le modifier. Créez le fichier /usr/local/etc/acme/deploy.sh**:

#!/bin/sh

set -e

BASEDIR="/usr/local/etc/acme"
DOMAINSFILE="${BASEDIR}/domains.txt"
LEDIR="/usr/local/etc/ssl/acme"
JAILSDIR="/jails"
TARGETS="web"
cat "${DOMAINSFILE}" | while read domain line ; do
    for jail in ${TARGETS}; do
        targetdir="${JAILSDIR}/${jail}/etc/ssl"
        # Check if the certificate has changed
        [[ -z "`diff -rq ${LEDIR}/${domain}/fullchain.pem ${targetdir}/certs/${domain}.pem`" ]] && continue
        cp -L "${LEDIR}/private/${domain}.pem"   "${targetdir}/private/${domain}.pem"
        cp -L "${LEDIR}/${domain}/fullchain.pem" "${targetdir}/certs/${domain}.pem"
        chmod 400 "${targetdir}/private/${domain}.pem"
        chmod 644 "${targetdir}/certs/${domain}.pem"
        # Restart/-load relevant services
        [[ "${jail}" = "web" ]] && jexec ${jail} service nginx restart
    done
done

Ce script va copier tous les certificats associés à vos dernières négociations LetsEncrypt et va les déployer dans votre jail web puis redémarrer nginx.

Renouvellement automatique

Pour finir il ne reste plus qu'à configurer le cron ajouté par le paquet acme-client afin de configurer la tâche hebdomadaire. Pour se faire, éditez le fichier /usr/local/etc/periodic.conf et ajoutez les entrées suivantes:

# letsencrypt
weekly_acme_client_enable="YES"
weekly_acme_client_user="nobody"
weekly_acme_client_deployscript="/usr/local/etc/acme/deploy.sh"

Cela va vous permettre d'activer le cron, et de lancer le déploiement automatiquement toutes les semaines.

Conclusion

Vous pouvez désormais faire des demandes de certificats LetsEncrypt facilement et les renouveler automatiquement via un cron fourni par acme-client.

Apache: authentification par certificats

L'authentification par certificats permet de remplacer le traditionnel couple login/password dans un environnement doté de PKI.

Le certificat doit être reconnu par le serveur web, que ce soit par le biais du fait que le CA authentifie côté serveur et qu'on peut également filtrer les utilisateurs par données présentes dans le certificat.

Entre autres, cela permet également d'utiliser des supports externes au clavier pour reconnaître les utilisateurs, comme des lecteurs de carte à puce.

Nous allons voir ici comment activer l'authentification par certificat sous Apache.

Configuration

Ouvrez le fichier de votre virtualhost favori et activez tout d'abord le support HTTPS en ajoutant une clef publique, privée et optionnellement une chaîne de certificats:

SSLEngine on
SSLCertificateFile "/etc/ssl/www.unix-experience.fr.crt"
SSLCertificateKeyFile "/etc/ssl/www.unix-experience.fr.key"
SSLCertificateChainFile "/etc/ssl/myCA.pem"

Maintenant que le chiffrement des communications est assuré, vous pouvez sécuriser l'accès au Virtualhost en rajoutant les lignes suivantes:

SSLVerifyDepth 3
SSLVerifyClient require
SSLCACertificateFile "/etc/ssl/myCA.pem"
SSLOptions +StdEnvVars

Détaillons les différentes options:

  • SSLVerifyDepth: permet de définir quelle est la profondeur de la vérification sur le certificat SSL. Il faut mettre au minimum 2 suivant comment est construit votre certificat client
  • SSLVerifyClient: permet de forcer l'authentification par certificat si vous optez pour require. Autrement spécifiez optionnal
  • SSLCaCertificateFile: Il s'agit du CA qui correspond aux certificats clients
  • SSLOptions + StdEnvVars: permet de passer les options du certificat client aux moteurs sous jacents (PHP par exemple).

Maintenant que votre serveur est capable d'authentifier les certificats provenant du CA myCA.pem, il vous faut générer des certificats utilisant un CN (Common Name) correspondant à l'adresse mail de vos utilisateurs et une application qui soit pleinement compatible en face (un SSO comme Shibboleth par exemple).

Conclusion

Vous savez désormais comment configurer Apache pour authentifier des clients par certificats. Il ne reste plus qu'à mettre en place une PKI.