Включаем поддержку TLS v1.3 в Nginx на примере Debian 9
Всем доброго времени суток!
Данный пост написан вследствие победы желания докопаться до сути над усталостью, сонливостью, соблазном опрокинуть очередную бутылочку пива пятничным вечером.
Сразу скажу, что ничего супер сложного не раскрываю, всего лишь включение TLS v1.3 в Nginx.
Наверняка на Хабре найдутся те, кто уже 100 раз это делал, поэтому данная статья — больше для новичков или для тех, кто хочет найти готовое решение в виде мануала, не тратя много времени на поиски, как я, например. Вспомнив, что давно не писал на Хабре и поставив статье метку «tutorial», принялся за дело.
Беглый поиск по Хабру показал, что на данную тему постов не было, а на «тостере» был топик, где вопрошающему ответили в комментариях в стиле «сам дурак».
Получается, вопрос остаётся без ответа, по крайней мере, в открытом доступе не нашёл рабочих мануалов. Не хорошо, надо исправить ситуацию :)
А началось всё с того, что в очередной раз настроив SSL сертификат от Letsencrypt (издатель сертификата совершенно не важен в рамках данной статьи), когда вроде бы всё было хорошо,
(тест настройки SSL сертификата на ssllabs.com, обзор)
и можно было бы спокойно закрыть таск в Jira и идти домой, но тут мой взор привлёк пункт о поддержке TLS 1.3. Точнее, там было указано, что этой самой поддержки TLS 1.3 как раз нет!
(тест настройки SSL сертификата на ssllabs.com, детали)
Ну что ж, как говорится,
Конечно, я видел этот пункт и раньше, но как-то не придавал значения, всё было не до того. А в этот раз сказал себе мысленно: «хватит это терпеть!» Надо всё же разобраться, как включить новый протокол.
Немного предыстории. Выход TLS v1.3 это не новость последних дней, работа над новой версией ведётся уже много лет. Со времени анонсирования TLS v1.2 прошло почти 10 лет. И вот, в апреле 2017 года вышла версия nginx 1.13.0, которая поддерживает TLS v1.3.
Если уважаемый читатель думает, что достаточно установить nginx >=1.13.0 и прописать
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
в настройках virtualhost, не спешите делать выводы и расходиться, я тоже попался на этот «развод» :)
При установленной версии nginx >=1.13.0 и при наличии вышеуказанной строчки в конфиге
nginx -t
не выдаёт ошибок, это плюс, но тест ssllabs показывает, что TLS 1.3 всё же не включён, а это большой минус.
Позже, изучив требования, я понял, что заставить работать новую версию протокола можно путём компиляции nginx >=1.13.0 с поддержкой openssl >=1.1.1, который сейчас разработке, причём версия черновика протокола — строго 18. Если брать другие версии draft, результат будет отличаться. Я не вдавался в подробности, почему-то на текущий момент за основу взяли именно 18ю версию черновика, о чём любезно сообщает ssllabs в своём disclaimer-е в результатах теста
(тест настройки SSL сертификата на ssllabs.com, детали)
Порядок действий:
1. Сохраняем содержимое /etc/nginx куда-нибудь в /backup, если до этого уже настроили требуемый virtualhost, чтобы не делать всё заново. Можно сразу же сделать
apt-get remove --purge nginx
2. Скачиваем и распаковываем исходники openssl 1.1.1 tls1.3 draft 18.
wget https://github.com/openssl/openssl/archive/tls1.3-draft-18.zip
3. Подготовительные работы для openssl
apt-get install build-essential libpcre3 libpcre3-dev zlib1g-dev checkinstall
4. Собираем openssl.
Переходим в каталог с распакованным содержимым архива openssl
cd openssl-tls1.3-draft-18
./config
make && make install
5. Подготовительные работы для nginx
apt-get build-dep nginx
apt-get install libossp-uuid-dev
apt-get install libxml2 libxml2-dev
apt-get install libxslt-dev
apt-get install libgd-dev
apt-get install libgeoip-dev
wget http://nginx.org/download/nginx-1.13.7.tar.gz
tar xvf nginx-1.13.7.tar.gz
cd nginx-1.13.7
./configure --with-cc-opt='-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-file-aio --with-threads --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_secure_link_module --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module --with-ld-opt=-lossp-uuid --with-cc-opt=-I/usr/include/ossp --with-openssl=/root/openssl-tls1.3-draft-18 --with-openssl-opt='enable-tls1_3'
При запуске конфигуратора nginx важно указать правильный путь к распакованным исходникам openssl нужной нам версии. У меня это /root/openssl-tls1.3-draft-18, если у вас другой путь к каталогу с исходниками openssl, тогда внесите изменения в опцию --with-openssl=.
На этапе выполнения конфигуратора openssl и/или nginx могут появиться ошибки, в основном лечатся обычным гуглением. Как правило, будет не хватать каких-то lib, ничего особенного.
90% решается тем, что берём название компонента/пакета, которого не нашёл конфигуратор и делаем
apt-get install [название пакета]-dev
и снова запускаем конфигуратор. Повторяем процедуру, пока конфигуратор не отработает без ошибок.
Затем
gawk -i inplace '/pthread/ { sub(/-lpthread /, ""); sub(/-lpthread /, ""); sub(/\\/, "-lpthread \\"); print } ! /pthread/ { print }' "objs/Makefile"
Последняя команда устраняет баг компиляции nginx. Он связан с тем, что openssl v1.1.1 сейчас только на стадии разработки, и в nginx по этому поводу что-то не учтено на данный момент.
6. Компиляция nginx
Запускаем
make
После успешной компиляции не спешите делать make install, лучше сделать всё красиво
mkdir /usr/lib/nginx
mkdir /var/log/nginx/
mkdir -p /var/lib/nginx/body
checkinstall --pkgname=nginx --pkgversion=1.13.7 --nodoc
Создавать каталоги надо до выполнения checkinstall, так как после сборки deb пакета будет попытка установить его в систему, если какого-то каталога не будет хватать, завершится fail-ом.
На этом этапе nginx должен быть установлен в системе, но если выполнить
nginx -t
выдаст, что nginx — нет такой команды/бинарника. Не спешите расстраиваться, всё дело в том, что установлен он не совсем по стандартному пути PATH, а вот здесь — /usr/share/nginx/sbin/nginx
Cледовательно, выполнять нужно
/usr/share/nginx/sbin/nginx -t
Чтобы было красиво, сделаем
ln -s /usr/share/nginx/sbin/nginx /usr/sbin/nginx
и тогда запускаем
nginx -t
себе на здоровье и без ошибок.
Небольшой минус — после инсталляции nginx нет ни init.d скрипта, ни конфига для systemd.
Лечится так (один из способов):
vim /lib/systemd/system/nginx.service
это новый файл, мы его создаём сами, вписываем содержимое
[Unit]
Description=A high performance web server and a reverse proxy server
After=network.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/share/nginx/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/share/nginx/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/share/nginx/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed
[Install]
WantedBy=multi-user.target
Затем
systemctl enable nginx.service
Возвращаем обратно каталог nginx из /backup в /etc, лучше перед этим удалить /etc/nginx, так как туда что-то было положено после выполнения checkinstall и dpkg -i
Готовы к запуску nginx
systemctl start nginx
Всё должно подняться, если нет, небольшой troubleshooting будет даже полезен.
Ещё один маленький штрих. Необходимо донастроить nginx virtualhost, активировать новый набор шифров.
Уже точно не помню, но точно больше часа ушло на то, чтобы найти источник, где упоминалось про шифры, и что в TLS 1.3 они должны быть такими:
ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-256-GCM-SHA384:ECDHE:!COMPLEMENTOFDEFAULT;
В целом конфигурация nginx у меня получилась такая:
server {
listen 443 ssl http2;
server_name tls1.3.netforce.ua;
access_log /var/log/nginx/tls1.3.netforce.ua_access.log combined;
error_log /var/log/nginx/tls1.3.netforce.ua_error.log;
ssl on;
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_certificate "/etc/nginx/ssl/server.crt";
ssl_certificate_key "/etc/nginx/ssl/server.key";
add_header Strict-Transport-Security "max-age=31536000";
ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-256-GCM-SHA384:ECDHE:!COMPLEMENTOFDEFAULT;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
root /var/www/tls1.3;
index index.html;
}
server {
listen 80;
server_name tls1.3.netforce.ua;
rewrite ^(.*)$ https://tls1.3.netforce.ua$1 permanent;
}
Это не готовая конфигурация для production среды, здесь нет кэширования, сжатия и других полезных и важных вещей. Напомню, цель статьи — это активировать TLS 1.3 и только.
После редактирования проверяем, что всё хорошо
nginx -t
Если да, делаем рестарт nginx и запускаем тест на ssllabs. Должно быть вот так
и вот так
Как включить поддержку TLS 1.3 в браузерах Chrome, Firefox, чтобы протестировать новый протокол можно было прямо на локальной машине, можно найти — здесь.
На этом точно можно успокоиться и пойти домой :)
Надеюсь, кому-то будет полезно. Спасибо!