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