В недавней статье об SSL-сертификатах, точнее, в комментариях к ней, затронули вопрос, который похоже создает для многих головную боль: как обновлять сертификаты от Let's Encrypt? Ведь сертификаты выдаются на 3 месяца, а потом их надо обновлять.
Конечно, есть Certbot, есть софт, который умеет сам получать и обновлять сертификаты — но вопрос возник именно по тому софту, который сам не умеет (а подключить Certbot почему‑то не получается)
Когда‑то у меня тоже возник такой вопрос, и почему‑то не устроил Certbot — не помню уже точную причину. Решил проблему иначе.
Во‑первых, что именно удостоверяет удостоверяющий центр Let's Encrypt? Он просто проверяет, что у якобы владельца сайта (для простоты будем считать что у нас сайт) есть права доступа к записям DNS домена или к самому сайту на этом домене.
То есть, если вы хотите получить себе сертификат например для сайта на домене Сбербанка — то у вас должен быть либо доступ к DNS‑серверу, либо к сайту Сбербанка, иначе ничего не получится. Причем к сайту доступ все равно нужен, потому что там должен находиться приватный ключ сервера и полученный сертификат. Для большинства применений такого уровня безопасности достаточно.
Как именно потом вы будете использовать полученный для домена сертификат — это ваше дело. Главное, на момент получения‑обновления сайт на этом домене должен быть доступен извне (ну, или доступны записи в DNS). При отправке вашего запроса на сертификат Let's Encrypt проверит «секретный файл» на сервере, и если он там есть — продлит действие сертификата. Называется это все acme protocol.
Для работы с ним есть скрипт, который так и называется — 'letsencrypt.sh'.
Проект есть на Гитхабе — https://github.com/digint/letsencrypt.sh
Как заметили наблюдательные комментаторы - по этому адресу - старый вариант.
Ну ок, у меня более новый, не помню откуда взял его в 2020 году:
39655 Aug 18 2020 letsencrypt.sh
(в любом случае - речь о подходе к получению и обновлению сертификатов, скриптами)
Это по факту просто shell‑скрипт, который не требует никакой особой экзотики для работы, не висит в памяти, не требует какого‑то особого взаимодействия с ним — просто запускается, отрабатывает, и всё.
Еще потребуется установленный пакет openssl — тоже стандартная для Linux вещь.
Все остальное — shell‑скрипты, причем несложные.
Первым делом нужно создать ключ аккаунта — по этому ключу Let's Encrypt будет дальше общаться с вами. Можно просто выполнить команду, а можно создать скрипт 1.create_account.sh, чтобы не вспоминать потом заново:
#!/bin/sh
umask 0177
openssl genrsa -out account.key 4096
umask 0022
Он просто создает файл ключа. Это ключ аккаунта.
Теперь его надо зарегистрировать в Let's Encrypt - создадим скрипт 2.register.sh:
#!/bin/sh
./letsencrypt.sh register -a account.key -e you@email.there
Теперь надо получить для аккаунта thumbprint - ту самую "секретную строку" в "секретном файле" - пишем скрипт 3.thumbprint.sh:
#!/bin/sh
./letsencrypt.sh thumbprint -a account.key > th
Содержимое файла th примерно такое:
account thumbprint: dkjjsdd6jr2a6JKJH7DFjjlklIU4HdfJkkk7788HhhR
Сохраним, он нам понадобится позже.
Создаем ключ сервера. Он как раз нужен на сервере, указывается в настройках веб-сервера.
Снова скрипт, 4.serverkey.sh:
#!/bin/sh
umask 0177
openssl genrsa -out server.key 4096
umask 0022
openssl req -new -key server.key -out self-signed.csr
openssl x509 -req -in self-signed.csr -signkey server.key -out self-signed.pem -days 365
Как видите, то жесамое что и первый скрипт, просто чтобы не запутываться потом, что когда делать. Кроме того — создается самоподписанный сертификат, он понадобится только один раз.
Теперь самое время настроить веб-сервер. На примере Nginx:
vim /etc/nginx/sites-available/my-site.conf
server {
listen 80;
server_name my-site.com;
location ~ "^/\.well-known/acme-challenge/([-_a-zA-Z0-9]*)$" {
default_type text/plain;
return 200 "$1.dkjjsdd6jr2a6JKJH7DFjjlklIU4HdfJkkk7788HhhR";
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name my-site.com;
ssl_certificate /etc/ssl/my-site.com.pem;
ssl_certificate_key /etc/ssl/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location ~ "^/\.well-known/acme-challenge/([-_a-zA-Z0-9]*)$" {
default_type text/plain;
return 200 "$1.dkjjsdd6jr2a6JKJH7DFjjlklIU4HdfJkkk7788HhhR";
}
..... your site settings ....
}
Что здесь происходит: при запросе типа my-site.com/.well-known/acme-challenge/blablabla сервер возвращает строку «blablabla.ваше_секретное_слово» из файла th, полученного ранее. Так Let's Encrypt понимает, что этот сайт на этом домене вам не чужой.
Также указываются места для сертификата и ключа сервера, на этом этапе сертификат просто должен быть — именно для этого и используем самоподписанный, переименовав его как указано в конфиге. Перезапускаем Nginx:
/etc/init.d/nginx restart
Теперь у нас сайт работает по HTTPS, но с самоподписанным сертификатом. А вот теперь заменим его на сертификат от Let's Encrypt, скрипт 5.sign.sh:
#!/bin/sh
cd /etc/ssl
if [ "x$1" != "x" ] ; then
echo "OK: use $1"
./letsencrypt.sh sign -a account.key -k server.key -c ${1}.pem -P true ${1}
fi
./5.sign.sh my-site.com
Скрипт отправит запрос на сертификат для сайта my-site.com, результат сохранит в my-site.com.pem, а цепочку сертификатов в my-site.com.pem_chain.
Остается добавить цепочку в сертификат (необязательно, но целесообразно) и перезапуcтить nginx:
cat my-site.com.pem_chain >> my-site.com.pem
/etc/init.d/nginx restart
Теперь сайт работает через сертификат от Lets Encrypt. Через три месяца надо продлевать — для этого достаточно повторить шаги:
./5.sign.sh my-site.com
cat my-site.com.pem_chain >> my-site.com.pem
/etc/init.d/nginx restart
Если нужен новый сайт на том же сервере — просто поменять «my‑site.com» на другое имя — шаги с 1 по 4 делать не обязательно, только если нужен другой аккаунт и на другой машине.
Но ведь это ручная работа, нужно следить за сроком сертификата, а не хочется?
Правильно, не хочется. Пусть работает компьютер, он железный:
Пишем скрипт renew.sh:
#!/bin/sh
cd /etc/ssl
list=$(find . -name \*.pem -mtime +30)
for i in ${list} ; do
host=$(echo $i | grep -oP '(?<=/)[^/]+(?=\.pem)')
./5.step.sign ${host}
while [ $? -ne 0 ] ; do
sleep 5
./5.step.sign ${host}
done
cat ${host}.pem_chain >> ${host}.pem
done
/etc/init.d/nginx restart
И вызываем его по крону, раз в несколько дней.
Что он делает? Файлы сертификатов доменов хранятся в виде site‑name.pem, он проверяет наличие тех, которые обновлялись более 30 дней назад. выделяет имя домена, и пытается обновить для него сертификат. Иногда с первого раза это не срабатывает, если сервер перегружен — тогда он с упорством маньяка будет раз в 5 секунд пробовать снова и снова, пока не пробьется.
В итоге у нас кучка скриптов, которые понятно в какой последовательности запускать, можно создавать новые аккаунты или использовать один общий, создавать новые серверные ключи или копировать старые, получать сертификаты для разных сайтов, и автоматически их потом обновлять. И всё — обычные скрипты.
UPD:
В статье не раскрыта тема получения сертификата на поддомены для нескольких сайтов. А она тоже интересная и полезная.
В моём случае делаю просто:
./5.sign.sh sub1.my-site.com
./5.sign.sh sub2.my-site.com
и так далее.
Скрипту всё равно для какого домена получать сертификат, если там настроена отдача "секретной строки".
Комментарии (0)
MountainGoat
16.09.2025 19:05Чтобы не заниматься всем этим вручную, я использую https://github.com/linuxserver/docker-swag. Это образ для Докера, содержащий nginx и самостоятельно следящий за сертификатом. Он даже уведомит на почту, если сертификат всё таки скоро протухнет. Точнее, попросит LetsEncrypt уведомить.
Затем из него можно копировать сертификаты куда надо или просто использовать его как proxy.
Kenya-West
16.09.2025 19:05Тоже порекомендую нафаршированный веб-сервер Caddy, который, помимо автообновления SSL любым из способов, автоматически обновляет свой конфиг при появлении контейнера с лейблами
caddy
в его сети. Очень удобно, конфиг становится ненужным в явном виде, не нужно ничего хардкодить - всё происходит динамически и декларативно, запускай себе docker compose'ы сколько хочешь.В бою на пет-проектах на 20+ серверов уже полгода.
Sank0k69
16.09.2025 19:05Топ материал, он бы идеально добавил бы мою статью про SSL, о которой идет речь в начале =D
Ахуительный контент, я искренне считаю, что они прям ИДЕАЛЬНО дополняют друг-друга.
greenork
16.09.2025 19:05Зачем использовать форк возрастом с говно мамонта, когда оригинал обновляется и работает? https://github.com/dehydrated-io/dehydrated
JBFW Автор
16.09.2025 19:05Зачем обновлять то, что работает? )
Это ж не московские бордюры....
greenork
16.09.2025 19:051) В вашем форке от 2015 года не вижу использования ACME v2.
https://github.com/digint/letsencrypt.sh
2) В оригинальном скрипте
Use new ACME v2 endpoint by default
2018-03-13
https://github.com/dehydrated-io/dehydrated/releases/tag/v0.6.1
3) С ноября 2021 ACME v1 не работает.
https://community.letsencrypt.org/t/end-of-life-plan-for-acmev1/88430/2
Что там у вас работает? Тут нужна гифка с Тиньковым.
Проверять не буду, мне теперь кажется что вы накопипастили мусор из чатика с нейросеткой.JBFW Автор
16.09.2025 19:05Начнем с того, что это не мой форк. Я его просто использую.
А во-вторых, я его использую - он работает. Вот прямо пару часов назад.
Правда, я совершенно не помню откуда конкретно этот скрипт взял, это было 5 лет назад - возможно это не тот который на конкретно этом Гитхабе, а другой, правильный - но он тоже называется letsencrypt.sh. если вы так хорошо разбираетесь в форках - может быть тогда правильную ссылочку на ТОТ letsencrypt.sh дадите?
Что там у вас за отношения с гифками и нейросетками - этого не знаю.
DikSoft
16.09.2025 19:05Для ленивых есть ещё вариант - Certify The Web
Что, если он "не умеет обслуживать именно вашу программу"?
Последовательность решения такая:
Ставим утилиту на отдельный сервер.
На сервере поднимаем IIS
Утилиту нацеливаем на этот IIS
Балансировщику на входе , который что-то публикует, объясняем, что запросы на /.well-known отправлять надо на этот сервер, например , для HAProxy это примерно так:
haproxy.cfg
.. # ACME acl app-acme-path path -i /.well-known acl app-acme-path path_beg -i /.well-known .. use_backend backend_http_acme if app-acme-path .. backend backend_http_acme mode http balance source server webserver1acme 10.11.12.13:80 check # Web server 1
Утилите задаем post execution script, вся задача которого будет создать в журнале приложений событие с каким-то вашим уникальным кодом
В планировщике создаем триггер на этот уникальный код в журнале, который будет запускать ваш скрипт, занимающийся установкой полученного сертификата, куда нужно и от той учетной записи, которая имеет нужные права.
Вариант рабочий, несложный в отладке, расширяемый и достаточно стабильный.
PereslavlFoto
16.09.2025 19:05Для ленивых есть ещё вариант - Certify The Web
К сожалению, в Requirements написано, что эта штука не работает на Windows XP.
aleksei_ee
16.09.2025 19:05Есть ли возможность с помощью этого или какого либо другого решения выдать сертификат для сайта, недоступного извне сети? То есть для внутреннего сайта
AntonLarinLive
16.09.2025 19:05Если есть контроль над публичной зоной DNS, совпадающей с внутренней, то можно сделать проверки исключительно через DNS без всяких сайтов.
aleksei_ee
16.09.2025 19:05Можно поподробнее? Что-то никак не придумать как именно это сделать.
Demosfen
16.09.2025 19:05Как вариант: https://github.com/joohoi/acme-dns-certbot-joohoi
Запускаете, создаете CNAME, ждете несколько минут, продолжаете процесс. Больше DNS трогать не надо, запускаете просто certbot renew.И можно без отдельной зоны DNS. Неоднократно встречал у больших дядек когда в днс есть и публичные записи и внутренние в духе servicename IN A 192.168.x.x. Да это некрасиво и так делать нехорошо, но кто вам запретит? :)
JBFW Автор
16.09.2025 19:05Есть.
Делается внешний сайт, единственный контент которого - секретная строка.
Делается внутренний сайт с тем же доменным именем, которое прописывается на своем локальном DNS сервере в интранете.
Снаружи - виден сайт-заглушка, этого достаточно для получения сертификата. Внутри виден настоящий внутренний сайт.
Доменное имя - из серии бесплатных, таких сервисов несколько есть.
PanenKov
16.09.2025 19:05А ещё есть caddy, который используется вместо nginx. Он сам запрашивает все сертификаты и сам их обновляет.
JBFW Автор
16.09.2025 19:05Есть. И он работает не точно как nginx, и под него надо переписывать конфиги, непонятно как у него с нагрузкой, балансировкой, перемаршрутизацией запросов, и неплохо бы разобраться как именно "он сам" что-то делает - на это нужно время, это изменение инфраструктуры.
Вообще же, как правило, чем больше разных функций в одной программе - тем хуже, не надёжнее и сложнее в поддержке.
Может caddy исключение из этого правила, но есть сомнения...
Sly_tom_cat
16.09.2025 19:05Есть acme.sh - в нем много вариантов обновления LE сертификатов и для случаев, когда нужно для верификации поднять http/https сервер, скрипт его поднимает сам. Причем тот "сервер" буквально из говна и палок сделан, но большего для верификации домена и не нужно.
Dupych
16.09.2025 19:05Никогда не было проблем с cerbot.
Автоматом получает сериификаты доя моих серверов.
Веб сервер выступает как прокси
bbasil
16.09.2025 19:05Существенный косяк в проверке файлов локальных сертификатов, а именно по времени создания самого файла. Более правильный путь - openssl проверяет expiration date самого сертификата и если ему осталось менее 30 дней запускает renewal процесс для этого сертификата.
Это особенно полезно когда у тебя стопка wildcard сертификатов для разных доменов.
JBFW Автор
16.09.2025 19:05Более правильный, да, но не видно практической разницы, в случае если эти файлы никаким иным образом не модифицируются.
Не могу придумать сценарий обновления времени их модификации при сохранении старого expiration date, кроме шаловливых ручек админа, зачем-то пересохранившего открытый в редакторе файл. При просто копировании туда-сюда копируется и время модификации.
Но если такая возможность допускается - правильнее проверять, конечно.
bbasil
16.09.2025 19:05Случаи бывают разные: от ... и до ...))
Проверять какой-либо критерий по некоему другому косвенному критерию - прямая дорога к длительному траблшутингу )JBFW Автор
16.09.2025 19:05Ну так-то вообще лучше по работающему сайту смотреть, и по его ответу проверять.
А то была чудная история про два IP на одном имени, где пользователя рандомно отправляло то туда, где актуальная версия сайта, то на старый сервер, где сертификат давно пропал. Админ чуть напутал...
Kenya-West
16.09.2025 19:05А то была чудная история про два IP на одном имени
Чудная штука - round-robin DNS-балансировка. Мне помогает грубо располовинить трафик. Тупо и без вкуса, зато работает!
Lazhu
16.09.2025 19:05А что не так с сертботом? Работает практически везде. Лишь бы была DNS A record. Ваял даже когда-то шеллскрипт для мини хостинга, прописывающий все конфиги для пачки доменов, в т.ч. и LE серт с автообновлением.
Grand_piano
16.09.2025 19:05Хотелось бы добавить - современные версии nginx умеют самостоятельно обновляться сертификаты, без этого набора "костыликов".
JBFW Автор
16.09.2025 19:05Так это старый набор, ему уже лет 5.
Тогда не умел, о том и речь: "если ПО не поддерживает само..."
otasss
16.09.2025 19:05Как попроще приделать S к сайту на awebserver через duckdns.org? torzhok.duckdns.org:8080
JBFW Автор
16.09.2025 19:05Это какое-то приложение для Андроида, вроде? Пишут, что на базе Апача.
Значит надо в эту сторону смотреть, как оно там устроено, где там апач и потом его настраивать.
Другой вариант - повесить на отдельном сервере (железном или VDS) всё тот же nginx, настроить сертификаты там, и обращаться к вашему серверу как к аплинку - тогда на нем вообще ничего менять не нужно.
Или через Cloudflare - но там уже могут быть проблемы со стороны duckdns...
Смотря какая цель была в том, чтобы запускать сервер на Андроиде.
masikm
16.09.2025 19:05Примерно таким же скриптом пользуюсь для домашних нужд, только года 3-4 назад пришлось его обновить до v2 ибо у вас v1 и он работать сейчас не будет.
Так же сразу скажу минус этого варианта - вот эти "а раз в 2 дня" вы рано или поздно попадете что сертификат протух а крон еще не наступил, и все у вас уйдет в сертификат не Валид что критично для того же python и много кого еще.
Я так понимаю смысл автора просто рассказать как это работает и что происходит в краце. Статья не плохая но если бы вы переделали все под v2 было бы совсем здорово
JBFW Автор
16.09.2025 19:05Видимо у меня действительно v2, но я не помню откуда его брал, если не с того Гитхаба.
Он работает сейчас.
39655 Aug 18 2020 letsencrypt.sh
Срок действия сертификата 90 дней, если пытаться его обновлять хотя бы раз в 10 дней - выходит что за последние пару месяцев уже 6 экземпляров скрипта всё пытаются раз в 5 секунд обновить несчастный сертификат, и все никак не могут. И никто этого не замечает.
Ну только если сервер сертификатов помер, но тогда уже без разницы
MountainGoat
16.09.2025 19:05сертификат протух а крон еще не наступил,
Сертификат даётся на 90 дней, при этом общепринято начинать его менять на 60-й. Как раз чтобы админ успел из отпуска выйти, если что-то не работает.
Fesc
16.09.2025 19:05В статье не раскрыта тема получения сертификата на поддомены для нескольких сайтов. А она тоже интересная и полезная.
JBFW Автор
16.09.2025 19:05В моём случае делаю просто:
./5.sign.sh sub1.my-site.com
./5.sign.sh sub2.my-site.com
и так далее.Получаются соответствующие файлы pem, дальше оно само работает.
Разные сайты - разные сертификаты.
DonAlPAtino
16.09.2025 19:05Направьте пожалуйста в какую бы сторону покопать. Есть пачка сервисов требующих сертов. Часть только внутри, часть опубликована через один IP через haproxy с ssl offload/раскидыванием по sni (http там нет вообще, только https). Среди зоопарка сервисов nextcloud, exchange, ms Terminal Gateway и еще пачка. Даже Cisco ASA. Все это постепенно вводилось в эксплуатацию и подключалось.
Сейчас получаю wildcard cert на шлюзе и раскатываю по всем линуксам (кроме винды, еще не добрался) ансиблем. На выходе уже какой-то монстроидальный скрипт и единая точка отказа. Вот как-то хотя бы часть сервисов заставить серты самим через web получать при этом http не открывая да и haproxy с его ssl offload лучше вообще не трогая.
DikSoft
У большинства DNS поставщиков есть API с авторизацией.
Для них гораздо проще использовать скрипт, построенный на базе Posh-ACME, запускаемый по расписанию. В скрипт прикручивается и остальная автоматизация, включая установки сертификата, его перепаковки под все разные закидоны (full_chain / стопка файлов), проверки, уведомления, и т.п.
Ваш вариант тоже рабочий. Кому-то будет проще с ним.