Linux Kernel TLS et Nginx

Dans cet article, je parlerai de l'histoire du développement et de l'état actuel de la technologie pour accélérer la distribution de contenu dans les connexions TLS en transférant le chiffrement au cœur du système d'exploitation, ainsi que de ma contribution au développement de cette direction.

Contexte


En 2015, Randall Stewart et Scott Long de Netflix ont fait une présentation lors de la conférence AsiaBSDCon2015 sur l'optimisation de la distribution de contenu crypté. Le message principal du rapport est de transférer le chiffrement des données vers le noyau du système d'exploitation afin de réduire le nombre de copies de données entre l'espace noyau et l'espace utilisateur et d'utiliser l'appel système sendfile () optimisé non bloquant. Le sujet s'est avéré très prometteur et déjà à la conférence Netdev 1.2 de 2016, Dave Watson de Facebook a fait une présentation sur la nécessité de créer des sockets TLS dans le noyau Linux, et Boris Pismenny, Ilya Lesokhin et Liran Liss de Mellanox ont fait une présentationsur la possibilité d'utiliser l'accélération matérielle TLS dans les cartes réseau. Le sujet a également été abordé lors de HighLoad ++ 2017 par un conférencier de Tempesta Technologies.

Prise en charge du noyau Linux


Le résultat de toutes ces conversations a été l'apparition de Kernel TLS dans le noyau Linux 4.13 (2017) avec prise en charge de TLSv1.2 et du chiffrement AES128-GCM. Initialement, le chiffrement du trafic sortant uniquement était pris en charge, la prise en charge du déchiffrement est apparue plus tard dans le noyau Linux 4.17 (2018). Dans la version 5.1, ils ont ajouté la prise en charge de TLSv1.3 et AES256-GCM, et dans la version 5.2, ils ont également ajouté le chiffrement AES128-CCM (2019).

Support dans l'espace utilisateur


Dans tous les rapports mentionnés ci-dessus, il a été dit que seul le cryptage des données utiles peut être placé dans le noyau, et toutes les approbations TLS et les messages de contrôle doivent être traités de la même manière dans l'espace utilisateur. Et pour cela, une version modifiée d'OpenSSL a été utilisée. Cependant, au moment de la publication des rapports dans le domaine public, il n'y avait aucune information sur les modifications à apporter à cette bibliothèque bien connue qui devaient être apportées pour prendre en charge la fonctionnalité. Peut-être le seul exemple disponible d'utilisation de Linux Kernel TLS était l'article de blog Filippo Valsorda Jouer avec kernel TLS sous Linux 4.13 et Go, qui est apparu immédiatement après la sortie du noyau Linux 4.13. Et, bien qu'il montre un exemple valable d'utilisation de la technologie, il ne permet pas de comprendre comment utiliser la technologie dans des projets réels. Après tout, très peu de gens écrivent eux-mêmes un serveur WEB pour leur projet, généralement tout le monde utilise des outils connus et éprouvés.

Prise en charge dans OpenSSL


Les premières discussions sur la technologie dans OpenSSL sont apparues à l'été 2017 peu de temps avant la sortie du noyau Linux 4.13 ( PR 3631 ), mais le processus de discussion a été très, très lent, les premiers vrais commentaires sont apparus en octobre (après la sortie du noyau 4.13), et la version de travail réellea commencé à discuter en février 2018. Au moment où toutes les corrections ont été approuvées, la fenêtre d'ajout de nouvelles fonctionnalités dans OpenSSL 1.1.1 a été fermée et la prise en charge de TLS du noyau a été déplacée vers la prochaine version. Depuis ce temps, la prise en charge du noyau TLS RX a été ajoutée et SSL_sendfile () est un appel à l'appel système correspondant avec un petit traitement des situations possibles dans le protocole TLS. Mais maintenant, en 2020, OpenSSL 3.0 n'est pas encore sorti, TLSv1.3 est pris en charge par la grande majorité des navigateurs et le chiffrement AES128-GCM est activement remplacé par le AES256-GCM plus résistant. J'ai donc pris la liberté et envoyé une Pull Request pour prendre en charge les nouveaux chiffrements et TLSv1.3 dans l'espoir qu'ils l'accepteraient avant la nouvelle version de la bibliothèque.

Support dans les serveurs WEB


Mais la prise en charge de Kernel TLS dans la bibliothèque TLS n'est pas suffisante. Des rapports sur la technologie indiquent qu'une efficacité maximale peut être obtenue en envoyant sans copier de données vers l'espace utilisateur - en utilisant l'appel système sendfile (). Et l'application serveur devrait être capable de distinguer les situations où vous pouvez utiliser sendfile () sur un socket avec TLS, et quand vous devez faire «lire à l'ancienne» read () / SSL_write (). Des progrès vers l'ajout de fonctionnalités à Nginx sont apparus en avril 2019, mais aucun changement n'a été accepté dans le code principal. La position des développeurs est que l'API pour ces fonctions n'est pas encore approuvée dans OpenSSL, et le code réel proposé dans le patch ne semble pas suffisamment portable sur différentes plates-formes. Pour être honnête, non seulement le code n'est pas très joli, mais il contient également des erreurs qui empêchent Nginx d'être construit sans corrections supplémentaires.Je n'ai pas pu trouver de support du noyau TLS dans d'autres serveurs Web (peut-être que quelqu'un l'a vu - dites-le moi dans les commentaires).

Et que faire?


Alors que la communauté attend la sortie d'OpenSSL 3.0, afin de commencer à développer la prise en charge du TLS du noyau dans Nginx avec une API stable, je suis allé dans l'autre sens. Dans mon coin douillet de GitHub, j'ai fait 2 choses:

  1. J'ai créé une fourchette OpenSSL et rétroporté tout ce qui concerne le noyau TLS vers la version stable d'OpenSSL 1.1.1 (branche OpenSSL_1_1_1-ktls). Surtout pour pouvoir vérifier la fonctionnalité dans des conditions de fonctionnement stable du reste de la bibliothèque. Au fur et à mesure que des versions stables deviennent disponibles, j'essaie de rebaser pour que la fourche soit à jour.
  2. J'ai créé un fork de Nginx, dans lequel j'ai ajouté (basé sur un correctif de Mellanox) la prise en charge de l'appel de SSL_sendfile () dans les conditions où cela est vraiment possible et avec les vérifications de socket SSL nécessaires, et la possibilité d'activer / désactiver la fonctionnalité via la variable de configuration. En plus de cette fonctionnalité, dans mon fork il y a aussi plusieurs patchs qui optimisent un peu le travail de Nginx et corrigent quelques bugs (branche master-feature). Dans la mesure du possible, j'essaie de rebaser sur la base de la branche master du dépôt principal Nginx, afin de garder la fourche à jour.

J'invite tout le monde à participer aux tests. Les commentaires et corrections du code peuvent être lancés sur Issues sur GitHub. Pour construire ce Nginx avec cet OpenSSL et le support TLS du noyau inclus, ajoutez des paramètres au script de configuration:

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

Pour le moment, je n'ai pas la possibilité de tester ce bundle Nginx + OpenSSL sous forte charge pour confirmer les performances du commentaire sur le patch Nginx, donc si soudainement quelqu'un peut mesurer la différence de charge CPU à des vitesses de téléchargement de fichiers élevées, ce serait génial d'obtenir des graphiques et d'ajouter les dans un article pour une compréhension visuelle de l'effet.

All Articles