Внезапные сбои при обновлении сертификатов Let's Encrypt

Некоторое время назад — неясно, сколько именно, но по крайней мере несколько месяцев — обновления Let’s Encrypt начали давать сбой на моём форуме Discourse, хотя ранее они работали без проблем в течение многих лет. Когда я впервые заметил это несколько дней назад, сертификат уже истёк в августе 2021 года. После нескольких попыток ручного обновления и перезапуска nginx я обнаружил, что срок действия сертификата был продлён лишь на несколько дней назад. Очевидно, это всё ещё не актуальный сертификат. Ручной запуск acme.sh для принудительного обновления (внутри контейнера Discourse) выдаёт следующую ошибку (где [site] — это, конечно, адрес моего сайта):

[site]:Ошибка проверки: получение данных по адресу http://[site]/.well-known/acme-challenge/[длинная строка альфа-проверки]: ошибка получения данных для проверки

Стоит отметить, что для доступа пользователей требуется вход в систему, но ранее это не вызывало проблем при обновлении SSL-сертификатов в течение многих лет работы.

Есть ли какие-либо идеи? Большое спасибо!

ОБНОВЛЕНИЕ: Тестирование проверки с помощью wget возвращает ошибку 404. Однако я не знаю, где именно в nginx для Discourse в контейнере настроены эти данные и как они связаны с соответствующим nginx, работающим за пределами контейнера.

Если это было несколько месяцев назад, связано ли это с чем-то вроде этого:

Привет. Это не должно иметь отношения к делу, так как такая проблема привела бы к тому, что браузер отклонял бы сертификаты с другими ошибками, а не с ошибкой истечения срока действия, как в моём случае. Похоже, что Let’s Encrypt внезапно не может пройти аутентификацию в Discourse для выдачи новых сертификатов. Спасибо.

Нет, если первое истечение срока произошло в августе. После этого оно должно было продлеваться.

Для приложения, которое не обновлялось после июня, могла возникнуть эта проблема: Letsencrypt certificate failure to renew - #11 by pfaffman

Не уверен, что это то, что вы ищете, но вот ссылка: discourse_docker/templates/web.letsencrypt.ssl.template.yml at main · discourse/discourse_docker · GitHub

Привет. Похоже, это не относится к моей ситуации. Я вижу ошибку 404, а не другие ошибки. Сборки обновлялись всё это время, и тот шаблон из GitHub — это действительно версия, уже установленная в моей системе. Спасибо!

Вы используете Cloudflare с оранжевым облаком или какой-либо другой обратный прокси-сервер?

Нет. Размещено локально на Ubuntu 18.04 с использованием стандартной установки Docker.

При ручном запуске задачи cron (внутри контейнера) для обновления сертификата ошибка всегда одинакова. Попытка получить:

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

завершается с ошибкой «Ошибка получения данных для проверки».

Так как вы не знакомы с этим процессом, возможно, система ожидает, что контейнер будет находиться в определённом состоянии, которого нет при запуске этого скрипта в одиночку? Например, возможно, требуется сначала выполнить другую задачу cron, которая подготовит nginx для доступа к такому URL.

Вы пробовали выполнить пересборку? (В процессе этого будет предпринята попытка получить новый сертификат.)

Вы упоминали, что это размещено локально. Можете ли вы получить доступ к экземпляру извне вашей сети, используя доменное имя?

Привет, да, несколько пересборок. Без изменений. Я использую Let’s Encrypt на множестве сайтов, не связанных с Discourse, и у них всё обновляется без проблем. Да, я могу получить доступ с внешнего сайта, и я тестировал с помощью wget — результат 404. Вопрос: Где именно в данном случае находится дерево HTML-файлов nginx, та часть, которая должна содержать (или должна содержать) директорию .well-known? Я не смог её найти. Спасибо.

Я не смог найти задачу cron, только скрипт уровня запуска в /etc/runit/1.d/letsencrypt. Похоже, этот скрипт запускает новый экземпляр nginx с конфигурацией, которая включает следующее:

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

Думаю, это означает, что путь в итоге будет /var/www/discourse/public/acme-challenge, хотя он вполне может создаваться перед проверкой, а затем удаляться после неё.

Если это тот скрипт, который вы пытались запустить вручную, вы сначала остановили nginx? Экземпляр, который пытается запустить скрипт, будет слушать порт 80, поэтому, если nginx уже запущен для Discourse, это, скорее всего, не сработает.

Думаю, я вижу проблему, но не знаю, как её исправить. Похоже, все попытки доступа к форуму через порт 80 (как и ожидалось) перенаправляются на порт 443 по HTTPS. Верно. Но это означает, что при попытке Let’s Encrypt провести валидацию для продления сертификат не проходит проверку, так как текущий сертификат уже истёк. Я вижу это перенаправление с помощью wget. Вопрос в том, как временно отключить перенаправление, чтобы Let’s Encrypt мог провести валидацию и выдать новый, не истёкший сертификат? Дополнительная возможная сложность заключается в том, что перенаправление является постоянным (301). Спасибо.

Эта перенаправляющая запись находится в файле /etc/nginx/conf.d/discourse.conf и не будет применена, если nginx остановить, а затем запустить с конфигурацией, упомянутой в моём предыдущем сообщении.

Боюсь, я не очень хорошо знаком с тем, как работает автоматическое обновление, поэтому не уверен, какой метод будет уместен для продления сертификата во время работы контейнера. Теоретически, остановка и запуск контейнера должны привести к продлению, но раз вы сказали, что пересборка не помогла, то, вероятно, и это тоже не сработает.

У утилиты acme.sh есть опции вроде --renew-all, но я не уверен, какие ещё опции потребуются для того, чтобы она сделала всё правильно в данном случае. Возможно, вам понадобится только следующее, но я не могу сказать наверняка.

LE_WORKING_DIR="/shared/letsencrypt" /root/acme.sh/acme.sh --renew-all

Действительно, это позволяет Let’s Encrypt подключиться без перенаправления, но, по-видимому, искомый файл не существует, поэтому в итоге возникает та же ошибка проверки.

У меня та же проблема. Кто-нибудь разработал четкую процедуру для исправления этой проблемы?

Теперь я использую это, чтобы успешно получить сертификат. Похоже, что токен валидации действительно извлекается через curl, но acme.sh ВСЁ ЕЩЁ каждый раз объявляет о сбое валидации! Так что всё ещё не работает.

“/shared/letsencrypt”/acme.sh --renew-all --force --insecure --home “/shared/letsencrypt” --debug

Привет, @L30110 :slightly_smiling_face:

Я один из постоянных участников сообщества Let’s Encrypt. Меня направил @JimPas, чтобы я ознакомился с этой темой, и я сделаю это сразу после возвращения с обеда.

У многих ACME-клиентов (например, acme.sh), когда в качестве метода аутентификации указан nginx, файл http-01 challenge создаётся в определённой директории на основе исключения/перенаправления в конфигурации сервера nginx, а не непосредственно в структуре директорий .well-known/acme-challenge в корневой директории веб-сайта. Часто это перенаправление существует только временно, на протяжении проверки вызова, как и сами файлы вызова.

Следовательно:


Важное замечание. Правильно написанный скрипт обновления должен делать ненужной остановку nginx. Обычно nginx используется для обслуживания файла(ов) вызова, а затем что-то вроде nginx -s reload применяется для плавной перезагрузки веб-сервера/прокси после получения нового сертификата.


Не совсем так. :wink:

Согласно Challenge Types - Let's Encrypt :

Наша реализация HTTP-01 вызова следует перенаправлениям, до 10 уровней в глубину. Она принимает только перенаправления на «http:» или «https:» и только на порты 80 или 443. Перенаправления на IP-адреса не принимаются. При перенаправлении на HTTPS-URL проверка сертификатов не выполняется (поскольку этот вызов предназначен для инициализации действительных сертификатов, он может столкнуться с самоподписанными или истёкшими сертификатами по пути).


Обычно, когда мы сталкиваемся с подобными проблемами, виновником чаще всего является одно из следующего:

  • Брандмауэр не пропускает трафик к веб-серверу/прокси, обслуживающему файл(ы) вызова.
  • Маршрутизатор/прокси настроен или сопоставлен неправильно, так что запрос проверки вызова от Boulder (сервер CA Let’s Encrypt) пытается получить файл(ы) с неверного веб-сервера или из неверной директории.
  • Какой-то тип переписывания/перенаправления (например, файлы .htaccess в Apache) мешает веб-серверу/прокси обслуживать файлы вызова из правильного места.
  • Использование нестандартных портов, обычно с неправильным сопоставлением.
  • Контейнер, запускающий ACME-клиент, создаёт файл(ы) вызова в месте, где веб-сервер/прокси (например, nginx) их не обслуживает. При использовании Docker это почти всегда является проблемой.

Привет. Из перечисленных вами пунктов несколько явно не относятся к моему случаю. Проблема не в брандмауэре — я могу вручную получить токен с помощью wget или curl со всех трёх позиций: 1) изнутри приложения Discourse в Docker, 2) снаружи контейнера Docker на хост-системе и 3) с любой связанной системы.

В этих ручных случаях я действительно получаю содержимое токена из ожидаемого местоположения, при условии что указан параметр --ignore или -k, чтобы обойти истёкший сертификат, когда Discourse автоматически перенаправляет на HTTPS.

Я не менял никаких аспектов конфигурации nginx, созданной Discourse, ни внутри, ни вне контейнера Docker с Discourse. Я не запускаю никаких копий nginx, а Apache работает на совершенно других портах только для локального использования. Отмечу, что всё это работало безупречно уже два года, с регулярным обновлением сертификатов и без каких-либо других изменений в приложениях — это очень стабильная система.

Нет необычных портов.

Поскольку я могу вручную получить содержимое токена, я не вижу, как могли бы быть задействованы неверные пути. ЗА ИСКЛЮЧЕНИЕМ …

При проведении тестов я не останавливал nginx вручную. Теперь я это сделал, но существенной разницы нет — те же ошибки от acme.sh (сейчас снова ошибка 56). Когда nginx останавливается изнутри контейнера, я вижу на хосте процесс runsv nginx, но у него нет рабочих или кэширующих процессов. При перезапуске nginx в контейнере на хосте снова появляются рабочие и кэширующие процессы вместе с процессом runsv nginx, который оставался активным. Команды sv start/stop nginx внутри контейнера дают ожидаемые подтверждения этих действий.

Но есть ещё один момент, упомянутый выше, который может вызывать беспокойство. И я не понимаю, почему это вдруг стало проблемой, учитывая, как долго всё работало до сих пор.

Статический IP-адрес форума, используемый извне моих локальных сетей, недоступен для самой этой машины при подключении к её собственным сервисам из-за особенностей предоставления статических IP провайдером. Я регулярно использую записи в /etc/hosts, чтобы назначать локальные IP-адреса для этих имён. Таким образом, когда я тестирую с помощью curl на той же машине (как внутри, так и снаружи контейнера — в обоих случаях в /etc/hosts добавлена запись для форума), тест использует другой (локальный) IP-адрес, чем тот, который использовался бы внешним сайтом при поиске через DNS. Может ли это иметь какое-либо значение? Спасибо.