Les renouvellements de certificats Let's Encrypt échouent (soudainement)

Il y a quelque temps – on ne sait pas exactement quand, mais au moins plusieurs mois – les renouvellements Let’s Encrypt ont commencé à échouer sur mon forum Discourse, après avoir fonctionné correctement pendant des années. Lorsque je l’ai remarqué il y a quelques jours, le certificat avait expiré en août 2021. Après avoir tenté quelques renouvellements manuels et redémarrages nginx, j’ai constaté que le certificat était mis à jour pour expirer il y a quelques jours seulement. Toujours pas un certificat actuel, évidemment. L’exécution manuelle de acme.sh pour forcer un renouvellement (dans le conteneur discourse) produit cette erreur (où [site] est bien sûr l’adresse de mon site) :

[site]:Verify error:Fetching http://[site]/.well-known/acme-challenge/[long alpha challenge string]: Error getting validation data

Je dois noter que le site nécessite une connexion pour tout accès utilisateur, mais cela n’a pas posé de problème pour les renouvellements de certificats SSL pendant les années de fonctionnement antérieures.

Des idées ? Merci beaucoup !

MISE À JOUR : Tester la vérification avec wget renvoie un 404. Cependant, je ne sais pas où ces données sont configurées dans nginx pour Discourse dans un conteneur et comment elles se rapportent au nginx associé qui proxy outside du conteneur.

2 « J'aime »

Si cela date de quelques mois, est-ce lié à ceci :

2 « J'aime »

Salut. Cela ne devrait pas être lié, car ce problème entraînerait le rejet des certificats par le navigateur avec des erreurs différentes, et non des erreurs d’expiration comme dans mon cas. Il semble que Let’s Encrypt ne puisse soudainement plus s’authentifier auprès de Discourse pour délivrer de nouveaux certificats. Merci.

2 « J'aime »

Pas si la première expiration était en août. Elle aurait dû être renouvelée après cela.

4 « J'aime »

Pour une application non mise à jour après juin, il aurait pu y avoir ce problème : Letsencrypt certificate failure to renew - #11 by pfaffman

pas sûr si c’est ce que vous cherchez mais : discourse_docker/templates/web.letsencrypt.ssl.template.yml at main · discourse/discourse_docker · GitHub

2 « J'aime »

Bonjour. Ceux-ci ne semblent pas s’appliquer. Je vois une erreur 404, pas les autres erreurs, les builds ont été mis à jour tout au long, et ce modèle de GitHub est bien la version déjà installée dans mon installation. Merci !

3 « J'aime »

Utilisez-vous Cloudflare avec le nuage orange ou un autre proxy inverse ?

2 « J'aime »

Négatif. Hébergé localement sur Ubuntu 18.04 en utilisant l’installation Docker par défaut.

3 « J'aime »

L’exécution manuelle de la tâche cron (dans le conteneur) pour le renouvellement des certificats échoue toujours de la même manière. La tentative d’obtention de :

http://[site]/.well-known/acme-challenge/[challenge-string]

échoue avec « Erreur lors de l’obtention des données de validation. »

2 « J'aime »

N’étant pas familier avec le processus, pourrait-il s’attendre à ce que le conteneur soit dans un état qui n’est pas le cas lorsque ce script est exécuté seul ? Par exemple, il s’attend peut-être à ce qu’un autre cron job se produise d’abord, ce qui prépare nginx à autoriser l’accès à une telle URL.

Avez-vous essayé de reconstruire ? (Ce qui tentera d’obtenir un nouveau certificat dans le processus.)

Vous mentionnez qu’il est hébergé localement. Pouvez-vous accéder à l’instance depuis l’extérieur de votre réseau en utilisant le nom de domaine ?

2 « J'aime »

Salut, oui plusieurs reconstructions. Aucun changement. J’utilise Let’s Encrypt sur un tas de sites non-Discourse et ils se renouvellent tous sans problème. Oui, je peux y accéder depuis un site externe et j’ai testé avec wget, le résultat est 404. Question : Où se trouve exactement l’arborescence html de nginx dans ce cas, la partie qui contiendrait (ou devrait contenir) le répertoire .well-known ? Je n’ai pas réussi à le trouver. Merci.

2 « J'aime »

Je n’ai pas trouvé de tâche cron, juste un script de niveau d’exécution dans /etc/runit/1.d/letsencrypt. Il semble que ce script démarre une nouvelle instance de nginx avec une configuration qui inclut ceci :

location ~ /.well-known {
  root /var/www/discourse/public;
  allow all;
}

Je pense que cela signifie que le chemin serait /var/www/discourse/public/acme-challenge, bien qu’il puisse être créé avant le défi, puis supprimé après.

Si c’est le script que vous avez essayé d’exécuter manuellement, avez-vous arrêté nginx au préalable ? L’instance que le script essaie de démarrer tentera d’écouter sur le port 80, donc je soupçonne que cela échouerait si nginx est déjà en cours d’exécution pour Discourse.

2 « J'aime »

Je pense que je vois le problème. Mais je ne sais pas comment le résoudre. Il semble que toutes les tentatives d’accès au forum sur le port https 80 soient (comme prévu) redirigées vers le port https 443. C’est exact. Mais cela signifie que lorsque Let’s Encrypt tente de valider pour le renouvellement, cela échoue, car le certificat actuel a expiré. Je peux voir la redirection avec wget. La question est donc : comment désactiver temporairement la redirection afin que Let’s Encrypt puisse valider et m’obtenir un nouveau certificat non expiré ? Une complication supplémentaire possible est que la redirection est un 301 permanent. Merci.

2 « J'aime »

Cette redirection se trouve dans /etc/nginx/conf.d/discourse.conf et ne sera pas utilisée lorsque nginx sera arrêté, puis démarré avec la configuration mentionnée dans mon précédent message.

Je crains de ne pas être très familier avec le fonctionnement de la mise à niveau automatique, je ne suis donc pas sûr de la méthode appropriée pour renouveler pendant que le conteneur est en cours d’exécution. En théorie, le simple arrêt et redémarrage du conteneur devrait entraîner son renouvellement, mais puisque vous avez dit qu’une reconstruction ne l’avait pas fait, cela ne le fera probablement pas non plus.

acme.sh a des options comme --renew-all, mais je ne suis pas sûr des autres options nécessaires pour qu’il fasse ce qu’il faut ici. Ce qui suit pourrait être tout ce dont vous avez besoin, mais je ne peux pas le dire avec certitude.

LE_WORKING_DIR="/shared/letsencrypt" /root/acme.sh/acme.sh --renew-all
2 « J'aime »

Et en effet, cela permet à Let’s Encrypt d’entrer sans la redirection, mais apparemment le fichier qu’il recherche n’existe pas, donc finalement le même échec de vérification.

2 « J'aime »

J’ai le même problème. Quelqu’un a-t-il trouvé une procédure claire pour corriger le problème ?

2 « J'aime »

J’utilise maintenant ceci pour essayer d’obtenir le certificat avec succès. Il semble que le jeton de validation EST récupéré par curl, mais acme.sh déclare TOUJOURS un échec de validation à chaque fois ! Donc toujours en panne.

« /shared/letsencrypt »/acme.sh --renew-all --force --insecure --home « /shared/letsencrypt » --debug

2 « J'aime »

Salut @L30110 :slightly_smiling_face:

Je suis l’un des habitués de la communauté Let’s Encrypt. J’ai été envoyé par @JimPas pour examiner ce fil de discussion, ce que je ferai dès mon retour de déjeuner.

3 « J'aime »

Avec de nombreux clients ACME (comme acme.sh) lorsque nginx est spécifié comme méthode d’authentification, le fichier du défi http-01 est créé dans un répertoire spécifique basé sur une exception/redirection dans la configuration du serveur nginx plutôt que directement dans la structure de répertoire .well-known/acme-challenge du répertoire webroot. Souvent, cette redirection n’existe que temporairement pendant la durée de la vérification du défi, tout comme les fichiers de défi eux-mêmes.

Par conséquent :


Une considération judicieuse. Un script de renouvellement correctement écrit devrait rendre inutile l’arrêt de nginx. Typiquement, nginx est utilisé pour servir les fichiers de défi, puis quelque chose comme nginx -s reload est utilisé pour recharger gracieusement le serveur web/proxy une fois le nouveau certificat acquis.


Non. :clin d’oeil:

Selon Challenge Types - Let's Encrypt :

Notre implémentation du défi HTTP-01 suit les redirections, jusqu’à 10 redirections de profondeur. Elle n’accepte que les redirections vers “http:” ou “https:”, et uniquement vers les ports 80 ou 443. Elle n’accepte pas les redirections vers des adresses IP. Lorsqu’elle est redirigée vers une URL HTTPS, elle ne valide pas les certificats (puisque ce défi est destiné à amorcer des certificats valides, il peut rencontrer des certificats auto-signés ou expirés en cours de route).


Généralement, lorsque nous rencontrons ce genre de problèmes, l’un des éléments suivants est généralement le coupable :

  • Un pare-feu n’autorise pas le trafic vers le serveur web/proxy qui sert les fichiers de défi.
  • Un routeur/proxy est mal mappé/configuré de telle sorte que la requête de vérification du défi de Boulder (le serveur CA Let’s Encrypt) tente de récupérer les fichiers auprès d’un serveur web ou d’un répertoire incorrect.
  • Un certain type de réécriture/redirection (par exemple, les fichiers .htaccess dans Apache) interfère avec la capacité du serveur web/proxy à servir les fichiers de défi depuis le bon emplacement.
  • Utilisation de ports non standard, généralement avec un mappage incorrect.
  • Le conteneur exécutant le client ACME crée les fichiers de défi dans un endroit où le serveur web/proxy (par exemple, nginx) ne les sert pas. Lorsque docker est impliqué, c’est presque toujours le problème.
2 « J'aime »

Salut. Ainsi, parmi les différents éléments que vous avez énumérés, plusieurs ne s’appliquent clairement pas dans mon cas. Ce n’est pas un problème de pare-feu – je peux accéder manuellement au jeton avec wget ou curl depuis 1) l’intérieur de l’application Docker Discourse, 2) depuis l’extérieur du conteneur Docker sur le système hôte, et 3) un système connexe.

Dans ces cas manuels, j’obtiens bien le contenu du jeton à l’emplacement attendu, en supposant que --ignore ou -k soit spécifié pour passer outre le certificat expiré lorsque Discourse redirige automatiquement vers https.

Je n’ai modifié aucun aspect de la configuration nginx créée par Discourse, ni à l’intérieur ni à l’extérieur du conteneur Docker Discourse. Je n’exécute aucune copie de nginx, et Apache fonctionne sur des ports complètement différents pour un usage local uniquement. Il est à noter que tout cela fonctionnait bien depuis plus de deux ans, avec des renouvellements de certificats de routine et aucun autre changement d’application – c’est une machine très stable.

Aucun port inhabituel.

Puisque je peux obtenir le contenu du jeton manuellement, je ne vois pas comment des emplacements incorrects pourraient être impliqués. SAUF…

Je n’arrêtais pas manuellement nginx pour mes tests. Je l’ai fait maintenant, et il n’y a pas eu de différence significative – mêmes erreurs de acme.sh (actuellement erreur 56 à nouveau). Lorsque nginx est arrêté depuis l’intérieur du conteneur, je vois une instance runsv nginx sur l’hôte, mais elle n’a pas de processus worker ou cache. Lorsque je redémarre nginx dans le conteneur, les processus worker et cache réapparaissent sur l’hôte avec le runsv nginx qui était resté. Les commandes sv start/stop nginx à l’intérieur du conteneur donnent les confirmations attendues de ces actions.

Mais il y a autre chose mentionné ci-dessus qui pourrait être préoccupant. Et je ne comprends pas pourquoi cela deviendrait soudainement un problème étant donné la durée pendant laquelle les choses ont fonctionné jusqu’à présent.

L’adresse IP statique du forum utilisée depuis l’extérieur de mes réseaux locaux n’est pas utilisable par cette machine pour se connecter aux propres services de cette machine, en raison des complexités de la manière dont les adresses IP statiques sont fournies par le FAI. J’ai régulièrement utilisé des entrées dans /etc/hosts pour fournir des adresses IP de réseau local pour ces noms. Ainsi, lorsque je teste avec curl sur la même machine (à l’intérieur ou à l’extérieur du conteneur, ils ont tous deux l’ajout /etc/hosts pour le forum), le test utilise une adresse IP différente (et locale) que celle qui serait utilisée par un site externe la recherchant via DNS. Y a-t-il un moyen que cela puisse être pertinent ? Merci.

2 « J'aime »