Application: "Certbot v.0.26-35" for "Let`s Encrypt" (on Python), "Bind9".
Задача: наладить запрашивание SSL-сертификата от "Let`s Encrypt", действие которого распространяется на все поддомены одним уровнем выше указанного (включая опорный) - так называемый wildcard-сертификат.
Ранее я уже рассказывал о том, как наладить полуавтоматизированное запрашивание и продление SSL-сертификатов привязанных к одному доменному имени с использованием для подтверждения владением доменом встроенного плагина "webroot", размещающего в указанном месте файловой системы специальный проверочный код, отдаваемый в текстовом файле посредством протокола HTTP. Для wildcard-сертификатов поддерживается только аутентификация посредством создания специальных DNS-записей (метод "dns-01"), основанная на подтверждении владения (управления) DNS-сервером, который поддерживает указанный домен.
Идея в том, что ACME-клиент ("Certbot" в данном случае) запрашивает от сервера "Let's Encrypt" уникальную строку-идентификатор, которую любым способом необходимо разместить в специальной TXT-записи нижеследующего формата, чтобы проверяющий сервер "Let's Encrypt" мог считать её и удостовериться, что мы владеем или управляем указанным DNS-сервером:
_acme-challenge.example.net. 120 IN TXT "Sg0...oLc"
Как вариант, можно воспользоваться встроенными средствами "Certbot", указав исполнителем плагин "manual" (подразумевающий, что все действия на стороне клиента осуществляются вручную) и метод аутентификации "dns-01" (задаваемые параметром "preferred-challenges") - тогда ACME-клиент в процессе работы сообщит нам имя TXT-записи и секретный код, который необходимо в ней опубликовать для подтверждения владения доменом.
Однако в корпоративной сети с двумя-тремя NS-серверами проще всего использовать дополнительно устанавливаемый certbot-плагин "dns-rfc2136", автоматизирующий процедуры получения секретного ключа, публикующий его и удаляющий DNS-запись после завершения запроса, область действия которого как правило достаточно просто ограничивается настройками NS-сервера, как такового.
Плагин "dns-rfc2136" взаимодействует с DNS-сервером посредством протокола "Dynamic DNS (DDNS)" с подписанием команд посредством "Transaction SIGnature (TSIG)". Не все DNS-серверы поддерживают эту связку, но в примере используется "Bind9", в этом плане полностью функциональный.
Установка в "Certbot" DNS-плагина.
Исходим из того, что certbot-клиент уже установлен и настроен, а нам лишь необходимо добавить плагин (в противном случае следует пройти все предыдущие этапы инсталляции).
Воспользовавшись встроенным в "Certbot" инсталлятором python-компонентов отдаём всего одну команду установки требуемого плагина:
# sudo -u certbot /opt/eff.org/certbot/venv/bin/pip install certbot-dns-rfc2136
Успешная установка будет сопровождаться примерно следующим выводом:
....
Successfully installed certbot-dns-rfc2136-0.35.1 dnspython-1.16.0
Successfully installed certbot-dns-rfc2136-0.35.1 dnspython-1.16.0
Для полной уверенности можно запросить список всех установленных плагинов и найти среди них требуемый "dns-rfc2136", как-то так:
# sudo -u certbot /opt/eff.org/certbot-auto/certbot-auto plugins
Параметров вызова у плагина немного, но перед работой есть смысл с ними ознакомиться:
# sudo -u certbot certbot --help all
Настройка плагина "certbot-dns-rfc2136".
Прежде всего подготовим TSIG-ключ ("Transaction SIGnature") для подписания запросов к NS-серверу, например "Bind9":
# apt install bind9utils
# mkdir /var/tmp/tsig-keys
# dnssec-keygen -a HMAC-SHA512 -b 512 -r /dev/urandom -K /var/tmp/tsig-keys -n HOST example.net.
# mkdir /var/tmp/tsig-keys
# dnssec-keygen -a HMAC-SHA512 -b 512 -r /dev/urandom -K /var/tmp/tsig-keys -n HOST example.net.
В результате в целевой директории появится два файла именованных вроде "Kexample.net.+165+XXXXX.key" и "Kexample.net.+165+XXXXX.private", с HMAC-хешами и описанием их параметров. Нас интересует только один результирующий файл, с секретным ключем, который можно вычленить одной простой командой:
# grep -e Key /usr/local/etc/letsencrypt/Kexample.net.+165+XXXXX.private | cut -d' ' -f2-
Файлы ключей нам больше не потребуются, так что их можно удалить:
# rm -r /var/tmp/tsig-keys
Используя полученную строку HMAC-хеша формируем конфигурационный файл TSIG-ключа для подключения "Certbot" к ведущему NS-серверу:
# vi /etc/letsencrypt/ns.example.net-rfc2136.ini
# (обязательно указывать IP-адрес, а не FQDN целевого NS-сервера!)
dns_rfc2136_server = 10.20.30.2
dns_rfc2136_name = letsencrypt-example.net
dns_rfc2136_secret = 0eit...A9sQ==
dns_rfc2136_algorithm = HMAC-SHA512
dns_rfc2136_server = 10.20.30.2
dns_rfc2136_name = letsencrypt-example.net
dns_rfc2136_secret = 0eit...A9sQ==
dns_rfc2136_algorithm = HMAC-SHA512
# chown -R certbot:root /etc/letsencrypt/ns.example.net-rfc2136.ini
# chmod -R go-rw /etc/letsencrypt/ns.example.net-rfc2136.ini
# chmod -R go-rw /etc/letsencrypt/ns.example.net-rfc2136.ini
Настройка первичного NS-сервера "Bind9".
Используя полученную ранее строку HMAC-хеша формируем конфигурационный файл TSIG-ключа для NS-сервера "Bind9":
# mkdir -p /etc/bind/key
# vi /etc/bind/key/letsencrypt-example.net.key
# vi /etc/bind/key/letsencrypt-example.net.key
// TSIG-ключ для подписания транзакций от ACME-приложений ("Let`s Encrypt")
key "letsencrypt-example.net" {
algorithm hmac-sha512;
secret "0eit...A9sQ==";
};
key "letsencrypt-example.net" {
algorithm hmac-sha512;
secret "0eit...A9sQ==";
};
# chown -R bind:bind /etc/bind/key
# chmod -R go-rw /etc/bind/key
# chmod -R go-rw /etc/bind/key
Включаем файл описания TSIG-ключа в конфигурацию "Bind9":
# vi /etc/bind/named.conf.options
// Включение файлов TSIG-ключей (для подписания транзакций)
include "/etc/bind/key/letsencrypt-example.net.key";
....
include "/etc/bind/key/letsencrypt-example.net.key";
....
Использование протокола DDNS, посредством которым "Certbot" подключается и оперирует сущностями "Bind9" проявляет в работе последнего неприятную особенность - при внесении изменений в доменную зону таковые вначале записываются в автоматически создаваемый файл журнала транзакций "*.jnl", которые минут через пятнадцать вливаются в оригинальный конфигурационный файл зоны, перезаписывая его с форматированием возможно кардинально отличающимся от исходного. Это неприемлемо в случае поддержания DNS-сервиса ручными правками, так что для тех доменов, которым мы собираемся запрашивать wildcard-сертификаты "Let`s Encrypt" будет лучше создавать выделенные для ACME-взаимодействий конфигурационные файлы доменной зоны:
# vi /etc/bind/named.conf
....
// Выделенная для взаимодействия с сервисами "Let`s Encrypt" доменная зона
zone "_acme-challenge.example.net" IN {
type master;
file "/var/lib/named/master/db._acme-challenge.example.net";
check-names ignore;
// Разрешаем предъявителю TSIG-ключа вносить изменения строго определённого характера
update-policy {
grant letsencrypt-example.net name _acme-challenge.example.net. TXT;
};
// Явно уведомляем secondary-серверы и разрешаем принятие от них AXRF-запросов
notify yes;
also-notify { 10.100.200.2;};
allow-transfer { 10.100.200.2; };
};
....
// Выделенная для взаимодействия с сервисами "Let`s Encrypt" доменная зона
zone "_acme-challenge.example.net" IN {
type master;
file "/var/lib/named/master/db._acme-challenge.example.net";
check-names ignore;
// Разрешаем предъявителю TSIG-ключа вносить изменения строго определённого характера
update-policy {
grant letsencrypt-example.net name _acme-challenge.example.net. TXT;
};
// Явно уведомляем secondary-серверы и разрешаем принятие от них AXRF-запросов
notify yes;
also-notify { 10.100.200.2;};
allow-transfer { 10.100.200.2; };
};
....
Минимально необходимый файл описания зоны доменных имён:
# vi /var/lib/named/master/db._acme-challenge.example.net
$TTL 3600
_acme-challenge.example.net. IN SOA ns.example.net. hostmaster.example.net. (
2019071001 ; sn = Serial number
900 ; ref = Refresh (15 min)
60 ; ret = update Retry (1 min)
86400 ; ex = Expiry (1 day)
900 ) ; nx = nxdomain Negative Cache TTL (15 min)
@ IN NS ns.example.net.
_acme-challenge.example.net. IN SOA ns.example.net. hostmaster.example.net. (
2019071001 ; sn = Serial number
900 ; ref = Refresh (15 min)
60 ; ret = update Retry (1 min)
86400 ; ex = Expiry (1 day)
900 ) ; nx = nxdomain Negative Cache TTL (15 min)
@ IN NS ns.example.net.
Одна из возможных проблем - нежелание вторичного NS-сервера обновлять свою конфигурацию по уведомлению от вышестоящего из-за того, что он считает её неизменившейся. Это поведение регулируется "серийным номером", а также параметрами длительности срока действия и кеширования доменного имени, указываемых в SOA - потому для нашего служебного домена сведём их до минимума.
Проверяем конфигурацию, как сервера, так и новой зоны доменных имён:
# named-checkconf /etc/bind/named.conf
# named-checkzone _acme-challenge.example.net /var/lib/named/master/db._acme-challenge.example.net
# named-checkzone _acme-challenge.example.net /var/lib/named/master/db._acme-challenge.example.net
Если ошибок не найдено - применяем конфигурацию:
# /etc/init.d/bind9 reload
Настройка вторичного NS-сервера "Bind9".
На стороне вторичного NS-сервера (или серверов), достаточно указать на несущий доменную зону главный NS-сервер, не забыв разрешить принимать от него уведомления обновлений:
# vi /etc/bind/named.conf
....
zone "_acme-challenge.example.net" IN {
type slave;
file "/var/lib/named/slave/db._acme-challenge.example.net";
masters { 10.20.30.2; };
allow-notify { 10.20.30.2; };
};
....
zone "_acme-challenge.example.net" IN {
type slave;
file "/var/lib/named/slave/db._acme-challenge.example.net";
masters { 10.20.30.2; };
allow-notify { 10.20.30.2; };
};
....
Проверяем синтаксис конфигурации новой зоны и применяем изменения:
# named-checkzone _acme-challenge.example.net /var/lib/named/slave/db._acme-challenge.example.net
# /etc/init.d/bind9 reload
# /etc/init.d/bind9 reload
Предварительные тесты.
Одна из проблем, с которым можно столкнуться при использовании средств автоматизации для комбинации DNS и "Let`s Encrypt" - неработоспособность связки главного и вторичных NS-серверов. Прежде всего есть смысл проверить, возможны ли со вторичных NS-серверов AXFR-запросы (транзакция репликации, отдающая всё содержимое доменной зоны):
# dig -b 10.100.200.2 axfr @10.20.30.2 example.net
...или, если AXFR-запросы разрешены только с аутентификацией HMAC-ключем:
# dig -b 10.100.200.2 axfr @10.20.30.2 example.net -k /etc/bind/key/ns2.example.net.key
Запрашиваем wildcard-сертификат у "Let`s Encrypt".
Прежде чем запрашивать сертификат "по настоящему" лучше провести функциональное тестирование без сохранения результатов (режим включается флагом "--dry-run"):
# sudo -u certbot /opt/eff.org/certbot/venv/bin/certbot certonly --dry-run \
--dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/ns.example.net-rfc2136.ini \
--dns-rfc2136-propagation-seconds 120 \
--server https://acme-v02.api.letsencrypt.org/directory \
--register-unsafely-without-email --agree-tos --manual-public-ip-logging-ok \
-d *.example.net
--dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/ns.example.net-rfc2136.ini \
--dns-rfc2136-propagation-seconds 120 \
--server https://acme-v02.api.letsencrypt.org/directory \
--register-unsafely-without-email --agree-tos --manual-public-ip-logging-ok \
-d *.example.net
В случае, если все составляющие отработали как предполагалось, мы увидим примерно следующее:
Plugins selected: Authenticator dns-rfc2136
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for example.net
Waiting 120 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
- The dry run was successful.
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for example.net
Waiting 120 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
- The dry run was successful.
Если постараться, то в течении двухминутного ожидания выделенного на проведение процедур создания DNS-записей таковые можно подсмотреть как на первичном NS-сервере, так и на вторичном:
# dig txt _acme-challenge.example.net @10.20.30.2
# dig txt _acme-challenge.example.net @10.100.200.2
# dig txt _acme-challenge.example.net @10.100.200.2
Вот теперь, если тестирование прошло гладко, запускаем команду запроса без флага "--dry-run" и через две минуты получаем уведомление о месторасположении нового набора SSL-сертификатов:
....
IMPORTANT NOTES:
Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/example.net/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/example.net/privkey.pem
....
IMPORTANT NOTES:
Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/example.net/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/example.net/privkey.pem
....
Сразу после создания можно полюбопытствовать параметрами SSL-сертификата и убедиться, что он действительно "wildcard":
# openssl x509 -text -noout -in /etc/letsencrypt/live/example.net/fullchain.pem
Certificate:
Data:
Version: 3 (0x2)
....
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
....
Subject: CN = *.example.net
....
Data:
Version: 3 (0x2)
....
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
....
Subject: CN = *.example.net
....
Применение и продление.
Способы применения SSL-сертификатов в разных web-серверах, а также автоматизация продления таковых (помним, что "Let`s Encrypt" выдаёт сертификаты сроком действия всего три месяца) подробно рассмотрены в предыдущих публикациях.
21 июля 2019 в 23:13
29 июля 2019 в 14:18
14 сентября 2022 в 04:04