Certificat wildcard avec Let's Encrypt via la vérification DNS

Publié le 16 min de lecture

Un certificat wildcard ne peut pas être vérifié par le serveur web, il passe obligatoirement par un enregistrement TXT dans le DNS. Ce guide montre la voie manuelle et la voie automatique sur Debian 13, Debian 12, Ubuntu 24.04 et Ubuntu 22.04, ainsi que les erreurs sur lesquelles cela bloque réellement.

Un certificat wildcard couvre tous les noms d'un même niveau : shop.MonDomaine.fr, mail.MonDomaine.fr, client-4711.MonDomaine.fr, y compris ceux qui n'existent pas encore aujourd'hui. C'est précisément pour cette raison que la méthode habituelle, celle qui passe par le serveur web, ne fonctionne plus ici. Ce guide présente les deux voies praticables, la manuelle et l'automatique, et insiste sur les endroits où cela dérape en pratique.

Tout ce qui suit a été vérifié sur Debian 13, Debian 12, Ubuntu 24.04 LTS et Ubuntu 22.04 LTS. Ces quatre systèmes divergent plus nettement au niveau de Certbot que la plupart des guides ne l'admettent, un tableau plus bas détaille ces écarts.

Pourquoi un certificat wildcard passe obligatoirement par le DNS

Let's Encrypt connaît trois méthodes de vérification. Deux d'entre elles sont exclues pour les wildcards :

  • HTTP-01 dépose un fichier sous http://name/.well-known/acme-challenge/token. Or, pour *.MonDomaine.fr, il n'existe aucun nom unique sous lequel ce fichier pourrait se trouver. L'autorité de certification devrait interroger une infinité de noms d'hôtes.
  • TLS-ALPN-01 souffre du même problème, elle vérifie elle aussi un hôte concret sur le port 443.
  • DNS-01 vérifie un enregistrement TXT sous _acme-challenge.MonDomaine.fr. Celui qui peut créer cet enregistrement contrôle la zone, et donc aussi n'importe quel nom en dessous. C'est la seule preuve qui convienne à une étoile.

Concrètement, cela signifie que --apache, --nginx, --webroot et --standalone sont inutilisables pour les wildcards. Qui tente quand même reçoit ce message :

Client with the currently selected authenticator does not support any
combination of challenges that will satisfy the CA. You may need to use an
authenticator plugin that can do challenges over DNS.

Ce n'est pas une erreur dans votre configuration, c'est la bonne réponse à une demande impossible. Pour le cas classique avec quelques noms fixes, la voie du serveur web reste la bonne, nous la décrivons dans l'article consacré au certificat SSL gratuit avec Certbot.

Deuxième point que presque tous les guides passent sous silence : un certificat wildcard couvre uniquement les noms situés un niveau plus bas. *.MonDomaine.fr vaut pour shop.MonDomaine.fr, mais ni pour MonDomaine.fr lui-même, ni pour a.b.MonDomaine.fr. Le domaine nu doit être demandé en plus, ce qui a des conséquences sur l'enregistrement TXT, voir plus bas.

Prérequis et disponibilité des paquets selon la distribution

Il vous faut un accès root en SSH, un domaine dont vous gérez la zone, et Certbot. Un serveur web actif n'est pas nécessaire pour l'émission, le port 80 n'a pas besoin d'être ouvert. C'est un effet secondaire agréable : vous pouvez aussi émettre un certificat pour un service qui n'est pas exposé à Internet.

Installez Certbot et les outils DNS :

apt update
apt install -y certbot bind9-dnsutils

Le paquet bind9-dnsutils fournit dig. Sur les quatre systèmes, l'ancien nom dnsutils n'est plus qu'un paquet de transition qui pointe vers bind9-dnsutils. Vérifiez quelle version de Certbot vous avez obtenue :

certbot --version

Les différences sont considérables, et ce sont elles qui déterminent quelle voie vous est réellement ouverte :

SystèmeCertbotPlugins présents dans les dépôts
Debian 134.0.0cloudflare, desec, google, infomaniak, rfc2136, route53
Debian 122.1.0cloudflare, digitalocean, dnsimple, gandi, gehirn, google, linode, ovh, rfc2136, route53, sakuracloud
Ubuntu 24.042.9.0cloudflare, digitalocean, dnsimple, gandi, gehirn, google, infomaniak, linode, ovh, rfc2136, route53, sakuracloud
Ubuntu 22.041.21.0cloudflare, digitalocean, dnsimple, gandi, gehirn, google, linode, ovh, rfc2136, route53, sakuracloud

Voilà la surprise : Debian 13 a sorti de son archive la plupart des plugins DNS. Qui travaillait sous Debian 12 avec python3-certbot-dns-ovh ou python3-certbot-dns-linode et passe à Debian 13 ne trouve tout simplement plus le paquet. Un apt upgrade qui franchit la frontière entre deux distributions le supprime, et le renouvellement s'interrompt sans que personne ne s'en aperçoive. Vérifiez ce point avant un changement de distribution.

Pour savoir quels plugins sont réellement chargés sur votre système :

certbot plugins

La voie manuelle avec un enregistrement TXT

La voie manuelle ne demande aucun accès API et fonctionne chez n'importe quel fournisseur DNS. Elle convient pour tester et pour les zones auxquelles vous ne touchez de toute façon que rarement. Elle a un inconvénient sérieux, nous y venons tout de suite.

Abaissez au préalable le TTL du futur enregistrement TXT dans votre zone, entre 60 et 300 secondes. Cela ne coûte rien et vous épargnera de l'attente par la suite. Ensuite :

certbot certonly --manual --preferred-challenges dns \
  --cert-name mondomaine.fr \
  -d "*.MonDomaine.fr" -d MonDomaine.fr

Les guillemets droits autour de "*.MonDomaine.fr" sont obligatoires. Sans eux, le shell remplace l'étoile par les noms de fichiers du répertoire courant, et Certbot demande des certificats pour vos fichiers. --cert-name est lui aussi vivement recommandé : sinon Certbot dérive le nom du dossier de stockage du premier nom de la liste, et personne n'a envie de chercher un répertoire contenant une étoile.

Certbot s'interrompt et affiche quelque chose comme :

Please deploy a DNS TXT record under the name:
_acme-challenge.MonDomaine.fr.

with the following value:

gfj9Xq...Rg85nM

C'est ici que la plupart des tentatives échouent. Vous avez demandé deux noms, l'étoile et le domaine nu. Ce sont deux vérifications distinctes, et toutes deux aboutissent sur le même nom d'enregistrement _acme-challenge.MonDomaine.fr, avec deux valeurs différentes. Certbot le dit d'ailleurs :

This must be set up in addition to the previous challenges; do not remove, replace, or undo the previous challenge tasks yet. Note that you might be asked to create multiple distinct TXT records with the same name. This is permitted by DNS standards.

Or beaucoup d'interfaces DNS ne proposent qu'un seul champ de saisie pour un même nom et remplacent la première valeur par la seconde. Il ne reste alors qu'une seule valeur dans la zone, l'une des deux vérifications échoue, et le message d'erreur ne mentionne pourtant qu'un seul nom. Si votre interface n'autorise pas deux enregistrements TXT portant le même nom, c'est rédhibitoire pour la voie manuelle. Utilisez dans ce cas la délégation par CNAME, décrite plus bas.

Vérifier d'abord, appuyer sur Entrée ensuite

Certbot attend votre confirmation. N'appuyez pas tout de suite sur Entrée. Ouvrez une deuxième session SSH et interrogez d'abord le serveur de noms faisant autorité, pas le résolveur local :

dig +short NS MonDomaine.fr
dig +short TXT _acme-challenge.MonDomaine.fr @ns1.fournisseur.example
dig +short TXT _acme-challenge.MonDomaine.fr @1.1.1.1
dig +short TXT _acme-challenge.MonDomaine.fr @8.8.8.8

N'appuyez sur Entrée qu'une fois que les deux valeurs apparaissent dans une même requête, et cela auprès de plusieurs résolveurs indépendants les uns des autres. Une sortie vide signifie que l'enregistrement n'est pas encore là. Voici ce que cela donne sur un domaine sans enregistrement, la sortie reste vide :

dig +short TXT _acme-challenge.example.com @1.1.1.1

Pourquoi ce détour par le serveur faisant autorité ? Si vous interrogez _acme-challenge avant d'avoir créé l'enregistrement, votre résolveur mémorise cette inexistence pendant toute la durée du TTL négatif issu de l'enregistrement SOA, souvent une heure. Vous ne voyez alors longtemps rien du tout alors que l'enregistrement est en place depuis un moment, et vous cherchez l'erreur au mauvais endroit. Le serveur faisant autorité, lui, n'a pas ce cache.

Le hic de la voie manuelle

Un certificat émis avec --manual et sans script ne se renouvelle jamais tout seul. Lors du prochain passage automatique, le journal indique :

An authentication script must be provided with --manual-auth-hook when using
the manual plugin non-interactively.

Certbot saute ce certificat et poursuit avec les autres, le code de retour reste souvent discret. Vous vous en apercevrez le jour où le navigateur protestera. Prévoyez donc fermement de refaire la même procédure à la main tous les 60 à 90 jours, ou passez à l'une des voies automatiques.

La voie automatique avec un plugin du fournisseur

Si votre fournisseur DNS dispose d'une interface de programmation et qu'il existe un plugin Certbot correspondant, Certbot crée lui-même l'enregistrement TXT, attend, fait vérifier, puis le supprime. C'est la voie que vous voulez pour les systèmes en production. Exemple avec Cloudflare :

apt install -y python3-certbot-dns-cloudflare

Déposez les identifiants en dehors du répertoire web et protégez-les immédiatement :

mkdir -p /root/.secrets/certbot
chmod 700 /root/.secrets/certbot

Le fichier /root/.secrets/certbot/cloudflare.ini ne contient qu'une seule ligne :

dns_cloudflare_api_token = VotreTokenIci

Ensuite, impérativement :

chmod 600 /root/.secrets/certbot/cloudflare.ini

Sans cela, Certbot avertit à chaque passage que les droits sont trop larges. Utilisez un token restreint disposant du droit d'écriture sur les enregistrements DNS, pas la clé globale du compte. La clé globale fonctionne encore, mais elle peut tout faire sur votre compte, et elle se retrouve ensuite en clair sur le serveur. Un token peut être limité à une seule zone et révoqué séparément en cas d'incident.

L'émission :

certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/certbot/cloudflare.ini \
  --dns-cloudflare-propagation-seconds 60 \
  --cert-name mondomaine.fr \
  -d "*.MonDomaine.fr" -d MonDomaine.fr

La valeur de --propagation-seconds correspond au temps d'attente entre la création de l'enregistrement et la requête de vérification. La valeur par défaut est de 10 secondes, bien trop juste pour beaucoup de zones. 60 secondes constituent un bon point de départ, 120 chez les fournisseurs lents. Ce seul chiffre explique une grande partie des renouvellements qui échouent sporadiquement, ceux que personne d'autre n'arrive à reproduire.

Pour les autres fournisseurs, le paquet s'appelle python3-certbot-dns-<fournisseur>, et les paramètres suivent le même schéma. Reportez-vous au tableau ci-dessus pour savoir si votre fournisseur est seulement présent sur votre distribution.

Quand le paquet adéquat manque

Le réflexe immédiat est pip install certbot-dns-quelquechose. Sur Debian 12, Debian 13 et Ubuntu 24.04, cela se termine ainsi :

error: externally-managed-environment

× This environment is externally managed

C'est voulu, ce n'est pas un défaut. Sur Ubuntu 22.04, la même commande passe encore, mais elle mélange alors des paquets pip aux paquets système, et au prochain apt upgrade les versions de Certbot et du plugin ne correspondent plus. Ne forcez pas le passage avec --break-system-packages.

La porte de sortie propre est la version Snap de Certbot, qui embarque les plugins et se maintient à jour toute seule. Retirez auparavant le paquet de la distribution, pour éviter que deux Certbot gèrent le même répertoire :

apt remove -y certbot
snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot
snap set certbot trust-plugin-with-root=ok
snap install certbot-dns-cloudflare

Important : les données existantes sous /etc/letsencrypt/ sont conservées, le snap les reprend. En revanche, le timer systemd s'appelle désormais snap.certbot.renew.timer et non plus certbot.timer. Qui passe à côté se retrouve avec deux timers ou aucun.

Sans plugin de fournisseur : rfc2136 et délégation CNAME

Deux méthodes fonctionnent quel que soit votre fournisseur DNS.

rfc2136 est la voie standard pour la mise à jour DNS dynamique avec une clé TSIG. Elle fonctionne avec BIND, Knot et PowerDNS, et le paquet est présent sur les quatre systèmes :

apt install -y python3-certbot-dns-rfc2136

Si vous exploitez votre DNS vous-même, c'est la solution la plus robuste, car elle ne dépend ni d'un service tiers ni d'une interface HTTP.

La délégation CNAME est la réponse la plus élégante à deux problèmes à la fois. Vous créez une seule fois, dans votre zone principale, un enregistrement qui ne changera plus jamais :

_acme-challenge.MonDomaine.fr.  CNAME  MonDomaine.fr.acme.une-autre-zone.fr.

Let's Encrypt suit les chaînes de CNAME quand il cherche l'enregistrement TXT. Celui-ci est donc créé dans la zone cible, et le serveur n'a besoin de droits d'écriture que sur cette zone. Cela règle plusieurs points d'un coup :

  • Les identifiants stockés sur le serveur web ne peuvent pas modifier votre zone principale. Un serveur web compromis ne peut pas détourner vos enregistrements MX.
  • La zone cible a le droit de porter plusieurs valeurs TXT en même temps, même si l'interface de votre fournisseur principal en est incapable.
  • La zone cible peut avoir un TTL très bas sans que la zone principale en pâtisse.

Le CNAME lui-même n'est plus jamais touché, il peut donc avoir un TTL élevé. On le vérifie avec :

dig +short CNAME _acme-challenge.MonDomaine.fr @1.1.1.1

Automatiser le renouvellement

Le paquet Certbot installe déjà un timer qui s'exécute deux fois par jour. Un certificat n'est renouvelé que lorsque c'est nécessaire :

systemctl list-timers certbot.timer

S'il ne tourne pas :

systemctl enable --now certbot.timer

Sur les quatre systèmes, le paquet apporte deux déclencheurs : /lib/systemd/system/certbot.timer et, en plus, /etc/cron.d/certbot. Le fichier cron vérifie lui-même au démarrage si le timer est actif, et ne fait alors rien, il n'y a donc pas de double renouvellement. Vous n'avez pas besoin d'un cron de renouvellement à vous, ce serait le troisième déclencheur pour la même tâche.

Il existe ici une différence entre les distributions qu'il vaut mieux connaître. Certbot jusqu'à la version 3 renouvelle quand il reste moins de 30 jours de validité. Certbot 4.0, donc la version présente dans Debian 13, renouvelle en revanche quand il reste un tiers de la durée de vie. Avec les 90 jours habituels aujourd'hui, les deux aboutissent au même moment. Dès que Let's Encrypt délivrera des certificats de durée plus courte, les deux versions se comporteront différemment, et seule la nouvelle s'adaptera automatiquement.

Testez le déroulement à blanc. Rien n'est émis et aucun quota n'est consommé :

certbot renew --dry-run

Certbot n'inscrit pas un certificat wildcard dans la configuration du serveur web, vous devez le faire vous-même, une fois. Pour que le serveur web charge effectivement le nouveau certificat après chaque renouvellement, créez un hook de déploiement. Écrivez-le sous forme de fichier, pas de paramètre :

mkdir -p /etc/letsencrypt/renewal-hooks/deploy

Le contenu de /etc/letsencrypt/renewal-hooks/deploy/reload-webserver.sh :

#!/bin/sh
systemctl reload nginx

Puis rendez-le exécutable :

chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-webserver.sh

Les scripts placés dans ce répertoire s'exécutent pour chaque certificat renouvelé. Le paramètre --deploy-hook, lui, n'est écrit que dans le fichier de renouvellement des certificats traités à cet instant précis. Les certificats ajoutés plus tard ne l'ont donc pas, et cela ne se remarque que des mois après.

Un avertissement au sujet des identifiants API : si le token est révoqué chez le fournisseur ou s'il expire, le renouvellement échoue sans que rien ne paraisse cassé. Le certificat est encore valable, après tout. Ce n'est que 30 jours plus tard que le service tombe. Vérifiez donc de temps en temps que vos certificats rajeunissent bel et bien, et ne comptez pas sur les avertissements d'expiration envoyés par mail.

Comment savoir que cela a vraiment marché

Le fait que la commande se soit terminée sans erreur ne prouve rien. Ces trois vérifications, si. D'abord la vue d'ensemble :

certbot certificates

Sous Domains, les deux noms doivent apparaître, *.MonDomaine.fr et MonDomaine.fr. Si l'étoile manque, vous avez obtenu un certificat ordinaire sans vous en rendre compte.

Ensuite, un coup d'œil dans le fichier lui-même :

openssl x509 -noout -text -in /etc/letsencrypt/live/mondomaine.fr/fullchain.pem | grep -A1 "Subject Alternative Name"

DNS:*.MonDomaine.fr doit y figurer. Le champ déterminant est Subject Alternative Name, pas le Common Name, que les navigateurs modernes n'évaluent plus du tout.

Troisièmement, et c'est là la véritable preuve, interrogez un nom que vous venez tout juste d'inventer :

echo | openssl s_client -servername test-1234.MonDomaine.fr -connect MonDomaine.fr:443 2>/dev/null | openssl x509 -noout -subject -dates

Si cela renvoie un certificat valide et aucun avertissement, le wildcard prend effectivement le relais. Vous avez terminé à ce moment-là seulement.

Erreurs fréquentes, au mot près

  • « DNS problem: NXDOMAIN looking up TXT for _acme-challenge.MonDomaine.fr » : l'enregistrement n'existe pas, il n'est pas encore propagé, ou vous l'avez créé chez le mauvais fournisseur. Le cas le plus fréquent : le domaine est certes enregistré chez le fournisseur A, mais les serveurs de noms pointent vers le fournisseur B, et l'enregistrement se trouve chez A. Seul compte ce que renvoie dig +short NS MonDomaine.fr.
  • « Incorrect TXT record ... found at _acme-challenge.MonDomaine.fr » : une valeur est bien là, mais ce n'est pas la bonne. Typique après une interruption, quand l'ancienne valeur est restée dans la zone, ou quand l'interface a écrit la deuxième valeur par-dessus la première. Supprimez les anciens enregistrements _acme-challenge et recommencez.
  • « DNS problem: SERVFAIL looking up TXT ... the domain's nameservers may be malfunctioning » : presque toujours une signature DNSSEC défectueuse, par exemple après un changement de fournisseur où l'ancien enregistrement DS est resté chez le registre. Réparez cela d'abord, sinon toute émission échouera.
  • « CAA record for MonDomaine.fr prevents issuance » : le piège que l'on néglige souvent. Pour les wildcards, l'autorité de certification évalue d'abord issuewild. Qui a posé issue "letsencrypt.org" mais garde à côté un issuewild ";" obtient des certificats ordinaires, mais pas de wildcard. À vérifier avec dig +short CAA MonDomaine.fr.
  • « too many certificates (5) already issued for this exact set of identifiers » : cinq certificats sont autorisés par période de sept jours pour la même combinaison de noms. Testez donc avec --dry-run ou contre l'environnement de test avec --test-cert. Le blocage expire de lui-même, il est impossible de le lever.
  • L'interface affiche l'enregistrement TXT, mais dig non : certains fournisseurs exigent que les modifications de la zone soient explicitement publiées. Un enregistrement sauvegardé n'est pas pour autant un enregistrement actif.
  • Le nom de l'enregistrement se retrouve en double : certaines interfaces ajoutent le domaine automatiquement. N'y saisissez donc que _acme-challenge, sinon vous obtenez _acme-challenge.MonDomaine.fr.MonDomaine.fr. Un dig sur le nom complet le révèle immédiatement.

Perspective : DNS-PERSIST-01

Let's Encrypt travaille à une nouvelle méthode de vérification nommée DNS-PERSIST-01. Au lieu de publier un token frais à chaque renouvellement, vous déposez une seule fois un enregistrement permanent qui autorise un compte ACME précis à émettre. Ensuite, le serveur n'a plus du tout besoin d'un accès en écriture au DNS pour renouveler. Pour les wildcards, ce serait un gain de sécurité net.

D'après le calendrier de Let's Encrypt, l'environnement de test était prévu pour la fin du premier trimestre 2026 et la production pour le deuxième trimestre. La prise en charge par Certbot n'est pour l'instant pas annoncée. Ne bâtissez donc pas encore vos plans dessus, mais gardez la méthode à l'œil si vous êtes justement en train de reconstruire votre gestion de certificats.

Résumé

Un certificat wildcard passe exclusivement par la vérification DNS, parce qu'une étoile ne peut pas être prouvée via un seul serveur web. La voie manuelle avec un enregistrement TXT fonctionne partout, mais elle ne se renouvelle jamais toute seule. La voie qui passe par un plugin de fournisseur est la seule que vous pouvez laisser tourner sans surveillance, et en pratique elle échoue le plus souvent à cause d'un temps d'attente trop court ou d'identifiants devenus silencieusement invalides. Si vous ne devez retenir qu'une seule chose : vérifiez l'enregistrement TXT auprès du serveur de noms faisant autorité avant de laisser Certbot continuer, et comptez fermement sur deux enregistrements sous le même nom dès que vous demandez l'étoile plus le domaine nu.

Questions fréquentes

Pourquoi ne puis-je pas émettre un certificat wildcard via Apache ou nginx ?
Parce que la vérification par le serveur web interroge un fichier sous un nom d'hôte précis. Or un wildcard couvre un nombre quelconque de noms, y compris ceux qui n'existent pas encore. Pour les wildcards, Let's Encrypt n'accepte donc que la vérification DNS. Sinon Certbot signale : Client with the currently selected authenticator does not support any combination of challenges that will satisfy the CA.
*.MonDomaine.fr couvre-t-il aussi MonDomaine.fr lui-même ?
Non. Un wildcard ne vaut que pour les noms situés exactement un niveau en dessous. Le domaine nu doit être demandé en plus, avec un paramètre -d supplémentaire. Il ne vaut pas davantage deux niveaux plus bas, donc pas pour a.b.MonDomaine.fr.
Pourquoi ai-je besoin de deux enregistrements TXT portant le même nom ?
Parce que l'étoile et le domaine nu sont deux vérifications distinctes qui aboutissent toutes deux sur _acme-challenge.MonDomaine.fr, avec des valeurs différentes. Les deux doivent figurer dans la zone en même temps. Le standard DNS l'autorise, mais beaucoup d'interfaces de fournisseurs remplacent la première valeur par la seconde.
Comment vérifier si l'enregistrement TXT est déjà propagé ?
Avec dig contre le serveur de noms faisant autorité et, en plus, contre au moins deux résolveurs publics, par exemple dig +short TXT _acme-challenge.MonDomaine.fr @1.1.1.1. N'interrogez pas le nom avant d'avoir créé l'enregistrement, sinon votre résolveur mémorise cette inexistence pendant toute la durée du TTL négatif.
Un certificat wildcard émis manuellement se renouvelle-t-il automatiquement ?
Non. Sans script, Certbot signale lors du passage automatique : An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively, puis saute le certificat. Pour un fonctionnement sans surveillance, il vous faut un plugin DNS de votre fournisseur ou rfc2136.
Quels plugins DNS pour Certbot existe-t-il sur Debian 13 ?
Debian 13 ne fournit plus que cloudflare, desec, google, infomaniak, rfc2136 et route53. Les paquets comme python3-certbot-dns-ovh, dns-linode ou dns-digitalocean, encore présents sous Debian 12 et Ubuntu, n'y figurent plus. Vérifiez ce point avant un changement de distribution, sinon le renouvellement s'interrompt sans que vous le remarquiez.
Qu'apporte une délégation CNAME de _acme-challenge ?
Elle déplace l'enregistrement TXT dans une zone séparée. Le serveur n'a alors plus besoin de droits d'écriture sur votre zone principale, un serveur web compromis ne peut donc pas modifier vos enregistrements MX ou A. Cela résout en plus le problème des interfaces qui n'autorisent pas deux valeurs TXT sous le même nom.

Lets-Encrypt Certbot Wildcard Certificat SSL DNS DNS-01 Debian Ubuntu