ADG Оставить заявку
Блог Инфраструктура 4 мин чтения

nginx как TLS-терминатор и скрипт ротации сертификатов: пока Let's Encrypt в закрытой бете

Let's Encrypt объявил публичную бету на осень. Пока ждём - настроили nginx как TLS-терминатор с автоматической ротацией платных сертификатов через скрипт и cron.

Контекст момента

Let's Encrypt объявляет публичную бета-программу на осень 2015 - бесплатные автоматически обновляемые TLS-сертификаты для всех

В начале августа Let's Encrypt объявил, что публичная бета-программа откроется осенью 2015 года. Идея - бесплатные TLS-сертификаты с автоматической выдачей и продлением через ACME-протокол, без ручной работы с CA и без ежегодных трат. На бумаге это выглядит как что-то, что меняет подход к сертификатам у всей индустрии. На практике - пока закрытая бета, есть список ожидания, широко недоступно.

Между тем у нескольких наших клиентов накопилась задача навести порядок с TLS: платные сертификаты, у которых истекает срок и никто не следит; nginx-конфигурации, которые помнят TLSv1.0 как что-то нормальное; отсутствие автоматического обновления вообще как явления.

Решили не ждать Let's Encrypt и закрыть вопрос тем, что есть сейчас.

Что именно не так с типичной nginx-конфигурацией TLS

Типичная история выглядит так: кто-то когда-то поставил сертификат, написал базовый nginx-блок, задокументировал в конфлюэнсе дату истечения - и забыл. Сертификат живёт два года, всё хорошо. За две недели до истечения приходит письмо от CA (если повезло и адрес ещё актуальный), кто-то вручную скачивает новый файл, перезапускает nginx. Или не приходит письмо, или некому читать, и в один прекрасный день браузер показывает «небезопасное соединение».

Второй слой проблемы - настройки шифрования. nginx с дефолтными параметрами ssl_protocols включает TLSv1.0 и TLSv1.1. В 2015 году это уже воспринимается как плохой тон - PCI DSS требует отключить TLSv1.0 с 2015 года, и хотя у большинства клиентов нет прямой обязанности следовать PCI DSS, это хороший ориентир.

Третий момент - nginx как TLS-терминатор перед приложением. Если приложение - это что-то на Java или PHP, которое само умеет HTTPS, часто возникает вопрос «а зачем nginx вообще». Ответ - управляемость: cipher suites, HSTS, OCSP Stapling, HTTP/2 (поддержка анонсирована, появится в ближайших версиях nginx) - всё это настраивается в одном месте, независимо от стека приложения. Приложение слушает 127.0.0.1:8080, nginx принимает 443 снаружи.

Скрипт ротации сертификатов

Пока автоматической выдачи нет, сделали полуавтоматику. Схема:

  • Сертификаты хранятся в одном месте - /etc/ssl/certs/managed/, каждый сайт в своей директории. Никаких сертификатов, разбросанных по конфигурационным папкам nginx.
  • Скрипт проверяет срок - через openssl x509 -enddate -noout получаем дату истечения, сравниваем с текущей. Если до истечения меньше 30 дней - уведомление. Если меньше 7 - уведомление повторно и запись в лог с повышенным приоритетом.
  • Замена сертификата - отдельный скрипт, который принимает новый файл, кладёт его на место, делает nginx -t и только при успешной проверке конфига - nginx -s reload. Если nginx -t упал - всё откатывается, старый сертификат остаётся.
  • Cron + email - проверка ежедневно, письмо на почту инженера. Не самое изящное решение, но работает и не требует отдельного мониторинга.

Всё это написано на bash, никаких внешних зависимостей. Ansible раскладывает скрипты и cron-задачи на серверы клиентов - об этом писали применительно к ansible-ролям, схема та же.

Конфигурация nginx

Минимальный разумный блок для TLS-терминатора в 2015 году:

server {
    listen 443 ssl;
    server_name example.ru;

    ssl_certificate     /etc/ssl/certs/managed/example.ru/fullchain.pem;
    ssl_certificate_key /etc/ssl/certs/managed/example.ru/privkey.pem;

    ssl_protocols       TLSv1.1 TLSv1.2;
    ssl_ciphers         'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:!aNULL:!MD5:!DSS';
    ssl_prefer_server_ciphers on;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;

    add_header Strict-Transport-Security "max-age=31536000" always;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name example.ru;
    return 301 https://$host$request_uri;
}

TLSv1.0 убрали. HSTS добавили. Набор шифров - не идеальный, но без MD5 и aNULL. OCSP Stapling можно добавить отдельно, если CA предоставляет OCSP-responder - у большинства коммерческих CA он есть.

Проверяем конфиг через ssllabs.com/ssltest - получаем A или B в зависимости от того, поддерживаем ли мы Forward Secrecy на всех шифрах. До A+ с HSTS preload пока не доходим - требуется более длинный max-age и includeSubDomains, что надо согласовывать с клиентом.

Чего ждём от Let's Encrypt

Главный эффект будет не в экономии на сертификатах - это несущественная сумма. Главный эффект - исчезновение ручных операций и человеческого фактора. Когда продление автоматическое и срок сертификата 90 дней вместо двух лет - срок истечения перестаёт быть инцидентом.

ACME-протокол, который Let's Encrypt продвигает, - это стандартизированный механизм взаимодействия между клиентом и CA. Сейчас каждый CA - это отдельный личный кабинет, отдельный процесс, отдельный формат CSR в лучшем случае. ACME это в теории унифицирует, и если протокол приживётся - другие CA тоже могут его поддержать.

Пока - ждём публичной беты, записались в список ожидания, в сентябре планируем попробовать на одном из инфраструктурных проектов в числе первых.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.