Удостоверяющий центр «Let’s Encrypt» (далее просто letsencrypt) вышел из беты пару месяцев назад, пообтерся в реальных условиях, избавился от детских болезней и оброс различными клиентами. И к этому моменту выдал 5 миллионов сертификатов. Самое время внедрять, т.е. получать сертификаты на свои домены и обновлять их в автоматическом режиме. Но как внедрить так, чтобы приблизиться к любимому админскому «поставил и забыл»? Чтобы было просто получать новые сертификаты, а старые при этом обновлялись автоматом? Ну и как добавить немного безопасности в этот процесс?
Ответ под катом.
- Скачать и настроить скрипт
sudo adduser --system --home /opt/letsencrypt le sudo -u le -s git clone https://github.com/lukas2511/letsencrypt.sh.git /opt/letsencrypt/ mkdir /opt/letsencrypt/.acme-challenges echo CONTACT_EMAIL="your@email" > /opt/letsencrypt/config echo "ваш_домен" > /opt/letsencrypt/domains.txt
- добавить в конфиг виртуального хоста nginx строчку
location /.well-known/acme-challenge/ { alias /opt/letsencrypt/.acme-challenges/; }
- запустить скрипт
sudo -u le /opt/letsencrypt/letsencrypt.sh --cron
- добавить ещё три строчки в конфиг виртуального хоста
listen 443 ssl; ssl_certificate /opt/letsencrypt/certs/ваш_домен/fullchain.pem; ssl_certificate_key /opt/letsencrypt/certs/ваш_домен/privkey.pem;
- добавить в крон пользователя le
1 0 * * * /opt/letsencrypt/letsencrypt.sh --cron
Как я уже сказал, letsencrypt оброс различными клиентами, которые позволяют получить сертификат. Этих самых клиентов, кстати, уже десятки под разные системы и языки программирования. В статье будет рассказано про реализацию клиента на bash от lukas2511, letsencrypt.sh. Это один скрипт на bash, который лежит в своей папке и для работы ему нужен только openssl. Запускаться он будет под отдельным пользователем. Конечно, при желании, всегда можно ещё сильнее закрутить гайки в плане безопасности — запускать в chroot и т.д.
Сначала нужно скачать и настроить скрипт.
Предположим, что ОС — linux, веб сервер — nginx, рабочая папка скрипта — /opt/letsencrypt, а пользователь — le.
Создадим системного пользователя, из под которого будет работать скрипт. При создании системного пользователя в debian/ubuntu, ему выставляется оболочка /bin/false и назначается группа nogroup, что нам вполне подходит.
$ sudo adduser --system --home /opt/letsencrypt le
Теперь можно стать этим пользователем и все делать под ним (кроме настройки nginx).
Скачиваем скрипт и смотрим содержимое.
$ sudo -u le -s
$ git clone https://github.com/lukas2511/letsencrypt.sh.git /opt/letsencrypt/
$ ls -la /opt/letsencrypt/
total 84
drwxr-xr-x 4 le le 4096 Jun 25 15:56 .
drwxr-xr-x 3 root root 4096 Jun 25 15:53 ..
-rw-r--r-- 1 le le 1406 Jun 25 15:56 CHANGELOG
drwxr-xr-x 3 le le 4096 Jun 25 15:56 docs
drwxr-xr-x 8 le le 4096 Jun 25 15:56 .git
-rw-r--r-- 1 le le 108 Jun 25 15:56 .gitignore
-rwxr-xr-x 1 le le 37634 Jun 25 15:56 letsencrypt.sh
-rw-r--r-- 1 le le 1080 Jun 25 15:56 LICENSE
-rw-r--r-- 1 le le 3040 Jun 25 15:56 README.md
-rwxr-xr-x 1 le le 8048 Jun 25 15:56 test.sh
-rw-r--r-- 1 le le 107 Jun 25 15:56 .travis.yml
Для работы скрипту нужна папка, куда он будет складывать файлы для валидации доменов.
По умолчанию скрипт настроен использовать папку /opt/letsencrypt/.acme-challenges
и будет падать с ошибкой, если такой папки нет.
Так же желательно создать конфиг с нужными параметрами. Параметры для работы скрипт пытается брать из файла /opt/letsencrypt/config
. По умолчанию файла нет и скрипт использует значения по умолчанию, но есть хорошо документированный конфиг в папке с документацией, который можно взять за основу.
Создаем папку и копируем конфиг
$ mkdir /opt/letsencrypt/.acme-challenges
$ cp /opt/letsencrypt/docs/examples/config /opt/letsencrypt/config
Чтобы посмотреть, с какими значениями работает скрипт, его можно вызвать с ключем --env
$ /opt/letsencrypt/letsencrypt.sh --env
# letsencrypt.sh configuration
#
# !! WARNING !! No main config file found, using default config!
#
declare -- CA="https://acme-v01.api.letsencrypt.org/directory"
declare -- LICENSE="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
declare -- CERTDIR="/opt/letsencrypt/certs"
declare -- CHALLENGETYPE="http-01"
declare -- DOMAINS_TXT="/opt/letsencrypt/domains.txt"
declare -- HOOK=""
declare -- HOOK_CHAIN="no"
declare -- RENEW_DAYS="30"
declare -- ACCOUNT_KEY="/opt/letsencrypt/accounts/aHR0cHM6Ly9hY21lLXYwMS5hcGkubGV0c2VuY3J5cHQub3JnL2RpcmVjdG9yeQo/account_key.pem"
declare -- ACCOUNT_KEY_JSON="/opt/letsencrypt/accounts/aHR0cHM6Ly9hY21lLXYwMS5hcGkubGV0c2VuY3J5cHQub3JnL2RpcmVjdG9yeQo/registration_info.json"
declare -- KEYSIZE="4096"
declare -- WELLKNOWN="/opt/letsencrypt/.acme-challenges"
declare -- PRIVATE_KEY_RENEW="yes"
declare -- OPENSSL_CNF="/usr/lib/ssl/openssl.cnf"
declare -- CONTACT_EMAIL=""
declare -- LOCKFILE="/opt/letsencrypt/lock"
CA
— какой удостоверяющий центр использовать. Их, как минимум два — боевой (по умолчанию) и тестовый. Дело в том, что у боевого есть разные ограничения по частоте запросов и количеству доменов. В эти ограничения легко упереться при тестировании. Поэтому я рекомендую для тестовых запусков указать тестовый центр. Он работает так же, как боевой, просто генерирует невалидные сертификаты.Вот тестовый CA:
CA="https://acme-staging.api.letsencrypt.org/directory"
CERTDIR
— папка для сертификатов. Внутри неё отельные папки по имени хоста. А в этих папках уже сертификаты для каждого хоста. Нужно будет настроить nginx читать сертификаты из этих папок (см. ниже).
DOMAINS_TXT
— список доменов. Одна строчка — один сертификат. В одной строчке может быть несколько доменов, тогда создается один сертификат для них. Скрипт берет первый домен, как название сертификата, а остальные домены указывает, как дополнительные. Например, для такого файла, скрипт создаст два сертификата: some.domain.com и test.com.
some.domain.com another.domain.net example.domain.org
test.com www.test.org ftp.test.net
HOOK
— скрипт, который запускается при различных действиях (при валидации доменов, при генерации сертификатов и т.д.).
Скрипту передаются разные параметры: название операции, пути к новым сертификатам и т.д.
Указание своего скрипта может помочь, если вам нужно выполнять чуть больше действий на каждом шаге.
Например, сертификат нужно разложить на несколько серверов, или нужно проводить валидацию домена через dns, а не через файл.
Скрипт, который ничего не делает, но содержит массу комментариев, лежит по адресу /opt/letsencrypt/docs/examples/hook.sh
RENEW_DAYS
— через сколько дней обновлять сертификат. Максимум 90, по умолчанию 30.
CONTACT_EMAIL
— рабочий email администратора.
Я рекомендую указать в конфиге свой email в CONTACT_EMAIL
и на время тестов прописать тестовый CA
.
Установка и первоначальная настройка скрипта на этом завершены. Теперь можно получать сертификаты.
Для начала получим сертифкат для одного домена: letest.lexore.net
В nginx для теста сделаем простой конфиг виртуального хоста, который будет выводить протокол, http или https.
server {
listen 80;
server_name letest.lexore.net;
location /.well-known/acme-challenge/ { alias /opt/letsencrypt/.acme-challenges/; }
location / {
default_type text/plain;
return 200 "scheme: $scheme";
}
}
Ключевой параметр: location /.well-known/acme-challenge/
В папке /opt/letsencrypt/.acme-challenges/
создаются файлы для подтверждения, что вы управляете сайтом.
Они должны быть доступны по адресу имя_сайта/.well-known/acme-challenge/
, иначе сертификат не подпишут.
Названия файлов генерируются случайным образом, поэтому проще открыть доступ ко всей папке.
В конце скрипт удаляет созданный файл, так что папка не будет захламляться.
Перезагрузим nginx и проверим сайт:
$ curl -i letest.lexore.net
HTTP/1.1 200 OK
Server: nginx
Date: Sun, 26 Jun 2016 13:13:18 GMT
Content-Type: text/plain
Content-Length: 12
Connection: keep-alive
scheme: http
Теперь нужно прописать имя хоста в domains.txt и запустить сам скрипт
$ echo letest.lexore.net > /opt/letsencrypt/domains.txt
$ /opt/letsencrypt/letsencrypt.sh --cron
# INFO: Using main config file /opt/letsencrypt/config
Processing letest.lexore.net
+ Signing domains...
+ Creating new directory /opt/letsencrypt/certs/letest.lexore.net ...
+ Generating private key...
+ Generating signing request...
+ Requesting challenge for letest.lexore.net...
+ Responding to challenge for letest.lexore.net...
+ Challenge is valid!
+ Requesting certificate...
+ Checking certificate...
+ Done!
+ Creating fullchain.pem...
+ Done!
Сертификат готов, файлы сертификата и ключа лежат в папке "/opt/letsencrypt/certs/letest.lexore.net
".
Осталось добавить настройки в nginx. Нужно добавить следующие строки в конфиг виртуального хоста:
listen 443 ssl;
ssl_certificate /opt/letsencrypt/certs/letest.lexore.net/fullchain.pem;
ssl_certificate_key /opt/letsencrypt/certs/letest.lexore.net/privkey.pem;
После перезагрузки nginx, можно пробовать сайт в браузере.
Если в конфиге скрипта был указан тестовый CA, браузер заругается на сертификат.
Но это все равно значит, что скрипт и nginx настроены правильно.
Теперь нужно просто поменять CA в конфиге на боевое значание и запустить скрипт ещё раз, добавив параметр --force
.
Без этого параметра скрипт не станет заново генерировать сертификат, т.к. ещё не подошел срок устаревания, указанный в конфиге.
le@endor:~$ /opt/letsencrypt/letsencrypt.sh --cron --force
# INFO: Using main config file /opt/letsencrypt/config
Processing letest.lexore.net
+ Checking domain name(s) of existing cert... unchanged.
+ Checking expire date of existing cert...
+ Valid till Sep 24 12:13:00 2016 GMT (Longer than 80 days). Ignoring because renew was forced!
+ Signing domains...
+ Generating private key...
+ Generating signing request...
+ Requesting challenge for letest.lexore.net...
+ Responding to challenge for letest.lexore.net...
+ Challenge is valid!
+ Requesting certificate...
+ Checking certificate...
+ Done!
+ Creating fullchain.pem...
+ Done!
После запуска скрипта и перезагрузки nginx у сайта появится правильный сертификат.
Сертификат готов, https работает.
Пару слов про несколько доменов.
Один сертификат может использоваться для нескольких доменов. Например, вот как это будет выглядеть, если добавить subdomain.letest.lexore.net
.
Запуск скрипта:
$ /opt/letsencrypt/letsencrypt.sh --cron --force
# INFO: Using main config file /opt/letsencrypt/config
Processing letest.lexore.net with alternative names: subdomain.letest.lexore.net
+ Checking domain name(s) of existing cert... changed!
+ Domain name(s) are not matching!
+ Names in old certificate: letest.lexore.net
+ Configured names: letest.lexore.net subdomain.letest.lexore.net
+ Forcing renew.
+ Checking expire date of existing cert...
+ Valid till Sep 24 12:48:00 2016 GMT (Longer than 80 days). Ignoring because renew was forced!
+ Signing domains...
+ Generating private key...
+ Generating signing request...
+ Requesting challenge for letest.lexore.net...
+ Requesting challenge for subdomain.letest.lexore.net...
+ Responding to challenge for letest.lexore.net...
+ Challenge is valid!
+ Responding to challenge for subdomain.letest.lexore.net...
+ Challenge is valid!
+ Requesting certificate...
+ Checking certificate...
+ Done!
+ Creating fullchain.pem...
+ Done!
Правка конфига nginx, перезапуск, и вот новый сертификат работает уже на два домена.
Домены не обязаны быть как-то связаны, можно сделать один домен для domain.com и ftp.example.net.
Максимум можно указать 100 доменов в одном сертификате.
Кому-то этого может хватить, чтобы обойтись одним сертификатом для всех сайтов на сервере.
Правда, этот сертификат придется пересоздавать на каждый новый домен в списке, и можно упереться в лимиты.
А теперь самое приятное — автоматизация.
Для того, чтобы скрипт сам обновлял все сертификаты из файла domains.txt и вы забыли про манипуляции руками, нужно добавить одну строчку в крон пользователя le:
0 1 * * * /opt/letsencrypt/letsencrypt.sh --cron
Таким образом, алгоритм перевода последующих сайтов на https:
- добавить
location /.well-known/acme-challenge/ { alias /opt/letsencrypt/.acme-challenges/; }
- добавить
ваш_домен
вdomains.txt
- запустить скрипт
/opt/letsencrypt/letsencrypt.sh --cron
- добавить настройки ssl
listen 443 ssl; ssl_certificate /opt/letsencrypt/certs/ваш_домен/fullchain.pem; ssl_certificate_key /opt/letsencrypt/certs/ваш_домен/privkey.pem;
Это не единственный способ автоматизации, многие клиенты letsencrypt позволяют свести всю работу к запуску одного скрипта по крону.
Данный скрипт мне понравился за простоту- всю работу делает один скрипт на bash.
А так же, за свои дополнительные возможности — кроме простой автоматизации, он из коробки поддерживает «сложную» автоматизацию, запуск своих скриптов и переопределение параметров.
Например, я уже упомянул про параметр HOOK
в конфиге, который позволяет запускать свой скрипт.
Так же из коробки есть параметр CONFIG_D
— папка, в которой будут запускаться все .sh
скрипты для переопределения параметров основного конфига.
Плюс, поддержка разных «аккаунтов» через указание разных ACCOUNTDIR
— папок с приватными ключами для подписи запросов.
Мне кажется, это позволит использовать скрипт в больших и сложных инфраструктурах.
Автор: lexore