UMGUM.COM 

Let‘s Encrypt + Wildcard + Bind9 ( Настройка подсистемы получения wildcard-сертификата от "Let's Encrypt", в спарке с NS-серверами "Bind9". )

10 июля 2019

OS: "Linux Debian 7/8/9/10", "Linux Ubuntu 14/16/18 LTS", "Linux Fedora 25/26/27/28/29".
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

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

В результате в целевой директории появится два файла именованных вроде "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

# chown -R certbot:root /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

// TSIG-ключ для подписания транзакций от ACME-приложений ("Let`s Encrypt")
key "letsencrypt-example.net" {
  algorithm hmac-sha512;
  secret "0eit...A9sQ==";
};

# chown -R bind:bind /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";
....

Использование протокола 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; };
};
....

Минимально необходимый файл описания зоны доменных имён:

# 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.

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

Проверяем конфигурацию, как сервера, так и новой зоны доменных имён:

# named-checkconf /etc/bind/named.conf
# 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; };
};
....

Проверяем синтаксис конфигурации новой зоны и применяем изменения:

# named-checkzone _acme-challenge.example.net /var/lib/named/slave/db._acme-challenge.example.net
# /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

В случае, если все составляющие отработали как предполагалось, мы увидим примерно следующее:

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.

Если постараться, то в течении двухминутного ожидания выделенного на проведение процедур создания DNS-записей таковые можно подсмотреть как на первичном NS-сервере, так и на вторичном:

# dig txt _acme-challenge.example.net @10.20.30.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
....

Сразу после создания можно полюбопытствовать параметрами 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
....

Применение и продление.

Способы применения SSL-сертификатов в разных web-серверах, а также автоматизация продления таковых (помним, что "Let`s Encrypt" выдаёт сертификаты сроком действия всего три месяца) подробно рассмотрены в предыдущих публикациях.


Заметки и комментарии к публикации:


Оставьте свой комментарий ( выразите мнение относительно публикации, поделитесь дополнительными сведениями или укажите на ошибку )