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

Скрипту всё равно для какого домена получать сертификат, если там настроена отдача "секретной строки".