Letsencrypt и автопродление на основе dns
Желаемый результат: автоматическое продление сертификатов, а если точнее, то только wildcard-сертификата.
Подготовка DNS-сервера
Wildcard-сертификаты у Letsencrypt работают только на основе подтверждения через DNS. При подтверждении скрипт должен будет автоматически вносить TXT-запись в зону. Т.к. динамически дёргать основную доменную зону страшно, заведём специальную зону под эту задачу, и вся жизнь будет проходить в ней.
# Создаём новый зонный файл cd /chroot/dns/etc/bind/dyn cp ../pri/sabitov.ru ./certbot.sabitov.ru mcedit certbot.sabitov.ru chmod 664 certbot.sabitov.ru chown root:named certbot.sabitov.ru # Правим родительскую зону, прописываем glue-запись cd ../pri/ mcedit sabitov.ru # Генерим ключ, раньше на этом шаге использовался dnssec-keygen. Сейчас это объявлено устаревшим. https://gitlab.isc.org/isc-projects/bind9/commit/21761bfe799c8f298e3ce26285426b9a30473e6d?view=parallel cd /chroot/dns/etc/bind tsig-keygen -a hmac-sha512 certbot >> ddns.key # Проверяем, что хорошо: cat ddns.key # Прописываем НОВУЮ зону, указываем, что с помощью выше созданного ключа можно эту зону править. mcedit named.conf # Рестарт, проверяем, что получилось /etc/init.d/named restart host -t any test.certbot.sabitov.ru ns2 host -t any test.certbot.sabitov.ru ns1
Файл ddns.key должен содержать запись вида:
- ddns.key
key "certbot" { algorithm hmac-sha512; secret "Hk0In2h8Z6be5jg7NoDDuC80qY+7jYucyv39peyhImPWIhufBCwwlz5jYbbKhfVEsBVtalCPmKz5atIH+3wPbg=="; };
В файле named.conf должны появиться следующие строки:
- named.conf
include "/etc/bind/ddns.key"; zone "certbot.sabitov.ru" { type master; file "dyn/certbot.sabitov.ru"; allow-query { any; }; allow-transfer { xfer; }; allow-update { key certbot; }; };
Где xfer – имя ACL, который разрешает трансфер зоны.
Зонный файл для certbot.sabitov.ru самый обычный. На момент написания:
- certbot.sabitov.ru
$ORIGIN . $TTL 900 ; 15 minutes certbot.sabitov.ru IN SOA ns1.sabitov.ru. root.sabitov.ru. ( 1812000011 ; serial 400 ; refresh (6 minutes 40 seconds) 400 ; retry (6 minutes 40 seconds) 800 ; expire (13 minutes 20 seconds) 400 ; minimum (6 minutes 40 seconds) ) NS ns1.sabitov.ru. NS ns2.sabitov.ru. $ORIGIN certbot.sabitov.ru.
Зонный файл для sabitov.ru должен содержать три принципиально важные записи:
- sabitov.ru
; Показывает, кто имеет право выпускать сертификаты для нашего домена. @ IN CAA 0 issue "letsencrypt.org" ; glue-запись, особенно важно, если основная зона и certbot.... будут расположены на разных NS'ах certbot IN NS ns1.sabitov.ru. IN NS ns2.sabitov.ru. ; собственно эта запись и нужна для работы. _acme-challenge CNAME _acme-challenge.certbot.sabitov.ru.
Логика работы такая: скриптам обновления сертификатов будет указано, что запись _acme-challenge надо вносить в зону certbot.sabitov.ru. При обновлении сертификата для всего домена sabitov.ru. Letsencrypt проверит запись _acme-challenge.sabitov.ru, увидит, что это CNAME, запросит _acme-challenge.certbot.sabitov.ru и получит правильные данные, после чего проверка успешно завершится.
Подготовка головной машины.
Одна из машин должна стать головной и следить за обновлением сертификата. При успешном обновлении, новый сертификат тарболится, выкладывается под http-сервер и скачивается остальными машинами, которые используют этот сертификат.
Прежде всего проверяем, что с головной машины можно выполнить апдейт зоны. Для этого готовим отдельный файлик, условно, nsupd:
server 1.2.3.4 key hmac-sha512:certbot Hk0In2h8Z6be5jg7NoDDuC80qY+7jYucyv39peyhImPWIhufBCwwlz5jYbbKhfVEsBVtalCPmKz5atIH+3wPbg== zone certbot.sabitov.ru update add t.certbot.sabitov.ru. 86400 IN TXT t ;update del t.certbot.sabitov.ru. send
Сервером указываем мастера зоны. Выполняем команду nsupdate nsupd
, после чего проверяем, что в зоне появилась новая запись: host t.certbot.sabitov.ru.
Если всё хорошо:
- ставим app-crypt/acme-sh
- создаём директорию /etc/letsencrypt
- регистрируем эккаунт в Letsencrypt
- для каждого домена, для которого будем генерить ключи создаём свою поддиректорию (в данном примере только одну sabitov.ru)
- копируем ddns.key с DNS-сервера в /etc/letsencrypt. Хорошо, если он будет назван в виде домен.key. Если в ddns.key лежит несколько ключей, лишнее надо удалить
- генерим сертификаты
Теперь тоже самое, только понятным языком:
mkdir -p /etc/letsencrypt/sabitov.ru # объединяем пп. 1 и 3 acme.sh --register-account --accountemail letsencrypt@sabitov.ru --config-home /etc/letsencrypt/ --cert-home /etc/letsencrypt scp master.sabitov.ru:/etc/bind/keys/Kcertbot.\* /etc/letsencrypt/ export NSUPDATE_SERVER=1.2.3.4 # IP мастера зоны certbot.sabitov.ru. export NSUPDATE_KEY=/etc/letsencrypt/sabitov.ru.key acme.sh --config-home /etc/letsencrypt/ --cert-home /etc/letsencrypt/ --issue -d sabitov.ru -d '*.sabitov.ru' --challenge-alias certbot.sabitov.ru --dns dns_nsupdate
Если и тут всё хорошо, лабаем скриптец, который будет дёргаться cron'ом ежесуточно:
- /usr/local/sbin/renew_certs.sh
#!/bin/bash -xe CERT_HOME="/etc/letsencrypt" EXGANGE_POOL="/var/www/site_name/htdocs/exchange_pool" DOMAIN_LIST=( sabitov.ru ) notAfter=`openssl x509 -text -noout -in "${CERT_HOME}/${DOMAIN_LIST[0]}/${DOMAIN_LIST[0]}.cer" | sed -ne 's/.*Not After : //p'` notBefore=`openssl x509 -text -noout -in "${CERT_HOME}/${DOMAIN_LIST[0]}/${DOMAIN_LIST[0]}.cer" | sed -ne 's/.*Not Before: //p'` delta=$(expr -- `date +%s --date="${notAfter}"` - `date +%s ` ) if [ $delta -ge 604800 ] ; then # one week # До конца сертификата осталось больше недели. # Проверяем, как давно обновились сертификаты, # если прошло больше 2х суток удаляем тарболы. delta=$(expr -- `date +%s --date="${notBefore}"` - `date +%s ` ) if [ $delta -ge 172800 ] ; then # two days rm -f "${EXGANGE_POOL}"/*.tar fi exit; fi # До конца сертификата осталось меньше недели. Обновляемся. export NSUPDATE_SERVER=1.2.3.4 export NSUPDATE_KEY=/etc/letsencrypt/Kcertbot.......private /usr/bin/acme.sh --log /tmp/acme.sh.log --log-level 2 --config-home "${CERT_HOME}" --cert-home "${CERT_HOME}" --renew-all cd "${CERT_HOME}" for d in ${DOMAIN_LIST[@]} ; do tar cvf "${EXGANGE_POOL}/${d}.tar" "${d}/"*.{cer,csr,key} done
Как финальный штрих, накручиваем в http-сервере доступ к «$EXGANGE_POOL».
Настройка остальных машин.
- /usr/local/sbin/renew_certs_at_clients.sh
#!/bin/bash -xe CERT_HOME="/etc/letsencrypt" EXGANGE_URL="http://site_name/exchange_pool/" DOMAIN_LIST=( sabitov.ru ) notAfter=`openssl x509 -text -noout -in "${CERT_HOME}/${DOMAIN_LIST[0]}/${DOMAIN_LIST[0]}.cer" | sed -ne 's/.*Not After : //p'` delta=$(expr -- `date +%s --date="${notAfter}"` - `date +%s ` ) if [ $delta -ge 604800 ] ; then # one week exit; fi # До конца сертификата осталось меньше недели. Обновляемся. mkdir -p "${CERT_HOME}" cd "${CERT_HOME}" for d in ${DOMAIN_LIST[@]} ; do /usr/bin/wget -t0 -q "${EXGANGE_URL}/${d}.tar" tar xvf "${d}.tar" rm -f "${d}.tar" done for s in nginx dovecot postfix ; do /etc/init.d/$s restart done