Outils pour utilisateurs

Outils du site


article:linux:certificat_let_s_encrypt_wildcard_avec_challenge_dns

Certificat Let's Encrypt wildcard avec challenge DNS

L'article décrit comment obtenir un certificat Let's Encrypt

  • Indépendant d'un serveur web (nginx, apache, …): Afin de pouvoir réutiliser les certificats librement et sur plusieurs services
  • Wildcard (*.mondomain.tld) et domaine de base: Ainsi pas besoin de rejouer la certification à chaque nouveau service
  • Challenge par DNS intermédiaire supportant API: Pour 2 raisons :
    • Que notre serveur de base fournisse une API ou pas, on ne s'en préoccupe pas
    • La clé API utilisé sur notre DNS intermédiaire ne couvre QUE le l'entrée DNS de certification. En cas de fuite c'est limité à celui-ci

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.

Préparation du système

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

Configuration et automatisation des entrées DNS

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 :

  • Créer un projet (Nom libre)
  • Dans la partie DNS du projet, vous ajoutez votre nom de domaine
    • Note: Il indiquera “It looks like your domain is pointing to different nameservers.”, ceci est normal et nous lui déléguerons uniquement le sous-domaine _acme-challenge utile à Let's Encrypt
  • Créer une zone type *TXT* nommé “_acme-challenge”, avec une valeur libre et un TTL de 60 secondes
  • Dans la partie Security > API tokens, vous aurez besoin d'une clé API avec accès en écriture (Read/Write)

Fichier de configuration

On va réunir la configuration utilisé dans les scripts dans un fichier .env

.env
DOMAIN="mondomain.tld"
TOKEN="MON-TOKEN-A-REMPLACER"

Mise à jour automatique du sous-domaine chez Hetzner

Ce script sera appelé par Certbot pour mettre à jour le challenge chez Hetzner.

/opt/certbot/hook-hetzner.sh
#!/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"
            }
        ]
    }
}

Nettoyage du sous-domaine chez Hetzner

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 :

hook-hetzner.cleanup.sh
#!/usr/bin/env bash
 
curl "https://api.hetzner.cloud/v1/zones/${DOMAIN}/rrsets/_acme-challenge/TXT" \
  --request DELETE \
  --header "Authorization: Bearer ${TOKEN}"

Configuration du domaine principal

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"

Automatisation 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 :

certbot-renew.sh
#!/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

Sécurisation du répertoire

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 .

Crontab

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.

Conclusion

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.

article/linux/certificat_let_s_encrypt_wildcard_avec_challenge_dns.txt · Dernière modification : de Gary

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki

GH3.BE WIKI 2026