- PVSM.RU - https://www.pvsm.ru -
В этой статье я расскажу об истории развития и текущем состоянии технологии ускорения раздачи контента в 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.
Итогом всех этих разговоров стало появление 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 год).
Во всех указанных выше докладах рассказывалось, что в ядро можно поместить только шифрование полезных данных, а все согласования и контрольные сообщения 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 появились летом 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 в надежде, что его примут до нового релиза библиотеки.
Но поддержать 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 вещи:
Предлагаю присоединиться к тестированию всех желающих. Замечания и исправления к коду можно кидать в 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
Нажмите здесь для печати.