ARTICLES
Automatiser Let's Encrypt
J’ai déjà parlé de l’infra que j’ai déployée dans un autre article, là je vais me focus sur le process que j’ai mis en place pour avoir du SSL sur tous mes projets.
Pour rappel, j’ai un reverse proxy sur lequel pointent tous mes noms de domaine et qui va rediriger les requêtes vers le bon backend selon le domaine demandé. Pourquoi un reverse proxy ?
- Centralisation des certificats et de la conf SSL (plus simple)
- Utilisation du cache et load-balancing si besoin (plus performant)
- Renvoie une erreur pas trop dégeu si le backend derrière est tout pété (ça arrive plus souvent qu’un backend se vautre suite à une prod foirée que le reverse proxy ne se vautre, vu que lui normalement il bouge pas souvent)
Parce que j’aime bien la cryptographie et que je ne vois pas pourquoi je n’en profiterais pas (ça coûte pas plus cher), j’ai décidé de chiffrer tous mes noms de domaine. Bon on est d’accord, sans Let’s Encrypt (que j’abrègerai LE dans la suite de cet article), je ne pourrais pas faire tout ça sans débourser quelques (dizaines) d’euros. Et surtout, je ne pourrais pas faire tout ça en full auto.
Bon, pour rappel pour les gens qui débutent : Let’s Encrypt, c’est une nouvelle autorité de certification. Pour les gens qui débutent vraiment beaucoup, une autorité de certification, c’est un organisme qui délivre des certificats cryptographiques (communément appelés “certificats SSL”). Quand vous allez sur un site en HTTPS (soit HTTP Sécurisé), le serveur vous présente un certificat pour prouver que c’est bien le bon serveur qui vous parle, et ce certificat est signé par une autorité de certification. Votre navigateur Web (Firefox, Chrome, …) embarque avec lui un paquet d’infos concernant toutes les autorités de certification qui lui permettent de vérifier qu’effectivement, le certificat que vous envoie le serveur a bien été signé par l’autorité en question et que donc vous pouvez lui faire confiance. Vous êtes donc sûr de parler au bon serveur.
Pour signer un certificat, l’autorité de certification a besoin de vérifier que vous (qui demandez un certificat pour exemple.fr) êtes bien en possesion des machines qui gèrent ce nom de domaine exemple.fr (sinon n’importe qui pourrait se faire signer un certificat pour google.com). Pour ce faire, plusieurs méthodes sont mises en place par les autorités, mais en général, elles vous demandent de mettre une certaine info dans les zones DNS du nom de domaine demandé, puis elle vérifient que vous avez bien mis ces infos (ce qui prouve que vous gérez la zone DNS et donc que ça vous appartient bien). Une fois que l’autorité a validé que vous êtes bien le proprio du domaine, elle vous signe votre certificat pour (en général) un an et ça vous coûte entre 0 € (chez StartSSL) et beaucoup plus.
Puis est arrivé LE qui a décidé de permettre à tout le monde d’avoir son certificat SSL signé gratos, sans forcément passer par une validation DNS (qui est assez relou - surtout étant donné que les certificats LE ne sont valables que 3 mois et que donc il va falloir se taper au moins 4 vérifications par an au lieu d’une seule).
Tout d’abord, plusieurs précisions sur mes besoins :
- La validation doit être en HTTP (pas en DNS), j’ai pas non plus envie de passer 1000 ans à tout configurer
- La validation doit pouvoir traverser mon reverse proxy (c’est pas mon reverse proxy qui va gérer mes clés mais une autre machine derrière)
En gros, voilà comment ça va se passer :
- Je run un script sur ma machine qui gère les clés SSL
- Cette machine fait la requête à LE
- LE fait la validation HTTP sur le domaine demandé
- Mon reverse proxy forward cette requête à la machine qui gère les clés SSL
- Le script répond à la requête et effectue la validation de LE
- LE signe mon certificat
- (bon ça c’est un autre sujet mais : Ansible push la clé sur mon reverse proxy)
Pour gérer toute la communication avec LE, je n’ai pas utilisé le client officiel car il ne permet pas (actuellement) de faire une validation HTTP en mode “standalone” (standalone = un serveur HTTP est spawné sur la machine qui va répondre à la vérification LE). Enfin si, en quelque sorte : il fait une validation HTTPS. Sauf que quand mon reverse proxy se prend une requête en HTTPS, il présente un certif qu’il possède (en l’occurrence, le premier qu’il trouve) avant de forward la requête. Du coup, LE ne se retrouve pas avec le certif qu’il attend (le certif que le script a généré) mais avec un certif pour un autre domaine et du coup ça marche pas. Donc j’utilise le client acme.sh, dispo ici : GitHub que j’ai patché par contre parce que sinon on est obligé de spawner le client HTTP sur le port 80 et moi j’avais pas super envie d’ouvrir le port 80 de ma machine qui stocke mes clés SSL. Bref.
Ensuite il suffit simplement de mettre la bonne conf pour le reverse proxy Nginx :
1server {
2 listen 80;
3
4 # On répond à tous les noms de domaines
5 server_name _;
6
7 # Le challenge HTTP va taper sur des fichiers dans ce dossier
8 location /.well-known/acme-challenge/ {
9 proxy_set_header Host $host;
10 proxy_set_header X-Real-IP $remote_addr;
11 proxy_set_header X-Real-Port $remote_port;
12 proxy_set_header X-Real-Server-Protocol $server_protocol;
13 proxy_pass http://mon.serveur.ssl:8080;
14 }
15}Et de lancer le bousin :
1./acme.sh --issue --standalone --standalonePort 8080 -d mondomaine.com --keylength ec-384Et hop ! Toutes les requêtes qui tapent sur mondomaine.com/.well-known/acme-challenge/truc vont partir sur mon.serveur.ssl:8080 (port sur lequel écoute acme.sh). Du coup la validation se fait, le certif est signé et je me retrouve avec un certificat tout neuf.
Vous noterez 2 choses :
- J’utilise des clés elliptiques (ec-384, qui correspond en réalité à secp384r1, je ferai un article un de ces 4 sur comment comprendre cet incompréhensible bordel cryptographique)
- Ça c’est pas forcément visible, mais je recréé une nouvelle clé client à chaque fois (je ne réutilise pas mes clés de compte). Je pourrais, mais vu que tout est recréé automatiquement à chaque fois, je ne vois pas encore l’intérêt de m’embêter avec la gestion des credentials du compte
Bon personnellement, je n’appelle pas directement acme.sh, je passe par un autre petit script qui va créer un ptit dossier temporaire (avec son nom qui dépend de la date, comme ça on a un genre de versionning du pauvre), va placer dedans toutes les clés et les certifs et qui va ensuite créér des liens symboliques (comme ça on tape sur un seul fichier qui va pointer sur le bon fichier derrière selon le mois).
Plus qu’à mettre ça en cron tous les 2 mois (un certif est valide 3 mois, comme ça on prend un pied de pilote d’un mois) et on est peinard pour un bon moment !