Включаем поддержку TLS v1.3 в Nginx на примере Debian 9

Всем доброго времени суток!

Данный пост написан вследствие победы желания докопаться до сути над усталостью, сонливостью, соблазном опрокинуть очередную бутылочку пива пятничным вечером.
Сразу скажу, что ничего супер сложного не раскрываю, всего лишь включение TLS v1.3 в Nginx.
image

Наверняка на Хабре найдутся те, кто уже 100 раз это делал, поэтому данная статья — больше для новичков или для тех, кто хочет найти готовое решение в виде мануала, не тратя много времени на поиски, как я, например. Вспомнив, что давно не писал на Хабре и поставив статье метку «tutorial», принялся за дело.

Беглый поиск по Хабру показал, что на данную тему постов не было, а на «тостере» был топик, где вопрошающему ответили в комментариях в стиле «сам дурак».
Получается, вопрос остаётся без ответа, по крайней мере, в открытом доступе не нашёл рабочих мануалов. Не хорошо, надо исправить ситуацию :)

А началось всё с того, что в очередной раз настроив SSL сертификат от Letsencrypt (издатель сертификата совершенно не важен в рамках данной статьи), когда вроде бы всё было хорошо,
image
(тест настройки SSL сертификата на ssllabs.com, обзор)

и можно было бы спокойно закрыть таск в Jira и идти домой, но тут мой взор привлёк пункт о поддержке TLS 1.3. Точнее, там было указано, что этой самой поддержки TLS 1.3 как раз нет!

image
(тест настройки SSL сертификата на ssllabs.com, детали)

Ну что ж, как говорится,
image

Конечно, я видел этот пункт и раньше, но как-то не придавал значения, всё было не до того. А в этот раз сказал себе мысленно: «хватит это терпеть!» Надо всё же разобраться, как включить новый протокол.

Немного предыстории. Выход 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-е в результатах теста

image
(тест настройки 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. Должно быть вот так

image

и вот так

image

Как включить поддержку TLS 1.3 в браузерах Chrome, Firefox, чтобы протестировать новый протокол можно было прямо на локальной машине, можно найти — здесь.

На этом точно можно успокоиться и пойти домой :)
Надеюсь, кому-то будет полезно. Спасибо!

© Habrahabr.ru