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

OpenSSL 1.0.2 на продакшне: ALPN, честный ECDH и сборка DEB без сюрпризов

Обновляем OpenSSL до 1.0.2 на серверах под управлением: проверяем nginx и Postfix, включаем ALPN, чиним цепочку сборки DEB-пакетов.

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

OpenSSL 1.0.2 выходит с поддержкой ALPN, улучшенным ECDH и новыми API для TLS-расширений

OpenSSL 1.0.2 вышел в январе, но мы не торопились: после FREAK и череды патчей хотелось дать релизу отстояться пару месяцев, посмотреть, не полетят ли hotfix-ы один за другим. Не полетели. Плюс к нам начали поступать вопросы про HTTP/2 и ALPN - и тут стало понятно, что откладывать дальше нет смысла.

Зачем вообще 1.0.2

Три причины, которые реально влияли на наши серверы.

ALPN (Application-Layer Protocol Negotiation). Без поддержки ALPN на стороне OpenSSL nginx не может согласовать HTTP/2 по TLS - протокол согласования выбирается на этапе handshake, и без нужного расширения сервер просто не объявит h2 в списке. С 1.0.2 это работает нормально, npn_protocols (для SPDY/NPN) уступит место http2 в строке listen - как только nginx доберётся до нативного HTTP/2. Пока используем как подготовительный шаг.

Честный ECDH с per-session ключами. В 1.0.1 для настройки ECDH надо было явно вызывать SSL_CTX_set_tmp_ecdh с захардкоженной кривой - и nginx до определённых версий просто не умел автоматически выбирать кривую под параметры клиента. В 1.0.2 добавлен SSL_CTX_set_ecdh_auto - сервер сам подбирает кривую из набора, согласованного с клиентом. Forward secrecy работает корректно без дополнительных костылей в конфиге.

API для TLS-расширений. Это скорее история для тех, кто пишет своё на OpenSSL, нам напрямую не нужно - но несколько библиотек в стеке клиентов от этого выигрывают.

Совместимость с nginx

С nginx проблем меньше, чем ожидали - при условии, что собираешь OpenSSL сам, а не берёшь пакет из репозитория дистрибутива. Ubuntu 14.04 тащит 1.0.1f, Debian Wheezy - 1.0.1e, и ни там ни там 1.0.2 не будет через apt-get upgrade.

Конфиг nginx при переходе на 1.0.2 менять почти не пришлось. Одно изменение: убрали явный ssl_ecdh_curve prime256v1 - теперь это ломает auto-selection. Оставили просто:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE+AESGCM:DHE+AESGCM:ECDHE+AES:DHE+AES:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

Проверка через openssl s_client -connect host:443 показывает ECDHE-RSA-AES128-GCM-SHA256 в качестве согласованного шифра - всё как надо.

Postfix: тут было веселее

Postfix линкуется с системным OpenSSL напрямую, и при замене разделяемой библиотеки иногда ведёт себя непредсказуемо. На одном сервере после обновления Postfix начал выдавать в лог:

warning: TLS library problem: error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher

Причина оказалась в том, что на этом сервере в smtpd_tls_mandatory_ciphers стоял high, а шифры из группы HIGH пересчитались при смене версии OpenSSL - несколько старых суитов туда перестали попадать. Некоторые клиенты (в основном старые MTA) не могли согласовать шифр и падали с ошибкой.

Решение: переключить на явный список шифров вместо символических групп, то же самое что делаем для nginx:

smtpd_tls_mandatory_ciphers = medium
smtpd_tls_ciphers = medium
smtpd_tls_exclude_ciphers = aNULL,eNULL,EXPORT,DES,RC4,3DES,MD5

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

Сборка DEB-пакетов: цепочка сломалась

Это был самый трудоёмкий момент. У нас для нескольких клиентов собраны кастомные DEB-пакеты nginx, собранные с нужной версией OpenSSL как статической зависимостью. Стандартный pipeline: скачать OpenSSL, собрать статику, собрать nginx с --with-openssl=.

При переходе на 1.0.2 сборка nginx упала на этапе компиляции с ошибкой в ssl_lib.c - конфликт объявлений функций. Покопавшись, нашли две причины:

Первое - в 1.0.2 изменилась сигнатура нескольких внутренних функций ECDH, и старый патч для nginx (из нашей репы) прилетал поверх несовместимых хедеров. Патч пришлось выкинуть - в актуальном nginx он уже не нужен, функциональность включена нативно.

Второе - сборочный скрипт вызывал ./config вместо ./Configure с явным таргетом. На Ubuntu 14.04 ./config автоматически выбирал linux-x86_64, и раньше это работало. С 1.0.2 поведение изменилось - скрипт стал подтягивать дополнительные флаги компилятора, несовместимые с версией GCC на билд-хосте. Фикс простой: явно указать таргет:

./Configure linux-x86_64 --prefix=/opt/openssl-1.0.2 no-shared no-idea no-mdc2 no-rc5
make && make install_sw

После этого nginx собрался без вопросов, пакет подписан, выложен во внутренний apt-репозиторий, раскатан через Ansible.

Что проверяли после

После обновления прогоняли testssl.sh и смотрели на Qualys SSL Labs. Оценка не изменилась - была A, осталась A. Но в деталях по ECDH стало чище: Labs показывает параметры кривой и флаг forward secrecy по каждому шифру отдельно, и теперь там везде зелёный.

На сопровождении держим несколько десятков серверов с разным стеком, и OpenSSL 1.0.2 пока прошёл без инцидентов на всех, где обновили. Postfix-история повторилась на двух серверах - теперь это пункт в нашем чеклисте обновления: после смены OpenSSL всегда проверяем почтовые логи на no shared cipher.

ALPN для HTTP/2 включили на двух тестовых серверах клиентов - nginx 1.7.x с поддержкой SPDY/NPN, переход на h2 пока в работе. Это отдельная история.

Контакт

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

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