В недавней статье об 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)


  1. DikSoft
    16.09.2025 19:05

    У большинства DNS поставщиков есть API с авторизацией.

    Для них гораздо проще использовать скрипт, построенный на базе Posh-ACME, запускаемый по расписанию. В скрипт прикручивается и остальная автоматизация, включая установки сертификата, его перепаковки под все разные закидоны (full_chain / стопка файлов), проверки, уведомления, и т.п.

    Ваш вариант тоже рабочий. Кому-то будет проще с ним.


  1. MountainGoat
    16.09.2025 19:05

    Чтобы не заниматься всем этим вручную, я использую https://github.com/linuxserver/docker-swag. Это образ для Докера, содержащий nginx и самостоятельно следящий за сертификатом. Он даже уведомит на почту, если сертификат всё таки скоро протухнет. Точнее, попросит LetsEncrypt уведомить.

    Затем из него можно копировать сертификаты куда надо или просто использовать его как proxy.


    1. Aracturat
      16.09.2025 19:05

      Лучше тогда более легкий https://github.com/nginx-le/nginx-le


    1. Kenya-West
      16.09.2025 19:05

      Тоже порекомендую нафаршированный веб-сервер Caddy, который, помимо автообновления SSL любым из способов, автоматически обновляет свой конфиг при появлении контейнера с лейблами caddy в его сети. Очень удобно, конфиг становится ненужным в явном виде, не нужно ничего хардкодить - всё происходит динамически и декларативно, запускай себе docker compose'ы сколько хочешь.

      В бою на пет-проектах на 20+ серверов уже полгода.


  1. Sank0k69
    16.09.2025 19:05

    Топ материал, он бы идеально добавил бы мою статью про SSL, о которой идет речь в начале =D

    Ахуительный контент, я искренне считаю, что они прям ИДЕАЛЬНО дополняют друг-друга.


  1. greenork
    16.09.2025 19:05

    Зачем использовать форк возрастом с говно мамонта, когда оригинал обновляется и работает? https://github.com/dehydrated-io/dehydrated


    1. JBFW Автор
      16.09.2025 19:05

      Зачем обновлять то, что работает? )

      Это ж не московские бордюры....


      1. greenork
        16.09.2025 19:05

        1) В вашем форке от 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

        Что там у вас работает? Тут нужна гифка с Тиньковым.
        Проверять не буду, мне теперь кажется что вы накопипастили мусор из чатика с нейросеткой.


        1. JBFW Автор
          16.09.2025 19:05

          Начнем с того, что это не мой форк. Я его просто использую.

          А во-вторых, я его использую - он работает. Вот прямо пару часов назад.

          Правда, я совершенно не помню откуда конкретно этот скрипт взял, это было 5 лет назад - возможно это не тот который на конкретно этом Гитхабе, а другой, правильный - но он тоже называется letsencrypt.sh. если вы так хорошо разбираетесь в форках - может быть тогда правильную ссылочку на ТОТ letsencrypt.sh дадите?

          Что там у вас за отношения с гифками и нейросетками - этого не знаю.


  1. 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, вся задача которого будет создать в журнале приложений событие с каким-то вашим уникальным кодом

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

    Вариант рабочий, несложный в отладке, расширяемый и достаточно стабильный.


    1. PereslavlFoto
      16.09.2025 19:05

      Для ленивых есть ещё вариант - Certify The Web

      К сожалению, в Requirements написано, что эта штука не работает на Windows XP.


  1. aleksei_ee
    16.09.2025 19:05

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


    1. AntonLarinLive
      16.09.2025 19:05

      Если есть контроль над публичной зоной DNS, совпадающей с внутренней, то можно сделать проверки исключительно через DNS без всяких сайтов.


      1. aleksei_ee
        16.09.2025 19:05

        Можно поподробнее? Что-то никак не придумать как именно это сделать.


        1. 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. Да это некрасиво и так делать нехорошо, но кто вам запретит? :)


    1. JBFW Автор
      16.09.2025 19:05

      Есть.

      Делается внешний сайт, единственный контент которого - секретная строка.

      Делается внутренний сайт с тем же доменным именем, которое прописывается на своем локальном DNS сервере в интранете.

      Снаружи - виден сайт-заглушка, этого достаточно для получения сертификата. Внутри виден настоящий внутренний сайт.

      Доменное имя - из серии бесплатных, таких сервисов несколько есть.


  1. PanenKov
    16.09.2025 19:05

    А ещё есть caddy, который используется вместо nginx. Он сам запрашивает все сертификаты и сам их обновляет.


    1. JBFW Автор
      16.09.2025 19:05

      Есть. И он работает не точно как nginx, и под него надо переписывать конфиги, непонятно как у него с нагрузкой, балансировкой, перемаршрутизацией запросов, и неплохо бы разобраться как именно "он сам" что-то делает - на это нужно время, это изменение инфраструктуры.

      Вообще же, как правило, чем больше разных функций в одной программе - тем хуже, не надёжнее и сложнее в поддержке.

      Может caddy исключение из этого правила, но есть сомнения...


      1. Demosfen
        16.09.2025 19:05

        Так и в сам nginx уже больше месяца назад это впилили.


        1. JBFW Автор
          16.09.2025 19:05

          Во, отлично.


          1. sirmax123
            16.09.2025 19:05

            А до того можно было на встроенном в OpenResty LUA получать
            (самому писать ничего не надо, есть готовый проект)

            Но, это удобно только если хочется Nginx с LUA

            (https://github.com/auto-ssl/lua-resty-auto-ssl)


  1. Sly_tom_cat
    16.09.2025 19:05

    Есть acme.sh - в нем много вариантов обновления LE сертификатов и для случаев, когда нужно для верификации поднять http/https сервер, скрипт его поднимает сам. Причем тот "сервер" буквально из говна и палок сделан, но большего для верификации домена и не нужно.


  1. Dupych
    16.09.2025 19:05

    Никогда не было проблем с cerbot.

    Автоматом получает сериификаты доя моих серверов.

    Веб сервер выступает как прокси


  1. bbasil
    16.09.2025 19:05

    Существенный косяк в проверке файлов локальных сертификатов, а именно по времени создания самого файла. Более правильный путь - openssl проверяет expiration date самого сертификата и если ему осталось менее 30 дней запускает renewal процесс для этого сертификата.

    Это особенно полезно когда у тебя стопка wildcard сертификатов для разных доменов.


    1. JBFW Автор
      16.09.2025 19:05

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

      Не могу придумать сценарий обновления времени их модификации при сохранении старого expiration date, кроме шаловливых ручек админа, зачем-то пересохранившего открытый в редакторе файл. При просто копировании туда-сюда копируется и время модификации.

      Но если такая возможность допускается - правильнее проверять, конечно.


      1. bbasil
        16.09.2025 19:05

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


        1. JBFW Автор
          16.09.2025 19:05

          Ну так-то вообще лучше по работающему сайту смотреть, и по его ответу проверять.

          А то была чудная история про два IP на одном имени, где пользователя рандомно отправляло то туда, где актуальная версия сайта, то на старый сервер, где сертификат давно пропал. Админ чуть напутал...


          1. Kenya-West
            16.09.2025 19:05

            А то была чудная история про два IP на одном имени

            Чудная штука - round-robin DNS-балансировка. Мне помогает грубо располовинить трафик. Тупо и без вкуса, зато работает!


  1. Lazhu
    16.09.2025 19:05

    А что не так с сертботом? Работает практически везде. Лишь бы была DNS A record. Ваял даже когда-то шеллскрипт для мини хостинга, прописывающий все конфиги для пачки доменов, в т.ч. и LE серт с автообновлением.


  1. Grand_piano
    16.09.2025 19:05

    Хотелось бы добавить - современные версии nginx умеют самостоятельно обновляться сертификаты, без этого набора "костыликов".


    1. JBFW Автор
      16.09.2025 19:05

      Так это старый набор, ему уже лет 5.

      Тогда не умел, о том и речь: "если ПО не поддерживает само..."


  1. otasss
    16.09.2025 19:05

    Как попроще приделать S к сайту на awebserver через duckdns.org? torzhok.duckdns.org:8080


    1. JBFW Автор
      16.09.2025 19:05

      Это какое-то приложение для Андроида, вроде? Пишут, что на базе Апача.

      Значит надо в эту сторону смотреть, как оно там устроено, где там апач и потом его настраивать.

      Другой вариант - повесить на отдельном сервере (железном или VDS) всё тот же nginx, настроить сертификаты там, и обращаться к вашему серверу как к аплинку - тогда на нем вообще ничего менять не нужно.

      Или через Cloudflare - но там уже могут быть проблемы со стороны duckdns...

      Смотря какая цель была в том, чтобы запускать сервер на Андроиде.


  1. masikm
    16.09.2025 19:05

    Примерно таким же скриптом пользуюсь для домашних нужд, только года 3-4 назад пришлось его обновить до v2 ибо у вас v1 и он работать сейчас не будет.

    Так же сразу скажу минус этого варианта - вот эти "а раз в 2 дня" вы рано или поздно попадете что сертификат протух а крон еще не наступил, и все у вас уйдет в сертификат не Валид что критично для того же python и много кого еще.

    Я так понимаю смысл автора просто рассказать как это работает и что происходит в краце. Статья не плохая но если бы вы переделали все под v2 было бы совсем здорово


    1. JBFW Автор
      16.09.2025 19:05

      Видимо у меня действительно v2, но я не помню откуда его брал, если не с того Гитхаба.

      Он работает сейчас.

      39655 Aug 18 2020 letsencrypt.sh

      Срок действия сертификата 90 дней, если пытаться его обновлять хотя бы раз в 10 дней - выходит что за последние пару месяцев уже 6 экземпляров скрипта всё пытаются раз в 5 секунд обновить несчастный сертификат, и все никак не могут. И никто этого не замечает.

      Ну только если сервер сертификатов помер, но тогда уже без разницы


    1. MountainGoat
      16.09.2025 19:05

      сертификат протух а крон еще не наступил,

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


  1. Fesc
    16.09.2025 19:05

    В статье не раскрыта тема получения сертификата на поддомены для нескольких сайтов. А она тоже интересная и полезная.


    1. JBFW Автор
      16.09.2025 19:05

      В моём случае делаю просто:

      ./5.sign.sh sub1.my-site.com
      ./5.sign.sh sub2.my-site.com
      и так далее.

      Получаются соответствующие файлы pem, дальше оно само работает.
      Разные сайты - разные сертификаты.


  1. 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 лучше вообще не трогая.