Краткая памятка по настройке HTTPS для Nginx через LetsEncrypt

Независимые умы никогда не боялись банальности

23 декабря 2019

Краткая памятка по настройке HTTPS для Nginx через LetsEncrypt

Сначала несколько общеизвестных фактов:

  • Сервис LetsEncrypt бесплатно предоставляет всем желающим заверенные SSL-сертификаты для доменных имён в публичных DNS-зонах.
  • Общение с сервисом производится только через API с помощью клиентских утилит, веб-интерфейса у него нет.
  • Выдавая сертификат на доменное имя, сервис требует от заявителя подтвердить, что он действительно является владельцем данного имени.
  • Подтверждение возможно одним из двух способов: через HTTP (по умолчанию) или через DNS.

Что требуется для подтверждения через HTTP?

  • Клиентская утилита обязана запускаться на том сервере, в IP-адрес которого распознаётся заверяемое доменное имя.
  • IP-адрес обязан быть публичным.
  • На порту 80 должен быть запущен Веб-сервер.
  • Веб-серверу требуется небольшая дополнительная настройка, чтобы правильно отвечать на проверочные запросы LetsEncrypt.

Почему в качестве клиентской утилиты мы выбрали dehydrated?

  • Потому что это один файл на bash с минимумом зависимостей (openssl и curl).
  • Потому что его пакет есть в стандартных репозитариях большиства дистрибутивов.
  • Потому что у него простая ручная установка в тех дистрибутивах, в которых готовый пакет отсутствует (например, в Ubuntu 16.04) или устарел.

Ручная установка (если нет готового пакета):

  • Скачиваем сценарий и делаем исполняемым:
  • cd /usr/bin
    wget https://raw.githubusercontent.com/lukas2511/dehydrated/master/dehydrated
    chmod +x dehydrated
  • Создаём каталоги:
  • mkdir /etc/dehydrated /var/lib/dehydrated
  • Создаём файл настроек /etc/dehydrated/config:
  • BASEDIR=/var/lib/dehydrated
    WELLKNOWN="${BASEDIR}/acme-challenges"
    DOMAINS_TXT="/etc/dehydrated/domains.txt"

Создаём ключи и получаем сертификаты:

  • Сначала создаём учётную запись в LetsEncrypt:
  • dehydrated --register --accept-terms
  • Добавляем имена в файл /etc/dehydrated/domains.txt (одна строка = имена одного сайта):
  • xx.ru www.xx.ru
    yy.ru www.yy.ru
  • Создаём /etc/nginx/sites-enabled/xx.ru.conf:
  • server {
    server_name xx.ru www.xx.ru;
    location ^~ /.well-known/acme-challenge {
        alias /var/lib/dehydrated/acme-challenges;
    }
    location / {
        return 301 https://$host$request_uri;
    }
  • Применяем новые настройки:
  • nginx -t
    nginx -s reload
  • И вызываем LetsEncrypt:
  • dehydrated -c
  • Если dehydrated отработает без ошибки, в /var/lib/dehydrated/certs появятся ключи и сертификаты.

Теперь включаем HTTPS:

  • Добавляем в /etc/nginx/sites-enabled/xx.ru.conf:
  • server {
        listen 443 ssl;
        server_name xx.ru www.xx.ru;
    
        ssl_certificate     /var/lib/dehydrated/certs/xx.ru/fullchain.pem;
        ssl_certificate_key /var/lib/dehydrated/certs/xx.ru/privkey.pem;
    }
  • И применяем новые настройки:
  • nginx -s reload

Настраиваем автопродление:

  • Сертификаты выдаются на 90 суток.
  • "dehydrated -c" без ключа "--force" не пытается продлевать сертификат, если он выдан менее 80 суток назад.
  • Поэтому оптимально вызывать продление раз в неделю — т.е. с минимумом неудачных попыток, но с гарантированным попаданием в заключительный 10-дневный интервал.
  • Для этого создаём файл /etc/cron.weekly/Dehydrated (и делаем его исполняемым через "chmod +x"):
  • #!/bin/sh
    
    dehydrated -c -g
  • Обратите внимание: при вызове через "cron" мы используем дополнительный ключ "-g", чтобы в случае ошибки dehydrated не прекратил работу, а продолжил обрабатывать следующие сертификаты.
  • Дополнительно создаём /etc/dehydrated/hook.sh (и тоже делаем исполняемым через "chmod +x"), который вызывается из основного сценария на разных стадиях выполнения. В нашем случае он будет простейшим:
  • #!/bin/sh
    
    test "$1" = "deploy_cert" || exit 0
    
    nginx -s reload
  • Проверяем, что /etc/dehydrated/config или /etc/dehydrated/conf.d/*.sh содержит директиву "HOOK=/etc/dehydrated/hook.sh" — в некоторых дистрибутивах она отсутствует!

Как и зачем использовать подтверждение через DNS вместо HTTP?

  • Сайт или сервис может быть недоступен из внешнего мира (находиться в офисной локальной сети, приватном облаке и т.д), поэтому LetsEncrypt не сумеет обратиться к нему извне, чтобы проверить владельца.
  • Либо по каким-то причинам мы вынуждены запускать клиентскую утилиту с другого компьютера.
  • В этом случае LetsEncrypt позволяет подтверждать владение доменом через специальные DNS-записи.
  • Для этого dehydrated должен запускаться с дополнительным ключом "-t dns-01".
  • Сценарий hook.sh становится примерно таким:
  • #!/bin/sh
    
    case "$1" in
        "deploy_challenge")  printf "Please add to DNS:\n_acme-challenge.%s. %d in TXT \"%s\"\n" "${2}" "${TTL}" "${4}"
                            read -p "Configure, then press Enter..." x  ;;
    
        "clean_challenge" )  printf "Please delete from DNS:\n_acme-challenge.%s. %d in TXT \"%s\"\n" "${2}" "${TTL}" "${4}" ;;
    
        "deploy_cert"     )  ;; # optional: /path/to/deploy_cert.sh "$@" ;;
    esac
  • Документация к Dehydrated содержит примеры автоматического создания-удаления DNS-записей через API различных DNS-провайдеров — Cloudflare, GoDaddy и т.д.

Мы используем cookie, чтобы сделать сайт удобнее