Linux Kernel TLS und Nginx

In diesem Artikel werde ich über die Entwicklungsgeschichte und den aktuellen Stand der Technik zur Beschleunigung der Verteilung von Inhalten in TLS-Verbindungen durch Übertragung der Verschlüsselung auf den Kern des Betriebssystems sowie über meinen Beitrag zur Entwicklung dieser Richtung sprechen.

Hintergrund


Bereits 2015 präsentierten Randall Stewart und Scott Long von Netflix auf der AsiaBSDCon2015- Konferenz die Optimierung der Verteilung verschlüsselter Inhalte. Die Hauptnachricht des Berichts besteht darin, die Datenverschlüsselung an den Kernel des Betriebssystems zu übertragen, um die Anzahl der Datenkopien zwischen Kernel-Space und User-Space zu verringern und den optimierten nicht blockierenden sendfile () -Systemaufruf zu verwenden. Das Thema erwies sich als sehr vielversprechend und bereits auf der Netdev 1.2- Konferenz 2016 hielt Dave Watson von Facebook eine Präsentation über die Notwendigkeit, TLS-Sockets im Linux-Kernel zu erstellen, und Boris Pismenny, Ilya Lesokhin und Liran Liss von Mellanox eine Präsentationüber die Fähigkeit, TLS-Hardwarebeschleunigung in Netzwerkkarten zu verwenden. Das Thema wurde auch auf der HighLoad ++ 2017 von einem Redner von Tempesta Technologies diskutiert.

Linux-Kernel-Unterstützung


Das Ergebnis all dieser Gespräche war das Erscheinen von Kernel TLS im Linux 4.13-Kernel (2017) mit Unterstützung für TLSv1.2 und die AES128-GCM-Verschlüsselung. Anfänglich wurde nur die Verschlüsselung des ausgehenden Datenverkehrs unterstützt, die Entschlüsselungsunterstützung wurde später im Linux-Kernel 4.17 (2018) angezeigt. In Version 5.1 wurde die Unterstützung für TLSv1.3 und AES256-GCM hinzugefügt, und in Version 5.2 wurde auch die AES128-CCM-Verschlüsselung (2019) hinzugefügt.

Unterstützung im User-Space


In allen oben genannten Berichten wurde gesagt, dass nur die Verschlüsselung nützlicher Daten im Kernel platziert werden kann und alle TLS-Genehmigungen und Kontrollnachrichten im Benutzerbereich gleich verarbeitet werden müssen. Und dafür wurde eine modifizierte Version von OpenSSL verwendet. Zum Zeitpunkt der Veröffentlichung der Berichte im öffentlichen Bereich gab es jedoch keine Informationen darüber, welche Änderungen an dieser bekannten Bibliothek vorgenommen werden mussten, um die Funktionalität zu unterstützen. Das vielleicht einzige verfügbare Beispiel für die Verwendung von Linux-Kernel-TLS war der Blog-Artikel Filippo Valsorda Spielen mit Kernel-TLS in Linux 4.13 und Go, die unmittelbar nach der Veröffentlichung des Linux 4.13-Kernels erschien. Und obwohl es ein gültiges Beispiel für den Einsatz von Technologie zeigte, brachte es kein Verständnis dafür, wie die Technologie in realen Projekten eingesetzt werden kann. Immerhin schreiben nur sehr wenige Leute einen WEB-Server für ihr Projekt selbst, normalerweise verwendet jeder bekannte und bewährte Tools.

Unterstützung in OpenSSL


Die ersten Diskussionen über die Technologie in OpenSSL wurden im Sommer 2017 kurz vor der Veröffentlichung des Linux 4.13-Kernels ( PR 3631 ) veröffentlicht, aber der Diskussionsprozess war sehr, sehr langsam, die ersten echten Kommentare erschienen im Oktober (nach der Veröffentlichung des 4.13-Kernels) und der tatsächlichen ArbeitsversionDie Diskussion begann im Februar 2018. Als alle Korrekturen vereinbart waren, wurde das Fenster zum Hinzufügen neuer Funktionen in OpenSSL 1.1.1 geschlossen und die Unterstützung für Kernel TLS auf die nächste Version verschoben. Seitdem wurde die Kernel-TLS-RX-Unterstützung hinzugefügt, und SSL_sendfile () ist ein Aufruf des entsprechenden Systemaufrufs mit einer kleinen Verarbeitung möglicher Situationen im TLS-Protokoll. Aber jetzt im Jahr 2020 ist OpenSSL 3.0 noch nicht herausgekommen, TLSv1.3 wird von der überwiegenden Mehrheit der Browser unterstützt, und die AES128-GCM-Verschlüsselung wird aktiv durch die widerstandsfähigere AES256-GCM ersetzt. Also nahm ich mir die Freiheit und schickte eine Pull-Anfrage , um neue Chiffren und TLSv1.3 zu unterstützen, in der Hoffnung, dass sie diese vor der neuen Veröffentlichung der Bibliothek akzeptieren würden.

Unterstützung in WEB-Servern


Die Unterstützung von Kernel-TLS in der TLS-Bibliothek reicht jedoch nicht aus. Berichten über die Technologie zufolge kann durch Senden eine maximale Effizienz erzielt werden, ohne dass Daten in den Benutzerbereich kopiert werden müssen - mithilfe des Systemaufrufs sendfile (). Die Serveranwendung sollte in der Lage sein, zwischen Situationen zu unterscheiden, in denen Sie sendfile () für einen Socket mit TLS verwenden können, und in denen Sie read () / SSL_write () auf die alte Weise lesen müssen. Einige Fortschritte beim Hinzufügen von Funktionen zu Nginx wurden im April 2019 erzielt, es wurden jedoch keine Änderungen am Hauptcode akzeptiert. Die Position der Entwickler ist, dass die API für diese Funktionen in OpenSSL noch nicht genehmigt ist und der im Patch vorgeschlagene Code auf verschiedenen Plattformen nicht portabel genug zu sein scheint. Um ehrlich zu sein, sieht der Code nicht nur nicht sehr gut aus, sondern enthält auch Fehler, die verhindern, dass Nginx ohne zusätzliche Korrekturen erstellt wird.Ich konnte die Kernel-TLS-Unterstützung auf anderen Webservern überhaupt nicht finden (vielleicht hat es jemand gesehen - sagen Sie es mir in den Kommentaren).

Und was zu tun?


Während die Community auf die Veröffentlichung von OpenSSL 3.0 wartet, bin ich in die andere Richtung gegangen, um Unterstützung für Kernel TLS in Nginx mit einer stabilen API zu entwickeln. In meiner gemütlichen Ecke von GitHub habe ich zwei Dinge getan:

  1. Ich habe einen OpenSSL- Fork erstellt und alles, was mit Kernel-TLS zu tun hat, auf die stabile Version von OpenSSL 1.1.1 (OpenSSL_1_1_1-ktls-Zweig) zurückportiert. Insbesondere, um die Funktionalität unter stabilen Betriebsbedingungen des Restes der Bibliothek überprüfen zu können. Sobald stabile Versionen verfügbar sind, versuche ich, die Basis neu zu erstellen, damit die Gabel auf dem neuesten Stand ist.
  2. Ich habe eine Verzweigung von Nginx erstellt, in der ich (basierend auf einem Patch von Mellanox) Unterstützung für das Aufrufen von SSL_sendfile () unter Bedingungen hinzugefügt habe, unter denen dies wirklich möglich ist und mit den erforderlichen SSL-Socket-Überprüfungen und der Möglichkeit, die Funktionalität über die Konfigurationsvariable zu aktivieren / deaktivieren. Zusätzlich zu dieser Funktion gibt es in meiner Gabelung auch einige Patches, die die Arbeit von Nginx ein wenig optimieren und einige Fehler beheben (Master-Feature-Zweig). Ich versuche so weit wie möglich, basierend auf dem Hauptzweig des Nginx-Hauptrepos eine Neubasis zu erstellen, um die Gabel auf dem neuesten Stand zu halten.

Ich lade alle ein, an Tests teilzunehmen. Kommentare und Korrekturen zum Code können bei Issues on GitHub ausgegeben werden. Fügen Sie dem Konfigurationsskript Parameter hinzu, um diesen Nginx mit dieser OpenSSL und der enthaltenen Kernel-TLS-Unterstützung zu erstellen:

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

Im Moment habe ich keine Gelegenheit, dieses Nginx + OpenSSL-Bundle unter hoher Last zu testen, um die Leistung anhand des Kommentars zum Nginx-Patch zu bestätigen. Wenn also plötzlich jemand den Unterschied in der CPU-Auslastung bei hohen Upload-Geschwindigkeiten für Dateien messen kann, wäre es großartig, Grafiken zu erhalten und hinzuzufügen sie in einen Artikel für ein visuelles Verständnis des Effekts.

All Articles