Title: Yet another tutorial about Let's Encrypt Author: Sebastien Badia Category: Tech Tags: https, letsencrypt Date: 2016-03-07 20:00 ![Logo Let's Encrypt](//letsencrypt.org/images/letsencrypt-logo-horizontal.svg) Voilà 1 million de certificats [Let's Encrypt](//letsencrypt.org/) délivrés (les certificats délivrés par [Let's Encrypt](https://crt.sh/?Identity=%25&iCAID=7395)); La littérature sur le sujet ne manque pas, mais je vais quand même en apporter une de plus, pour la partie compilation :-) Pour rappel, Let's Encrypt, est un projet porté par l' « Internet Security Research Group » (ISRG), qui vise à automatiser, rendre accessible à tous, de manière ouverte et gratuite, des certificats SSL ## Génération Nous allons ici, ne pas utiliser le client officiel; principalement parce qu'il intègre trop de fonctionnalités (serveur web intégré, …), mais aussi parce qu'il ne répond pas vraiment aux besoins de cet article (arborescence flexible, serveur nginx, …; et que c'est plus facile à lire un script de 200 lignes qu'un énorme client…) Le client que nous allons utiliser est donc le [acme-tiny](https://github.com/diafygi/acme-tiny) (écrit en python). Lors de mes pérégrinations sur la toile (oui ok ça fait un peu vieillot, j'aurais pu dire « alors que je surfais sur le web », … :-D), je suis tombé sur ce script [letsencrypt.sh](https://github.com/lukas2511/letsencrypt.sh). Le coté FQDN à un endroit, et le rechargement lorsque les AltNames changent sont des features appréciables. Après c'est du bash, donc comme vous voulez :) ### Création des certificats De mon coté, j'ai opté pour une arborescence de ce type (certificats générés dans un répertoire dédié, avec un utilisateur dédié). ``` $ mkdir -p /etc/letsencrypt/{certs,challenges,csr,pem,private} /etc/letsencrypt/ ├── certs ├── challenges ├── csr ├── pem └── private ``` Il suffit donc alors de : * Créer une clé pour le domaine en question * Créer une demande de signature (CSR) en y incluant les noms DNS alternatifs * Lancer le client (qui va se charger de créer un challenge) et signer si le challenge est ok. * Télécharger et concaténer le certificat obtenu avec le certificat intermédiaire de Let's Encrypt Le challenge sert ici à ce que let's encrypt vérifie que nous contrôlons bien le domaine pour lequel nous demandons un certificat. Il faut d'ailleurs ajouter à la configuration du serveur web, l'endroit où seront stockés ces challenges. ```nginx location /.well-known/acme-challenge/ { alias /etc/letsencrypt/challenges/gitoyen.net/; try_files $uri =404; } ``` Côté certificat intermédiaire, ils sont disponibles sur le site de [Let's Encrypt](https://letsencrypt.org/certificates/). Bon donc du coup ça donne cela dans un mini-script (pas super beau…) ```bash #!/bin/bash # bootstrap letsencrypt account=$1 cert=$2 dns=$3 mkdir -p /etc/letsencrypt/{certs,challenges,csr,pem,private} pushd /etc/letsencrypt if [[ ! -f "./private/${account}.key" ]]; then openssl genrsa 4096 > "./private/${account}.key" fi if [[ ! -f ./pem/intermediate.pem ]]; then wget -O - https://letsencrypt.org/certs/lets-encrypt-x1-cross-signed.pem > ./pem/intermediate.pem fi echo "##### ${cert} #####" if [[ ! -f "./private/${cert}.key" ]]; then openssl genrsa 4096 > "./private/${cert}.key" fi mkdir -p "./challenges/${cert}" openssl req -new -sha256 -key "./private/${cert}.key" -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=%s" "${dns}")) > "./csr/${cert}.csr" acme_tiny.py --account-key "./private/${account}.key" --csr "./csr/${cert}.csr" --acme-dir "/etc/letsencrypt/challenges/${cert}/" > "./certs/${cert}.crt" cat "./certs/${cert}.crt" ./pem/intermediate.pem > "./pem/${cert}.pem" popd ``` Qu'il suffit de lancer comme ceci (pour l'exemple je génère un certificate gitoyen.net avec comme domaines gérés gitoyen.net et www.gitoyen.net (oui de la pub au passage)) ```bash $ bash bootstrap-letsencrypt.sh gitoyen gitoyen.net 'DNS:gitoyen.net,DNS:www.gitoyen.net' ##### gitoyen.net ##### Parsing account key... Parsing CSR... Registering account... Already registered! Verifying www.gitoyen.net... www.gitoyen.net verified! Verifying gitoyen.net... gitoyen.net verified! Signing certificate... Certificate signed! ``` Plutôt cool non ? Il suffit alors d'adapter la configuration du serveur web, pour prendre en compte ces certificats. Et passer tout en HTTPS ! (dans notre cas, c'est un nginx en reverse proxy qui gère le SSL et forward en HTTP sur une IP privée). ```nginx # Gitoyen server { server_name gitoyen.net planet.gitoyen.net; rewrite ^(.*) https://$host$1 permanent; } # Gitoyen SSL # DNS:gitoyen.net,DNS:planet.gitoyen.net server { listen 443; ssl on; client_max_body_size 20M; server_name gitoyen.net www.gitoyen.net; ssl_certificate /etc/letsencrypt/pem/gitoyen.net.pem; ssl_certificate_key /etc/letsencrypt/private/gitoyen.net.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA; ssl_session_cache shared:SSL:50m; ssl_prefer_server_ciphers on; ssl_dhparam /etc/letsencrypt/dh4096.pem; ssl_ecdh_curve secp384r1; add_header Strict-Transport-Security max-age=15768000; resolver 127.0.0.1; location /.well-known/acme-challenge/ { alias /etc/letsencrypt/challenges/gitoyen.net/; try_files $uri =404; } location / { proxy_pass http://10.41.1.143; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-Ssl on; } } ``` Pour la génération du dhparams (4096 bits c'est bien) ```bash openssl dhparam -out dh4096.pem 4096 ``` Et vous voila donc avec une super config et du HTTPS automatisé, on peut aller faire un tour sur [imirhil](https://tls.imirhil.fr/https/gitoyen.net) pour un check de la conf et bien sûr pour pouvoir briller en société… ## Supervision Bon c'est bien beau, mais avec tout ça les certificats sont valables que 90j (oui mort à CertPatrol :-() mais pour le coup il va donc falloir superviser tout cela. ### Via Certificate Monitor Si vous utilisez le sympa [certificatemonitor](https://certificatemonitor.org/) ([code source](https://github.com/RaymiiOrg/certificate-expiry-monitor)) de Raymii, il suffit alors d'ajouter vos FQDN dans l'interface. Et ainsi recevoir une notification avant l'expiration de vos certificats. ### Via Checkmk Sinon si vous avez déjà une bonne veille supervision à base de nagios ou checkmk ou compatible, il suffit d'ajouter les checks via le script `check_http`. Dans la conf `main.mk` de checkmk: ```checkmk # /etc/checkmk/main.mk legacy_checks = [ ## Gitoyen ( ( "check-certificate!gitoyen.net", "Certificate Gitoyen - Letsencrypt", True), ['baloo.gitoyen.net']), ( ( "check-certificate!www.gitoyen.net", "Certificate www Gitoyen - Letsencrypt", True), ['baloo.gitoyen.net']), ] ``` Et ne pas oublier la `check_command` associée (on émet un warning lorsque que le certificat expire à J-30 et un critical à J-7) ```nagios # /etc/checkmk/conf.d/commands_extra.mk define command { command_name check-certificate command_line $USER1$/check_http -H $ARG1$ -C 30,7 --sni } ``` ## Renouvellement Il existe des scripts de renouvellement automatique: * [letsencrypt-renew](https://github.com/octopuce/octopuce-goodies/tree/master/letsencrypt-renew) * [letsencrypt.sh](https://github.com/lukas2511/letsencrypt.sh) Mais je ne sais pas encore quoi en penser, pour le moment je réagis lorsque que le check passe en warning avec ce mini script bash. ```bash #!/bin/bash account='gitoyen' certs='gitoyen.net www.gitoyen.net' pushd /etc/letsencrypt for cert in $certs do echo "##### ${cert} #####" acme_tiny.py --account-key ./private/gitoyen.key --csr ./csr/${cert}.csr --acme-dir /etc/letsencrypt/challenges/${cert}/ > ./certs/${cert}.crt cat ./certs/${cert}.crt ./pem/intermediate.pem > ./pem/${cert}.pem done popd systemctl restart nginx ``` ## Chocolat Même si le modèle des CA est bancal, il n'y a plus de raison maintenant de ne pas proposer du HTTPS partout !