L'article décrit comment obtenir un certificat Let's Encrypt
Le programme fournira en sortie les certificats *.mondomain.tld et mondomain.tld. On pourra faire pointer nos services directement dessus ou les fournir aux différents serveurs.
Note: Un certificat Let's Encrypt a une durée de vie de 90 jours. Il est généralement renouvellé 30 jours avant son expiration. Dans mon cas mes services redémarrent au moins une fois par semaine, je ne me préoccupe donc pas de redémarrer les services au moment du changement de certificat, ceux-ci chargerons les nouveaux certificats de manière asynchrone au moment du redémarrage.
apt install python3 python3-venv
On va créer un compte dédié à l'usage de certbot :
adduser --system --group --no-create-home --shell /usr/sbin/nologin certbot addgroup certssl-user
–system : Compte système, possède un UID/GID bas, afin de séparer des utilisateurs interactif
–group : Crée un groupe éponyme
–no-create-home : N'aura pas de home directory, inutile ici
–shell /usr/sbin/nologin : Le binaire nologin empêche d'ouvrir un shell si on s'y connect
L'utilisateur n'aura pas de mot de passe non plus, car inaccessible.
Le groupe certssl-user permettra d'autoriser qui peut consulter les certificats
Le dossier de travail sera /opt/certbot, on va créer le nécessaire à cet endroit. En l'occurence un environnement virtuel python avec le package certbot installé
# Création du répertoire de travail mkdir /opt/certbot cd /opt/certbot # Création de l'environnement virtuel python3 -m venv . bin/pip install --upgrade pip # Installation de certbot bin/pip install certbot # certbot travaillera dans le dossier letsencrypt, on lui crée ses dossiers mkdir -p letsencrypt/{conf,work,log}
La structure est maintenant faite
Le service DNS de Hetzner servira de DNS intermédiaire pour nous fournir un sous-domaine avec une API DNS. Sur le site vous devrez :
On va réunir la configuration utilisé dans les scripts dans un fichier .env
DOMAIN="mondomain.tld" TOKEN="MON-TOKEN-A-REMPLACER"
Ce script sera appelé par Certbot pour mettre à jour le challenge chez Hetzner.
#!/usr/bin/env bash curl "https://api.hetzner.cloud/v1/zones/${DOMAIN}/rrsets/_acme-challenge/TXT/actions/add_records" \ --request POST \ --header "Content-Type: application/json" \ --header "Authorization: Bearer ${TOKEN}" \ --data "{ \"ttl\": 300, \"records\": [ { \"value\": \"\\\"${CERTBOT_VALIDATION}\\\"\", \"comment\": \"Certbot Script - $(date)\" } ] }" sleep 30
On n'oublie pas de le rendre exécutable
chmod +x hook-duckdns.sh
A noter que la variable CERTBOT_VALIDATION sera une variable d'environnement passé par certbot.
Pour valider le bon fonctionnement :
export CERTBOT_VALIDATION="Exemple de challenge de Certbot" ./hook-hetzner.sh unset CERTBOT_VALIDATION
Doit retourner :
{
"action": {
"id": XXXXXXX,
"status": "running",
"command": "set_rrset_records",
"progress": 0,
"started": "2026-04-24T15:41:15Z",
"finished": null,
"error": null,
"resources": [
{
"id": XXXXXXX,
"type": "zone"
}
]
}
}
Il est recommandé (mais pas obligatoire) de nettoyer l'enregistrement DNS après validation par Let's Encrypt. Tout comme pour le script précédent, Certbot propose d'appeler un script pour le nettoyage, que voici :
#!/usr/bin/env bash curl "https://api.hetzner.cloud/v1/zones/${DOMAIN}/rrsets/_acme-challenge/TXT" \ --request DELETE \ --header "Authorization: Bearer ${TOKEN}"
Le domaine principal doit pouvoir déléguer _acme-challenger.mondomain.tld vers Hetzner, ce qui est le rôle des enregistrement de type NS.
Sur votre console Hetner, dans la partie DNS > mondomain.tld > Name servers, Hetner indique les serveurs DNS qui contient notre enregistrement, dans mon cas : hydrogen.ns.hetzner.com., helium.ns.hetzner.de., oxygen.ns.hetzner.com. (Potentiellement différent chez vous)
Chez votre registrar, vous devrez ajouter ces serveurs comme record de type NS pour le sous-domaine _acme-challenge :
_acme-challenge IN NS hydrogen.ns.hetzner.com. _acme-challenge IN NS helium.ns.hetzner.de. _acme-challenge IN NS oxygen.ns.hetzner.com.
Pour valider le bon fonctionnement :
dig _acme-challenge.mondomain.tld TXT ;; ANSWER SECTION: _acme-challenge.gh3.be. 268 IN TXT "Exemple de challenge de Certbot"
Tout est en place pour que Certbot puisse travailler. Le certificat n'étant valable que 90 jours, il est obligatoire d'automatiser son renouvellement. On créer un script également pour ceci :
#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(dirname "$0")" CONF_DIR="letsencrypt/conf/" WORK_DIR="letsencrypt/work/" LOGS_DIR="letsencrypt/log/" echo \> "$(date)": Starting renew script cd "${SCRIPT_DIR}" if [[ ! -f ".env" ]]; then echo "Fichier .env manquant" exit 1 fi set -a source .env set +a echo \> Renew \[*.\]${DOMAIN} bin/certbot --config-dir "${CONF_DIR}" --work-dir "${WORK_DIR}" --logs-dir "${LOGS_DIR}" \ certonly --verbose --non-interactive --agree-tos --manual \ --preferred-challenges dns --domains "${DOMAIN}" --domains "*.${DOMAIN}" \ --manual-auth-hook ./hook-hetzner.sh \ --manual-cleanup-hook ./hook-hetzner-cleanup.sh echo \> Publish new *.pem files to directory cert/${DOMAIN} if [ ! -d "cert/${DOMAIN}" ]; then mkdir -p "cert/${DOMAIN}" fi cp -rfL "${CONF_DIR}/live/${DOMAIN}/*.pem" "cert/${DOMAIN}/" chmod 0640 "cert/${DOMAIN}/*"
Après l'avoir rendu exécutable, vous pouvez le lancer.
chmod +x certbot-renew.sh ./certbot-renew.sh
Mes tests ont été fait avec un utilisateur interactif par facilité. Mettons en place la sécurité nécessaire
# L'utilisateur certbot devient propriétaire du dossier (récursif) chown -R certbot: /opt/certbot # Droit restreint sur les fichiers et dossiers cd /opt/certbot find . -type d -exec chmod 750 {} \; find . -type f -exec chmod 640 {} \; chmod 700 *.sh chmod 600 .env # Le groupe certssl-user est autorisé sur le dossier cert chown -R certbot:certssl-user cert chown certbot:certssl-user .
Je trouve qu'une fois semaine est un bon compromis. Dès qu'il restera 30 jours au certificat, il sera au maximum dans les 7 prochains jours.
Certbot ne tentera rien si le certificat est trop récent.
crontab -e -u certbot # Renouvellement certificat Let's Encrypt # Chaque dimanche à 2h14 14 2 * * 0 /opt/certbot/certbot-renew.sh > /opt/certbot/cron.log
En cas de problème, le log sera disponible dans cron.log.
Le script n'est pas parfait, n'est pas hyper sécurisé et il y a forcément moyen de faire mieux/optimisé…
MAIS, je l'utilise depuis maintenant des années (un peu amélioré pour l'article et initialement avec DuckDNS), et il ne m'a jamais fait défaut.
CEPENDANT ! Il ne dispense pas d'un monitoring de l'état du certificat SSL sur le site web où il sera utilisé avec une alerte s'il expire prochainement (- de 15 jours), signe d'un problème.