Hello les gens ! j’espère que tout le monde va bien. Aujourd’hui, on reste dans la pure technique avec un système que je suis en train d’évaluer chez nous, pour utiliser et dans la mesure du possible automatiser la mise à jours de certificats SSL publiques via Let’s Encrypt et injecter ceux-ci dans nos load balancers F5/BigIP.
Pour l’instant, et après pas mal d’heures de bidouillage, de tests et de difficultés diverses, je suis enfin parvenu à mes fins grâce à des scripts Shell. Comme je suis plus habitué à plutôt travailler en Shell, je vais passer la main à un collègue, lui-même spécialiste d’Ansible, pour automatiser le tout ensuite via des playbooks. Néanmoins, en attendant, je vous partage mes découvertes et les bases sur lesquelles je me suis appuyé pour simplifier au maximum la gestion de Let’s Encrypt, tout en gardant le contrôle des certificats.
Un p’tit schéma
Voici le principe général, un schéma valant toujours mieux qu’un long discours 🙂

En pratique, c’est relativement connu et simple : j’utilise donc Nginx Proxy Manager pour centraliser la gestion des certificats, à la fois intuitive et suivie. Deux raisons motivent ce choix : la simplicité d’utilisation et le suivi automatique des renouvellements. Même si l’objectif à terme est d’automatiser l’ensemble du processus, la confiance ne doit jamais exclure le contrôle, qu’il soit humain ou technique.
J’ai ainsi développé un script qui exploite l’API REST de nos load balancers F5 pour y injecter les certificats préalablement récupérés via NPM.
Nginx Proxy Manager
Pour votre info, globalement NPM ressemble à ça, pour ceux qui ne connaissent pas :

NPM est un reverse proxy basé sur Nginx, comme peut l’être Traefik, qui permet en plus et très facilement de gérer la sécurité SSL de tous vos points d’accès web frontend. Idéal pour un petit Home Lab. Pour ce qui me concerne, je n’utilise que les fonctions de pilotages des certificat Let’s Encrypt en coordination avec OVH qui fournit une API direct avec notamment la gestion de ses domaines.
Le script shell principal
Voici le script shell en question (franchement , ça ne s’invente pas … si vous n’êtes pas un spécialiste de ces environnements, je trouve cela assez compliqué chez Big IP, honnêtement) :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# !/bin/bash # ######## Récupérer un token d'authentification sur l'appliance F5 BASEURL="https://xxxxxxxxx.fqdn.fr" echo "BaseURL: $BASEURL" echo "Appel de l'API pour récupérer le token ..." TOKEN=`curl -sk -X POST "$BASEURL/mgmt/shared/authn/login" -H "Content-Type: application/json" -d '{ "username":"user-script","password":"xxxxxxxxxxx","loginProviderName": "tmos" }' | jq .token.token | tr -d '"'` ######## Récupération des certificats et clés depuis a hiérarchie de fichier NPM KEY=`./extract-file.sh www.test-domain.fr key` CERT=`./extract-file.sh www.test-domain.fr cert` CHAIN=`./extract-file.sh www.test-domain.fr chain` KEYSIZE=`stat -Lc%s $KEY` # combien d'octets pour le fichier ? KEYRANGE=$(( $KEYSIZE - 1)) CERTSIZE=`stat -Lc%s $CERT` # combien d'octets pour le fichier ? CERTRANGE=$(( $CERTSIZE - 1)) CHAINSIZE=`stat -Lc%s $CHAIN` # combien d'octets pour le fichier ? CHAINRANGE=$(( $CHAINSIZE - 1)) ######## Affichage echo "Token: $TOKEN" echo "Keyfile: $KEY ($KEYSIZE/$KEYRANGE)" echo "Certfile: $CERT ($CERTSIZE/$CERTRANGE)" echo "ChainFile: $CHAIN ($CHAINSIZE/$CHAINRANGE)" ######## Upload des fichier vers le F5 # Certificat echo "Upload du certificat ----------" curl -sk -X POST \ -H "X-F5-Auth-Token: $TOKEN" \ -H "Content-Type: application/octet-stream" \ -H "Content-Range: 0-$CERTRANGE/$CERTSIZE" \ --data-binary "@$CERT" \ "$BASEURL/mgmt/shared/file-transfer/uploads/prod-cert.pem" | jq # Chaine de certif echo "Upload de la chaine de certification ----------" curl -sk -X POST \ -H "X-F5-Auth-Token: $TOKEN" \ -H "Content-Type: application/octet-stream" \ -H "Content-Range: 0-$CHAINRANGE/$CHAINSIZE" \ --data-binary "@$CHAIN" \ "$BASEURL/mgmt/shared/file-transfer/uploads/prod-chain.pem" | jq # Clé privée echo "Upload de la clé privée ----------" curl -sk -X POST \ -H "X-F5-Auth-Token: $TOKEN" \ -H "Content-Type: application/octet-stream" \ -H "Content-Range: 0-$KEYRANGE/$KEYSIZE" \ --data-binary "@$KEY" \ "$BASEURL/mgmt/shared/file-transfer/uploads/prod-key.pem" | jq ######## Installation des fichiers uploadés dans des conteneurs F5 exploitables dans les "profiles" de production echo "----------" curl -sk -X POST \ -H "X-F5-Auth-Token: $TOKEN" \ -H "Content-Type: application/json" \ -d '{"command":"install","name":"PROD-CHAIN","from-local-file":"/var/config/rest/downloads/prod-chain.pem"}' \ "$BASEURL/mgmt/tm/sys/crypto/certkeychain" | jq echo "----------" curl -sk -X POST \ -H "X-F5-Auth-Token: $TOKEN" \ -H "Content-Type: application/json" \ -d '{"command":"install","name":"PROD-CERT","from-local-file":"/var/config/rest/downloads/prod-cert.pem"}' \ "$BASEURL/mgmt/tm/sys/crypto/cert" | jq echo "----------" curl -sk -X POST \ -H "X-F5-Auth-Token: $TOKEN" \ -H "Content-Type: application/json" \ -d '{"command":"install","name":"PROD-KEY","from-local-file":"/var/config/rest/downloads/prod-key.pem"}' \ "$BASEURL/mgmt/tm/sys/crypto/key" | jq ######## Enfin, activation des clé, certificat et chaine de certif ainsi intégrées dans dans le "profile" de production PROD-PROFILE echo "----------" curl -sk -X PATCH \ -H "X-F5-Auth-Token: $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "certKeyChain": [{"cert":"/Common/PROD-CERT","key":"/Common/PROD-KEY","chain":"/Common/PROD-CHAIN","name":"PROD-PROFILE" }] }' \ "$BASEURL/mgmt/tm/ltm/profile/client-ssl/~Common~PROD-PROFILE" | jq |
Vous noterez que le script en appelle un autre dont le role permets tout simplement d’extraire les chemins des fichiers .pem (clé, chaine, certificat) en fonction des domaines gérés sur NPM. Voici ce script :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# !/bin/bash # HOMEDIR="" CERT="" if [ "$1" == "" ] ; then echo "Donnez le nom du domain a chercher dans l'argument de la commande." exit 1 else CERT=$1 fi if [ "$2" == "" ] ; then echo "Fichier PEM souhaité (key,cert,chain)." exit 1 fi DIRLIST=`find nginx-proxy/letsencrypt/live/* -type d` trouve=1 for courant in $DIRLIST ; do extract=`openssl x509 -in $courant/cert.pem -noout -text | grep DNS | tr -d 'DNS:' | awk '{ print $1 ; }'` if [ "$extract" == "$CERT" ] ; then case $2 in "key") echo "$HOMEDIR$courant/privkey.pem" ;; "cert") echo "$HOMEDIR$courant/cert.pem" ;; "chain") echo "$HOMEDIR$courant/fullchain.pem" ;; *) echo "no known var $var" ;; esac trouve=0 break; fi done exit $trouve |
Un exemple de sortie interactive de ce script :
|
1 2 |
user@host-slt01:/datas/nginx-proxyman-stack# ./extract-file.sh www.test-domain.fr key nginx-proxy/letsencrypt/live/npm-6/privkey.pem |
j’ai également un script qui me sort la list de tous les certificats gérés par NPM, je ne l’utilise pas ici, car pas besoin, mais ça permet d’avoir une vue d’ensemble :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# !/bin/bash # HOMEDIR=`pwd` echo "HOMEDIR: $HOMEDIR" echo "------" DIRLIST=`find ./nginx-proxy/letsencrypt/live/* -type d` for courant in $DIRLIST ; do echo -n "SubjectName: " extract=`openssl x509 -in $courant/cert.pem -noout -text | grep DNS | tr -d 'DNS:' | awk '{ print $1 ; }'` echo $extract echo "Repertoire : " $HOMEDIR\/$courant echo -n "Clé privée : " $HOMEDIR\/$courant"/privkey.pem - date de maj:" date -r $courant/privkey.pem "+%Y-%m-%d" echo -n "Certificat : " $HOMEDIR\/$courant"/cert.pem - date de maj:" date -r $courant/cert.pem "+%Y-%m-%d" echo -n "FullChain : " $HOMEDIR\/$courant"/fullchain.pem - date de maj:" date -r $courant/fullchain.pem "+%Y-%m-%d" echo -n "Expiration du certificat" openssl x509 -in $courant/cert.pem -noout -text | grep "Not After" echo "------" done |
Ce qui donne ce genre de choses :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
user@host-slt01:/datas/nginx-proxyman-stack# ./certlist.sh HOMEDIR: /datas/nginx-proxyman-stack ------ SubjectName: testcert.test-domain.fr Repertoire : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-1 Clé privée : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-1/privkey.pem - date de maj:2025-09-25 Certificat : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-1/cert.pem - date de maj:2025-09-25 FullChain : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-1/fullchain.pem - date de maj:2025-09-25 Expiration du certificat Not After : Dec 24 15:03:49 2025 GMT ------ SubjectName: testcert2.fqdn.fr Repertoire : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-2 Clé privée : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-2/privkey.pem - date de maj:2025-09-25 Certificat : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-2/cert.pem - date de maj:2025-09-25 FullChain : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-2/fullchain.pem - date de maj:2025-09-25 Expiration du certificat Not After : Dec 24 15:02:50 2025 GMT ------ SubjectName: www.test-domain.fr Repertoire : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-6 Clé privée : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-6/privkey.pem - date de maj:2025-11-19 Certificat : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-6/cert.pem - date de maj:2025-11-19 FullChain : /datas/nginx-proxyman-stack/./nginx-proxy/letsencrypt/live/npm-6/fullchain.pem - date de maj:2025-11-19 Expiration du certificat Not After : Feb 17 07:44:32 2026 GMT ------ user@host-slt01:/datas/nginx-proxyman-stack# |
Vous voyez, c’est relativement basique : il s’agit de transformer cela en un playbook Ansible pour obtenir une version plus industrielle et entièrement automatisée. À terme, il suffira d’appuyer sur un bouton et de superviser le renouvellement des certificats une fois par semaine, via une procédure dédiée.
L’évolution souhaitée, à plus long terme, est d’automatiser intégralement le processus et de ne plus utiliser du tout Nginx Proxy Manager. Mais cela fera partie de la prochaine étape via par exemple certbot ou d’autres scripts Let’s Encrypt… mais chaque chose en son temps !
