В недавней статье об 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
и так далее.
Скрипту всё равно для какого домена получать сертификат, если там настроена отдача "секретной строки".