Installer, configurer et renouveller automatiquement un certificat SSL Let's Encrypt

J'ai récemment passé mon site https://vincent.composieux.fr en HTTPS en utilisant Let's Encrypt, un service permettant la génération gratuite et automatisée d'un certificat SSL. Cet article va vous expliquer la façon dont j'ai procédé pour la mise en place ainsi que pour le renouvellement automatique du certificat tous les 60 jours.

Qu'est-ce-que Let's Encrypt ?

L'initiative lancée par Let's Encrypt est sponsorisée par les plus grandes entreprises du domaine de l'Internet telles que: Google (Chrome), Mozilla (Firefox), Akamai, Facebook, ... ce service est donc largement supporté. Depuis le 3 décembre 2015, Let's Encrypt est entré en beta publique, permettant à chacun de générer un certificat SSL pour son site. C'est de plus complètement gratuit et automatisé. Une vraie révolution. Commençons donc à installer Let's Encrypt et créer notre premier certificat !

Installation de Let's Encrypt et création du certificat

Tout d'abord, clonons le repository Git de Let's Encrypt et plaçons les sources dans /opt:

$ git clone https://github.com/letsencrypt/letsencrypt
$ mv letsencrypt /opt/letsencrypt

Nous allons maintenant créer un fichier de configuration permettant de définir la façon par laquelle nous voulons que notre certificat SSL soit généré pour notre domaine. Créez donc un fichier à l'emplacement /usr/local/etc/le-yourdomain-webroot.ini:

# We use a 4096 bit RSA key instead of 2048
rsa-key-size = 4096

email = email@example.org
domains = yourdomain.tld, subdomain.yourdomain.tld

authenticator = webroot

# This is the webroot directory of your domain in which
# letsencrypt will write a hash in /.well-known/acme-challenge directory.
webroot-path = /var/www/yourdomain.tld/

Je ne vais pas m'éterniser sur ce fichier de configuration car il est plutôt simple à comprendre, il suffit en effet de spécifier le(s) domaine(s) pour lesquels vous voulez générer un certificat, une adresse e-mail et que nous allons utiliser la méthode webroot pour authentifier le domaine.

Afin que la méthode d'authentification fonctionne correctement et en fonction de votre configuration Nginx, vous devrez ajouter ces quelques lignes :

    location '/.well-known/acme-challenge' {
        root /var/www/yourdomain.tld/;
        try_files $uri /$1;
    }

Maintenant que votre fichier de configuration est créé, générez le certificat SSL à l'aide de la commande suivante :

$ sudo /opt/letsencrypt/letsencrypt-auto certonly --config /usr/local/etc/le-yourdomain-webroot.ini

Si la commande a été exécutée correctement, Let's Encrypt doit vous avoir créé un fichier .pem (entre autres) dans le répertoire suivant :

$ ls /etc/letsencrypt/live/yourdomain.tld/
cert.pem  chain.pem  fullchain.pem  privkey.pem

Il est maintenant temps de modifier la configuration de votre virtual host Nginx ainsi que votre configuration de Varnish pour prendre en compte ce certificat.

Mise à jour de la configuration de Nginx et Varnish

Commençons par le virtual host Nginx afin d'écouter sur le port 443 (HTTPS). Il nous suffit d'ajouter une section server qui fera simplement un proxy_pass sur votre application HTTP standard :

server {
    listen 443 ssl;
    server_name yourdomain.tld;

    ssl_certificate /etc/letsencrypt/live/yourdomain.tld/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.tld/privkey.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers AES256+EECDH:AES256+EDH:!aNULL;

    location / {
        proxy_pass http://127.0.0.1:80;

        proxy_set_header X-Real-IP  $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header Host $host;
    }
}

Après avoir redémarré Nginx, vous devriez pouvoir naviguer sur https://yourdomain.tld et voir votre certificat SSL avec un statut valide. Si comme moi, vous utilisez Varnish et souhaité rediriger tout le trafic HTTP vers HTTPS, voici un bout de code à ajouter dans votre configuration Varnish (v4) :

sub vcl_recv {
    if (req.http.X-Forwarded-Proto !~ "(?i)https") {
        return (synth(750, ""));
    }

    ...

    return (hash);
}

sub vcl_synth {
    if (resp.status == 750) {
        set resp.status = 301;
        set resp.http.Location = "https://yourdomain.tld" + req.url;

        return(deliver);
    }
}

Maintenant, lorsque vous visiterez http://yourdomain.tld, vous serez automatiquement redirigé vers https://yourdomain.tld. Nous y somme presque ! Votre certificat est maintenant prêt mais pour une période de 90 jours seulement. Afin d'éviter qu'il n'expire au bout de ces quelques jours, il nous faut mettre en place un script de renouvellement automatique.

Script de renouvellement automatique du certificat SSL

Afin de renouveller le certificat, j'ai créé un script (sur une base trouvée sur Internet) qui sera exécuté dans via une tâche crontab tous les jours à 2h du matin. Ce script va vérifier en fontion de la date courante et la date d'expiration du certificat afin de renouveller le certificat dans le cas ou nous serions dans la période de 30 jours avant l'expiration (cette valeur peut être modifiée). Veuillez noter que la configuration d'Nginx sera rechargée afin de prendre en compte le nouveau certificat. Voici le script. Vous pouvez le placer à l'emplacement /usr/local/etc/renew-certificates.sh:

#!/bin/bash

WEB_SERVICE='nginx'
CONFIG_FILE='/usr/local/etc/le-yourdomain-webroot.ini'
LE_PATH='/opt/letsencrypt'
EXP_LIMIT=30;

if [ ! -f $CONFIG_FILE ]; then
        echo "[ERROR] config file does not exist: $CONFIG_FILE"
        exit 1;
fi

DOMAIN=`grep "^\\s*domains" $CONFIG_FILE | sed "s/^\\s*domains\\s*=\\s*//" | sed 's/(\\s*)\\|,.*$//'`
CERT_FILE="/etc/letsencrypt/live/$DOMAIN/fullchain.pem"

if [ ! -f $CERT_FILE ]; then
	echo "[ERROR] certificate file not found for domain $DOMAIN."
fi

DATE_NOW=$(date -d "now" +%s)
EXP_DATE=$(date -d "`openssl x509 -in $CERT_FILE -text -noout | grep "Not After" | cut -c 25-`" +%s)
EXP_DAYS=$(echo \\( $EXP_DATE - $DATE_NOW \\) / 86400 |bc)

echo "Checking expiration date for $DOMAIN..."

if [ "$EXP_DAYS" -gt "$EXP_LIMIT" ] ; then
	echo "The certificate is up to date, no need for renewal ($EXP_LIMIT days left)."
	exit 0;
else
	echo "The certificate for $DOMAIN is about to expire soon. Starting webroot renewal script..."
        $LE_PATH/letsencrypt-auto certonly --renew-by-default --config $CONFIG_FILE
	echo "Reloading $WEB_SERVICE"
	/usr/sbin/service $WEB_SERVICE reload
	echo "Renewal process finished for domain $DOMAIN"
	exit 0;
fi

Vous pouvez le tester en modifiant la valeur de la variable EXP_LIMIT de 30 à 90 et vous devriez voir la sortie suivante dans le cas ou le certificat a bien été renouvellé :

Checking expiration date for yourdomain.tld...
The certificate for yourdomain.tld is about to expire soon. Starting webroot renewal script...
Updating letsencrypt and virtual environment dependencies......
Requesting root privileges to run with virtualenv: /root/.local/share/letsencrypt/bin/letsencrypt certonly --renew-by-default --config /usr/local/etc/letsencrypt/le-yourdomain-webroot.ini

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/yourdomain.tld/fullchain.pem. Your cert will
   expire on 2016-03-31. To obtain a new version of the certificate in
   the future, simply run Let's Encrypt again.
 - If you like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Reloading nginx
[ ok ] Reloading nginx configuration: nginx.
Renewal process finished for domain yourdomain.tld

Il ne vous reste plus qu'à ajouter cette petite ligne dans votre crontab afin que le script soit exécuté tous les jours à 2h du matin par exemple :

0 2 * * * /bin/bash /usr/local/etc/renew-certificates.sh

Et voila, c'est fini ! Votre certificat SSL gratuit est maintenant en place et sera renouvellé automatiquement.