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

Данный пост написан вследствие победы желания докопаться до сути над усталостью, сонливостью, соблазном опрокинуть очередную бутылочку пива пятничным вечером. Сразу скажу, что ничего супер сложного не раскрываю, всего лишь включение 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, чтобы протестировать новый протокол можно было прямо на локальной машине, можно найти — здесь.

На этом точно можно успокоиться и пойти домой :)

Надеюсь, кому-то будет полезно. Спасибо!

Комментарии (10)


  1. vesper-bot
    23.12.2017 08:28

    > после инсталляции nginx нет ни init.d скрипта, ни конфига для systemd. Лечится так

    Проще было бы скопировать nginx.service в /backup до удаления пакета, имхо.

    Кроме того, а почему на openssl-1.1.1 не сделать checkinstall? А то в nginx делаете, а выше — нет.


    1. pashaxp Автор
      23.12.2017 10:20

      Спасибо за комментарий! Полностью согласен, не делал checkinstall для openssl, так как это только тестовая среда, и вероятность того, что переустанавливать nginx придётся намного выше, чем openssl. По сути, пока подбирал рабочую конфигурацию, openssl ни разу не сносил, в то время, как nginx — раза 3 или 4. На продакшене, конечно, надо делать checkinstall на все пакеты.


  1. Akdmeh
    23.12.2017 12:01

    А какой смысл в TLS 1.3 кроме исследовательского?
    Как я понимаю, когда он «выйдет с беты» — многие приложения включат его по умолчанию и в вашей инструкции отпадет необходимость.
    Just for fun, или это может реально предоставить какую-то дополнительную безопасность?


    1. pashaxp Автор
      23.12.2017 12:53

      В моём случае — действительно, увидеть, что «работает». Однако, полагаю, что для многих будет полезно включить новый протокол в целях, к примеру, разработки, подготовиться к тому, чтобы обновить свои приложения заблаговременно, протестировать их работу. Ощутить, так сказать, на себе все эти преимущества .


  1. Coocos
    23.12.2017 17:04

    Какой смысл в пункте 5 устанавливать пакеты отдельными командами apt-get install?


    1. pashaxp Автор
      23.12.2017 17:06

      Записывал шаги по мере появления ошибок конфигуратора по поводу недостающих пакетов, в свою очередь, можете скомпоновать как Вам удобнее


  1. chemtech
    23.12.2017 17:28
    +1

    Вот есть заметка как скомпилировать rpm nginx mainline (1.13) с openssl 1.1.1 с поддержкой TLSv1.3
    gist.github.com/StarDuster/0d6fb37132fe64c0e7f60631e02b0f0d
    Правда там RPM, а у вас DEB.


    1. pashaxp Автор
      23.12.2017 17:32

      Спасибо! Читателям, думаю, будет полезно дополнить мануал rpm-based примером


  1. Sleuthhound
    23.12.2017 17:47

    cd openssl-tls1.3-draft-18
    ./config
    make && make install


    1. Не делайте так ни на dev ни на prod среде если у вас пакетный дистрибутив debian. Получите гору мусора в системе, про которую потом забудите и в последствии это приведет к массе проблем.
    2. В случае nginx в этом нет необходимости, openssl линкуется статически к nginx и он не зависит от системной версии openssl.

    Уже точно не помню, но точно больше часа ушло на то, чтобы найти источник, где упоминалось про шифры, и что в TLS 1.3 они должны быть такими:


    На самом деле их больше:
    TLS13-AES-256-GCM-SHA384
    TLS13-CHACHA20-POLY1305-SHA256
    TLS13-AES-128-GCM-SHA256
    TLS13-AES-128-CCM-8-SHA256
    TLS13-AES-128-CCM-SHA256

    Посмотреть можно так:
    # ./openssl-1.1.1-tls1.3-draft-18/.openssl/bin/openssl ciphers -v 'ALL:!ADH:@STRENGTH' | grep TLS13
    TLS13-AES-256-GCM-SHA384 TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
    TLS13-CHACHA20-POLY1305-SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
    TLS13-AES-128-GCM-SHA256 TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD
    TLS13-AES-128-CCM-8-SHA256 TLSv1.3 Kx=any      Au=any  Enc=AESCCM8(128) Mac=AEAD
    TLS13-AES-128-CCM-SHA256 TLSv1.3 Kx=any      Au=any  Enc=AESCCM(128) Mac=AEAD
    


    cd nginx-1.13.7
    ./configure --with-cc-opt='-g -....


    Правильнее было бы сделать apt-get source nginx подправить debian/rules и собрать nginx через dpkg-buildpackage и в итоге получить DEB пакет и ни мучиться, если интересно, то почитайте в моей статье про пересборку Nginx c поддержкой ALPN.


    1. pashaxp Автор
      23.12.2017 18:52

      супер, благодарю за дополнение!