Let’s Encrypt, Certbot et SSL sur Debian
Pour ceux qui ne connaissent pas, Let's Encrypt fourni, gratuitement, des certificats SSL reconnus par les navigateurs (une petite révolution).
Le SSL, qu'est-ce que c'est ? C'est un protocole permettant de sécuriser les échanges sur internet, que ce soit via les protocoles http, ftp ou encore pop (pour les e-mails). Ce protocole ne se nomme plus SSL (Secure Sockets Layer) mais TLS (Transport Layer Security) depuis 1999 (merci wikipedia ^^). Le protocole TLS est le successeur du protocole SSL (ce dernier n'étant plus utilisé) mais le terme SSL est resté.
Pour expliquer rapidement comment ça fonctionne, le SSL utilise une paire de clé (publique et privée) et une autorité de confiance :
- Le navigateur se connecte au serveur et demande une connexion SSL
- Le serveur envoi un certificat SSL, contenant sa clé publique et sa signature numérique chiffrée, au navigateur
- Le navigateur essaye de déchiffrer la signature avec les clés publiques des autorités de confiance intégrés au navigateur (VeriSign, Visa, etc)
- S'il y arrive, ça prouve que le certificat a été émis par une autorité de confiance, tout va bien. S'il n'y arrive pas il va déchiffrer la signature avec la clé publique fournie avec le certificat et va alerter l'utilisateur que ce certificat n'a pas été émis par une autorité de confiance et n'est donc pas sûr (on parle de certficat auto-émis).
- Une fois le certificat validé, le navigateur va échanger des données chiffrées avec le serveur en utilisant des suites cryptographiques (voir plus de détails ici).
La révolution amenée par Let's Encrypt est qu'il a été reconnu comme une autorité de confiance et que, contrairement aux autres autorités de confiance, il propose des certificats gratuitement.
Toutes les commandes indiquées doivent être lancées avec l'utilisateur root (ou avec sudo)
Activez le support SSL
Tout d'abord on va activer le support ssl ainsi que le mode headers dont aura besoin.
a2enmod ssl headers
Maintenant que ça c'est fait, on va installer le client Certbot qui est utilisé pour générer les certificats SSL Let's Encrypt.
Installation de Certbot
Il va nous falloir installer la version présente dans les dépôts backports de Debian. Pour ajouter ces dépôts, ouvrez le fichier
/etc/apt/sources.list
Ajoutez la ligne suivante au fichier
deb http://ftp.debian.org/debian bullseye-backports main
Mettez à jour les dépôts
aptitude update
On va maintenant pouvoir installer Certbot
aptitude install certbot -t bullseye-backports
Maintenant que nous avons tout ce qu’il nous faut, passons à la création.
Création du certificat SSL sans wildcard
Si vous avez besoin de créer un certificat wildcard (c’est-à-dire valide pour tous les sous-domaines également), passez au chapitre suivant.
Commencez par créer un fichier lets_encrypt.ini (vous pouvez le nommer comme vous voulez) et mettez-y les lignes suivantes dedans :
authenticator = standalone renew-by-default = True agree-tos=true email = postmaster@votredomaine.fr domains=www.domaine.fr,domaine.fr rsa-key-size = 4096
Ceci va être le fichier de config du client Certbot que l’on va lancer pour générer nos certificats SSL. Si vous vous demandez à quoi correspondent ces lignes :
- authenticator = standalone : indique qu’on va utiliser un serveur web temporaire pour authentifier nos noms de domaine
- renew-by-default = True : indique qu’on va renouveler des certificats existants
- agree-tos=true : permet d’accepter les conditions d’utilisation de Certbot
- email : c’est l’adresse sur laquelle seront envoyés les messages de renouvellement. Il vous faut la modifier
- domains : indique les domaines pour lesquels vous voulez renouveler les certificats. Il vous faudra les modifier pour mettre vos noms de domaine
- rsa-key-size = 4096 : permet de générer un certificat avec une clé RSA de 4096 bits et non pas de 2048 bits comme c’est le cas par défaut.
Le client Certbot a besoin d'utiliser le port 80. Il vous faudra donc arrêter Apache le temps de l'opération.
On va donc commencer par couper Apache
systemctl stop apache2.service
Il ne reste plus qu’à générer notre certificat. Pensez à modifier l’emplacement du fichier ini si vous ne l’avez pas mis dans le home de l’utilisateur root
certbot certonly --config /root/lets_encrypt.ini
Les certificats vont être créés dans le répertoire
/etc/letsencrypt/live/domaine.fr/
Si tout c'est bien passé, vous devriez avoir le message suivant
IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at /etc/letsencrypt/live/www.domaine.fr/fullchain.pem. Your cert will expire on 2016-05-15. 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
Si vous allez dans le répertoire /etc/letsencrypt/live/domaine.fr/, vous trouverez 4 fichiers :
- privkey.pem qui est la clé privée SSL
- cert.pem qui est le certificat SSL. (n'est utilisé que par les versions d'Apache inférieure à 2.4.8)
- chain.pem qui contient des certificats intermédiaires (n'est utilisé que par les versions d'Apache inférieure à 2.4.8)
- fullchain.pem qui est une concaténation des fichiers cert.pem et chain.pem (n'est utilisé que par les versions d'Apache supérieure ou égale à 2.4.8)
Création d’un certificat wildcard
Let’s Encrypt permet également de générer des certificats wildcard. Les wildcard sont des certificats qui sont valables pour l’ensemble des sous-domaines.
Avec la méthode du chapitre précédent, il faut lister tous les sous-domaines lors de la création du certificat SSL. Du coup si on crée un nouveau sous-domaine 3 jours après avoir généré le certificat SSL il faut le regénérer en incluant le nouveau sous-domaine. Avec les certificats wildcard, plus besoin.
Pour vérifier que le nom de domaine pour lequel vous souhaitez générer un certificat vous appartient, Let’s Encrypt propose 2 types de challenges :
- http-01 : il envoie une requête http
- dns-01 : il demande la création d’un enregistrement TXT sur la zone du domaine
Dans le chapitre précédent, on a utilisé le challenge http-01, le plus simple à mettre en place. Mais pour les certificats wildcard, seul le challenge dns-01 est accepté. Je ne traiterai dans ce chapitre que le cas du serveur DNS auto-hébergé, dans mon cas Bind. Certbot possède d’autres plugins permettant de compléter le challenge dns-01 dans le cas où votre dns est géré par ovh, ghandi, etc, mais je ne les ai jamais testés.
Il faut donc impérativement que vous ayez suivi la page IP dynamique et DNS sur la mise en place de dns dynamique. Seuls les chapitres 1 et 2 de cet article, qui traitent de la création de la clé et de la configuration de Bind, sont indispensables. L’autre partie traite de la mise à jour de l’adresse ip, qui n’est pas utile pour les wildcard. Par contre, il ne vous faudra pas utiliser “update-policy”comme indiqué au début du chapitre 2, mais “allow-update” comme indiqué à la fin du chapitre 2 (que vous utilisiez ISPConfig ou non).
Il va également nous falloir installer un plugin pour mettre à jour notre zone DNS
aptitude install python3-certbot-dns-rfc2136
Créez un fichier ini, lets_encrypt.ini par exemple (vous pouvez l’appeler comme vous voulez). Voici ce qu’il doit y avoir dedans :
authenticator = dns-rfc2136 renew-by-default = True agree-tos=true email = toto@domaine.fr domains=domaine.fr,*.domaine.fr rsa-key-size = 4096 server = https://acme-v02.api.letsencrypt.org/directory dns-rfc2136-credentials = /root/dns.ini
Expliquons toutes ces lignes :
- authenticator = dns-rfc2136 : indique qu’on va utiliser le plugin dns-rfc2136. Il utilisera automatiquement le challenge dns-01 également
- renew-by-default = True : accepte automatiquement le renouvellement du certificat
- agree-tos=true : accepte automatiquement les conditions d’utilisation
- email = toto@domaine.fr : indique l’adresse mail sur laquelle sera envoyé les mails d’information de renouvellement
- rsa-key-size = 4096 : force la création d’un certificat avec une clé 4096 bits (2048 bits par défaut)
- server = https://acme-v02.api.letsencrypt.org/directory : force l’utilisation de la dernière API de Let’s Encrypt nécessaire aux wildcard
- dns-rfc2136-credentials = /root/dns.ini : indique le chemin du fichier ini du plugin dns-rfc2136
Et voici ce que doit contenir le fichier dns.ini (pareil, vous pouvez l’appeler comme vous voulez) :
# Target DNS server dns_rfc2136_server = IP du serveur DNS # Target DNS port dns_rfc2136_port = 53 # TSIG key name dns_rfc2136_name = nom de la clé # TSIG key secret dns_rfc2136_secret = contenu de la clé # TSIG key algorithm dns_rfc2136_algorithm = HMAC-SHA512
Si votre serveur DNS et votre serveur web (Apache ou Nginx) sont sur le même serveur, il vous faudra mettre 127.0.0.1 pour l’IP. Le nom de clé correspond au nom que vous avez donné à la clé dans le fichier named.conf.keys. Le contenu correspond au code secret de la clé. Si vous avez utilisé un algorithme autre que le HMAC-SHA512, pensez à modifier cette partie.
Comme ce fichier ini contient le code secret de la clé, chose assez sensible, on va modifier les droits de ce fichier
chmod 600 dns.ini
Comme j’ai mis la clé dans le home de l’utilisateur root, lui seul pourra lire ce fichier.
Il ne reste plus qu’à générer notre certificat. Pensez à modifier l’emplacement du fichier ini si vous ne l’avez pas mis dans le home de l’utilisateur root
certbot certonly --config /root/lets_encrypt.ini
Les certificats vont être créés dans le répertoire
/etc/letsencrypt/live/nomdedomaine/
Une chose à savoir, les certificats créés par certbot ne sont accessibles qu'à l'utilisateur root. Les programmes comme Apache2, Postfix, PureFTP, etc sont exécutés par l'utilisateur root et auront donc accès à ces certificats mais il y peut y avoir d'autres programmes exécuté par un autre utilisateur ayant besoin de ces certificats (Collabora Online par exemple). Le meilleur moyen de donner accès à ces programmes aux certficats est d'utiliser https://fr.wikipedia.org/wiki/Access_Control_List. Commencez par installer le paquet acl :
sudo aptitude install acl
Ensuite tapez les commandes suivantes (remplacez utilisateur par votre utilisateur devant accéder aux certificats et nomdedomaine.fr par votre nom de domaine) :
sudo setfacl -m u:utilisateur:rX /etc/letsencrypt/{live,archive} sudo setfacl -R -m u:utilisateur:rX /etc/letsencrypt/{live,archive}/nomdedomaine.fr
La première commande donne accès à l'utilisateur “utilisateur” aux répertoires /etc/letsencrypt/live/ et /etc/letsencrypt/archive mais pas à leurs sous-dossiers. La seconde commande donne accès à l'utilisateur “utilisateur” aux fichiers se trouvant dans les répertoire /etc/letsencrypt/live/nomdedomaine.fr et /etc/letsencrypt/archive/nomdedomaine.fr.
L'attribution des droits se fait en 2 fois car vous pouvez avoir les certificats de plusieurs noms de domaine dans vos dossiers live et archive mais ne vouloir donner l'accès qu'à un en particulier. Cette modification des droits est perdue après un redémarrage, donc pensez à ajouter un cron pour relancer ces commandes à chaque démarrage.
Configuration d’Apache
Maintenant, il nous faut modifier les fichiers de config d'Apache pour intégrer le certificat SSL qui se trouve dans le répertoire /etc/letsencrypt/live/www.domaine.fr/.
Dans votre fichier de configuration Apache pour votre nom de domaine, ajoutez les lignes suivantes :
<VirtualHost *:443> ServerName domaine.fr ServerAlias www.domaine.fr DocumentRoot /var/www/domaine.fr ErrorLog /var/log/apache2/error.example.com.log CustomLog /var/log/apache2/access.example.com.log combined <Directory /var/www/domaine.fr> Options -Indexes +FollowSymLinks +MultiViews AllowOverride none Require all granted </Directory> SSLEngine on SSLCertificateFile /etc/letsencrypt/live/www.domaine.fr/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/www.domaine.fr/privkey.pem Header always set Strict-Transport-Security "max-age=15768000" </VirtualHost> # intermediate configuration SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 SSLHonorCipherOrder off SSLSessionTickets off SSLUseStapling On SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
Petite explication rapide de ces lignes :
- SSLEngine on : active le SSL
- SSLCertificateFile : indique le chemin du certificat SSL.
- SSLCertificateKeyFile : indique le chemin vers la clé privée.
- Header always set Strict-Transport-Security (HSTS) : sert à indiquer que les connexions doivent se faire en utilisant le https.
- SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 : interdit l'utilisation des versions du protocole TLS/SSL SSLv3, TLSv1 et TLSv1.1 qui sont de vieilles versions comportant des failles de sécurité. Seules les versions TLSv1.2 et TLSv1.3 sont acceptées.
- SSLCipherSuite : indique les suites cryptographiques qui peuvent être utilisées (des failles de sécurités ont été trouvées sur certaines suites).
- SSLHonorCipherOrder : on indique que l'ordre des suites cryptographiques à utiliser est imposé par le client
- SSLSessionTickets : on désactive les tickets de session TLS. Si cette fonction est activée sans redémarrer le serveur selon une périodicité appropriée (par exemple quotidiennement), cela compromet le niveau de confidentialité.
- SSLUseStapling on et SSLStaplingCache : permet de vérifier les certificats SSL plus rapidement.
Il faut également que toutes les connexions http (port 80) soient redirigées vers https (port 443) car le HSTS ne fonctionne pas sur tous les navigateurs.
Dans votre fichier de configuration Apache2 il faut rajouter ces lignes après <VirtualHost *:80> :
Redirect / https://domaine.fr/
Redémarrez Apache et c'est tout bon.
Si jamais vous utilisez plusieurs fichiers de configurations, c'est mon cas vu que j'ai pas mal de sous-domaines, il peut être plus simple de rentrer les paramètres SSL dans le fichier de configuration SSL d'Apache2. Ça permet d'appliquer ces options à tous les fichiers de configuration ayant le SSL d'activé.
Ouvrez le fichier de configuration SSL :
sudo nano /etc/apache2/mods-enabled/ssl.conf
Les configurations indiquées dans ce fichier seront appliquées à tous les vhosts. Avant la balise “IfModule”, ajoutez les lignes suivantes :
# intermediate configuration SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 SSLHonorCipherOrder off SSLSessionTickets off SSLUseStapling On SSLStaplingCache "shmcb:logs/ssl_stapling(32768)" # HTTP Strict Transport Security (mod_headers is required) (63072000 seconds) Header always set Strict-Transport-Security "max-age=63072000"
On redémarre Apache :
sudo systemctl restart apache2
Il existe un site qui teste et note les connexions SSL. C'est https://www.ssllabs.com/ssltest/index.html. Avec ce qu'on a modifié, vous devriez avoir A+ en note.
Automatisation du renouvellement des certificats Let’s Encrypt
Les certificats Let’s Encrypt étant valable 3 mois, c’est vite chiant de devoir les renouveler à la main tous les 3 mois. On va donc voir comment automatiser tout ça.
Créez un fichier script_letsencrypt.sh (vous pouvez le nommer comme vous voulez).
Si vous utilisez un certificat standard (pas de wildcard), collez les lignes suivantes dedans (pensez à modifier le chemin des différents fichiers)
#!/bin/sh #Vérification de l'expiration du certificat SSL de domaine.fr" if ! openssl x509 -checkend 86400 -in /etc/letsencrypt/live/www.domaine.fr/fullchain.pem; #Si le certificat expire d'ici 24h (86400 secondes) then systemctl stop apache2.service certbot certonly --config /root/lets_encrypt.ini systemctl start apache2.service fi
Si vous utilisez un certificat wildcard, collez les lignes suivantes (pensez à modifier le chemin des différents fichiers)
#!/bin/sh #Vérification de l'expiration du certificat SSL de domaine.fr" if ! openssl x509 -checkend 86400 -in /etc/letsencrypt/live/www.domaine.fr/fullchain.pem; #Si le certificat expire d'ici 24h (86400 secondes) then certbot certonly --config /root/lets_encrypt.ini systemctl restart apache2.service rm /etc/bind/pri.domaine.fr*jnl systemctl restart bind9 fi
Petite explication sur ce script :
- Le “if ! openssl x509 -checkend 86400” va permettre de vérifier la date d’expiration du certificat SSL d’ici un temps donné (ici 86400 secondes, soit 24h) et n'exécuter le script que si le certificat expire dans moins de 24h.
- Ensuite, dans le cas où on utilise le challenge http-01 (certificats sans wildcard ), le script va arrêter Apache avec la commande
systemctl stop apache2.service
- Le client Certbot est exécuté avec le fichier de config lets_encrypt.ini grâce à la commande
certbot certonly --config /root/lets_encrypt.ini
- Une fois le certificat renouvelé, le script va relancer/redémarrer Apache avec la commande
systemctl start apache2.service
ou
systemctl restart apache2.service
- Puis, dans le cas où on utilise le challenge dns-01 (certificat wildcard), on supprime le fichier journal de Bind9 et on le relance (ce fichier peut provoquer des erreurs) avec les commandes
rm /etc/bind/pri.domaine.fr*jnl systemctl restart bind9
Pensez à rendre ce fichier exécutable
chmod +x /root/script_letsencrypt.sh
Il ne nous reste plus qu’à lancer ce script tous les jours via le crontab de l’utilisateur root.
Editez le crontab du root
crontab -e
Ajoutez la ligne suivante
00 01 * * * /root/script_letsencrypt.sh >> /var/log/lets-encrypt.log
Il vous faudra là aussi modifier le répertoire d’accès au script.
Le script sera lancé tous les jours à 1h du matin par l’utilisateur root et votre certificat sera renouvelé automatiquement 24h avant son expiration. Les sorties seront stockées dans le fichier /var/log/lets-encrypt.sh, ce qui vous permettra de voir si des erreurs surviennent.