- PVSM.RU - https://www.pvsm.ru -

Linux Kernel TLS и Nginx

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

Предыстория

В далеком 2015 году Randall Stewart и Scott Long из компании Netflix выступили на конференции AsiaBSDCon2015 c докладом [1] об оптимизации раздачи шифрованного контена. Основной посыл доклада — необходимо переносить шифрование данных в ядро операционной системы для уменьшения количества копирований данных между kernel-space и user-space и использовать оптимизированный неблокирующий системный вызов sendfile(). Тема оказалась очень перспективной и уже в 2016 году на конференции Netdev 1.2 [2] Dave Watson из Facebook выступил с докладом [3] о необходимости создания TLS сокетов в ядре Linux, а Boris Pismenny, Ilya Lesokhin и Liran Liss из Mellanox — с докладом [4] о возможности использовать аппаратное ускорение TLS в сетевых картах. Так же тема обсуждалась на HighLoad++ 2017 докладчиком от Tempesta Technologies.

Поддержка в ядре Linux

Итогом всех этих разговоров стало появление Kernel TLS в ядре Linux 4.13 (2017 год) c поддержкой TLSv1.2 и шифра AES128-GCM. Первоначально поддерживалось шифрование только исходящего трафика, поддержка дешифрования появилась позже в ядре Linux 4.17 (2018 год). В версии 5.1 добавили поддержку TLSv1.3 и AES256-GCM, а в версии 5.2 еще и шифр AES128-CCM (2019 год).

Поддержка в user-space

Во всех указанных выше докладах рассказывалось, что в ядро можно поместить только шифрование полезных данных, а все согласования и контрольные сообщения TLS необходимо обрабатывать всё так же в user-space. И для этого использовалась модифицированная версия OpenSSL. Однако на момент публикации докладов в открытом доступе не было информации о том, какие именно модификации в эту широкоизвестную библиотеку надо сделать, чтобы поддержать функционал. Пожалуй, единственным доступным примером использования Linux Kernel TLS была статья в блоге Filippo Valsorda Playing with kernel TLS in Linux 4.13 and Go [5], появившаяся сразу после выхода ядра Linux 4.13. И, хотя она показывала действительный пример использования технологии, это не принесло понимания в то, как же использовать технологию в реальных проектах. Ведь очень мало кто самостоятельно пишет WEB-сервер для своего проекта, обычно все используют известные и проверенные временем инструменты.

Поддержка в OpenSSL

Первые обсуждения технологии в OpenSSL появились летом 2017 года незадолго до выхода ядра Linux 4.13 (PR 3631 [6]), однако процесс обсуждения шел очень и очень медленно, первые реальные замечания появились в октябре (уже после выхода ядра 4.13), а собственно рабочий вариант [7] начали обсуждать в феврале 2018. К моменту согласования всех исправлений, окно добавления нового функционала в OpenSSL 1.1.1 было закрыто, и поддержку Kernel TLS перенесли в следующий релиз. С того времени добавили поддежку Kernel TLS RX, и SSL_sendfile() — вызов соответствующего syscall с небольшой обработкой возможных ситуаций в протоколе TLS. Но сейчас на дворе 2020 год, OpenSSL 3.0 еще не вышел, TLSv1.3 поддерживается подавляющим большинством браузеров, а шифр AES128-GCM активно вытесняется более стойким AES256-GCM. Поэтому я взял на себя смелость и отправил Pull Request [8] на поддержку новых шифров и TLSv1.3 в надежде, что его примут до нового релиза библиотеки.

Поддержка в WEB-серверах

Но поддержать Kernel TLS в TLS библиотеке мало. В докладах про технологию говорилось, что максимальную эффективность можно получить используя отправку без копирования данных в user-space — используя системный вызов sendfile(). И серверное приложение должно уметь различать ситуации, когда можно использовать sendfile() на сокете с TLS, а когда необходимо «по-старинке» делать read()/SSL_write(). Некоторые подвижки в сторону добавления функционала в Nginx появились в апреле 2019 года, но в основной код изменения приняты не были. Позиция разработчиков в том, что API для этих функиций в OpenSSL еще не утвержден, а собственно сам код, предложенный в патче, не выглядит достаточно портируемым на разные платформы. Скажу честно, код не только выглядит не очень красиво, но и содержит ошибки, которые не позволяют собрать Nginx без дополнительных исправлений. Поддержки Kernel TLS в других веб-серверах я вообще не смог найти (может быть кто-то видел — подскажите в комментах).

И что же делать?

Пока сообщество ожидает выхода релиза OpenSSL 3.0, чтобы начать разработку поддержки Kernel TLS в Nginx в условиях стабильного API, я пошел иным путём. В своём уютном уголочке GitHub я сделал 2 вещи:

  1. Создал форк [9] OpenSSL и бэкпортировал всё связанное с Kernel TLS в стабильную версию OpenSSL 1.1.1 (ветка OpenSSL_1_1_1-ktls). Специально для того, чтобы иметь возможность проверять функционал в условиях стабильной работы остальной части библиотеки. По мере выхода стабильных версий стараюсь делать rebase, чтобы форк был актуальным.
  2. Создал форк [10] Nginx, в который добавил (на основе патча от Mellanox) поддержку вызова SSL_sendfile() в условиях, когда это действительно возможно и с необходимыми проверками SSL сокета, и возможность включения/выключения функционала через конфигурационную переменную. Помимо этой фичи в моем форке так же есть несколько патчей, немного оптимизирующих работу Nginx, и исправляющих некоторые баги (ветка master-feature). По мере возможностей я стараюсь делать rebase на основе master-ветки основного репо Nginx, чтобы поддерживать форк в условно-актуальном состоянии.

Предлагаю присоединиться к тестированию всех желающих. Замечания и исправления к коду можно кидать в Issues в GitHub. Собрать этот Nginx с этим OpenSSL и включенной поддержкой Kernel TLS можно добавив параметры к скрипту configure:

./configure --with-openssl=<OpenSSL-fork-dir> --with-openssl-opt="enable-ktls"

В настоящий момент у меня нет возможности протестировать эту связку Nginx + OpenSSL под серьезной нагрузкой, чтобы подтвердить показатели из коммента к патчу Nginx, поэтому если вдруг кто сможет замерить разницу в нагрузке на CPU на больших скоростях отдачи файлов — было бы здорово получить графики и добавить их в статью для визуального понимания эффекта.

Автор: vadimfedorenko

Источник [11]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/nginx/352860

Ссылки в тексте:

[1] докладом: https://people.freebsd.org/~rrs/asiabsd_2015_tls.pdf

[2] Netdev 1.2: https://netdevconf.info/1.2/accepted-sessions.html

[3] докладом: https://netdevconf.info/1.2/session.html?dave-watson

[4] докладом: https://netdevconf.info/1.2/session.html?boris-pismenny

[5] Playing with kernel TLS in Linux 4.13 and Go: https://blog.filippo.io/playing-with-kernel-tls-in-linux-4-13-and-go

[6] PR 3631: https://github.com/openssl/openssl/pull/3631

[7] рабочий вариант: https://github.com/openssl/openssl/pull/5253

[8] Pull Request: https://github.com/openssl/openssl/pull/11589

[9] форк: https://github.com/junjunk/openssl

[10] форк: https://github.com/junjunk/nginx

[11] Источник: https://habr.com/ru/post/501010/?utm_source=habrahabr&utm_medium=rss&utm_campaign=501010