SSD Nodes Learn 🎉 VPS ab $4.99/Monat
Anleitungen Matt ConnorVon Matt Connor

AES-NI auf dem VPS prüfen und wieder aktivieren

Prüfen Sie AES-NI auf Ihrem VPS, messen Sie den AES-GCM-Verlust durch maskiertes CPUID und aktivieren Sie den schnellen Pfad mit OPENSSL_ia32cap.

Was Ihnen AES-NI auf einem VPS tatsächlich bringt

AES-NI ist auf einem VPS ein Satz aus sechs x86-Anweisungen, die eine AES-Runde (Advanced Encryption Standard) hardwarebeschleunigt ausführen. Wenn das CPU-Modell Ihres Providers diese Anweisungen verbirgt, sind sie in der zugrunde liegenden Hardware trotzdem vorhanden. OpenSSL kann sie jedoch nicht erkennen und verwendet stattdessen eine Softwareimplementierung, die ungefähr zehnmal so viele Zyklen pro Byte benötigt. Sie können das Feature mit einem Befehl prüfen, den Unterschied mit zwei Befehlen messen und den schnellen Pfad häufig mit einer Umgebungsvariablen wieder aktivieren.

Die Anweisungen sind AESENC, AESENCLAST, AESDEC, AESDECLAST, AESIMC und AESKEYGENASSIST. Intel hat sie 2010 eingeführt, AMD folgte. Daher verfügt praktisch jede Server-CPU, die Sie mieten können, über die entsprechende Hardware. Eine ergänzende Anweisung, PCLMULQDQ, führt eine Carry-less-Multiplikation aus. Diese benötigt GCM (Galois/Counter Mode), um seinen Authentifizierungstag zu erzeugen. AES-GCM ist nur dann schnell, wenn beide verfügbar sind, weil Verschlüsselung und Tag-Erzeugung getrennte Aufgaben sind.

An vier Stellen auf einem VPS zeigt sich dies in Ihren Monitoring-Daten:

  • TLS-Terminierung (Transport Layer Security). Ein Webserver, der AES-128-GCM oder AES-256-GCM bereitstellt, verbringt den größten Teil seiner Zeit für symmetrische Kryptografie mit AES.
  • Verschlüsselte Volumes. LUKS (Linux Unified Key Setup) und dm-crypt führen aes-xts bei jedem Lese- und Schreibvorgang im Kernel auf der CPU aus.
  • AES-basierter VPN-Verkehr. OpenVPN mit AES-256-GCM und IPsec mit AES-GCM nutzen diese Funktion intensiv.
  • Verschlüsselte Backups. Jede Anwendung, die einen Datenstrom mit AES verschlüsselt, bevor er den Server verlässt, verursacht denselben Aufwand.

Ein häufiges Einsatzszenario ist überhaupt nicht betroffen. WireGuard verwendet für seine Daten ChaCha20-Poly1305 und greift nie auf AES zu. Ein selbst gehostetes WireGuard-VPN läuft auf einem Host mit ausgeblendetem Feature-Flag daher genauso schnell. Dieser Unterschied ist ein praktischer Grund, WireGuard und OpenVPN zu vergleichen, bevor Sie sich für einen Tunnel auf einem günstigen VPS entscheiden.

So prüfen Sie, ob Ihr VPS AES-NI unterstützt

Der Kernel kopiert die CPUID-Feature-Bits nach /proc/cpuinfo. Ein grep-Befehl beantwortet daher die Frage.

grep -m1 -o '\baes\b' /proc/cpuinfo
lscpu | grep -i -o '\baes\b'

Wenn einer der beiden Befehle aes ausgibt, stellt die CPU AES-NI für diesen Gast bereit. Eine leere Ausgabe bedeutet, dass dies nicht der Fall ist. lscpu liest dieselben Flags aus. Daher liefern beide Befehle immer dasselbe Ergebnis. Verwenden Sie den Befehl, der auf dem System installiert ist.

Prüfen Sie nun, welche CPU der Host für Ihre Instanz meldet.

grep -m1 'model name' /proc/cpuinfo

Eine echte Modellbezeichnung wie Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz oder AMD EPYC 7443P 24-Core Processor bedeutet, dass der Host das Modell der physischen CPU an Sie weitergibt. QEMU Virtual CPU version 2.5+ oder Common KVM processor bedeutet, dass etwas anderes geschieht. Dieser Fall ist besonders wichtig, um ihn zu verstehen.

Warum das Flag fehlt, obwohl es die CPU unterstützt

CPUID ist die Instruktion, mit der ein Programm abfragt, welche Funktionen die CPU unterstützt. In einer virtuellen Maschine wird CPUID immer an den Hypervisor abgefangen. Daher entscheidet der Hypervisor, was dem Gast mitgeteilt wird. Die meisten Verwaltungsoberflächen stellen diese Entscheidung als CPU-Modell des Gasts dar. qemu64 und kvm64 sind generische Basismodelle. Beide enthalten AES-NI und SSSE3 nicht in ihrer Funktionsausstattung. Daher sieht der Gast kein aes-Flag, selbst wenn der physische Host ein aktueller EPYC ist. Ein VPS ist ein Gast auf der Hardware eines anderen Anbieters. Deshalb wird jede von ihm gemeldete Funktion eine Ebene höher festgelegt. Wenn diese Schichtung für Sie neu ist, beginnen Sie mit der Erklärung, was ein VPS ist.

Hosts wählen absichtlich ein generisches Modell. Eine Live-Migration zwischen Rechnern mit unterschiedlichen Prozessoren funktioniert nur, wenn dem Gast nie eine Funktion mitgeteilt wurde, die auf dem Zielrechner fehlt. Die Kosten tragen Sie. Ihr Kernel und Ihre installierte Kopie von OpenSSL lesen diese maskierten CPUID-Informationen einmal beim Start. Danach wählen beide für die gesamte Laufzeit des Prozesses den langsamen Codepfad.

Die Lösung muss auf der Host-Seite umgesetzt werden: -cpu host in der Terminologie von QEMU, ein benanntes Modell mit AES-NI oder ein explizites +aes, das dem Modell hinzugefügt wird. Innerhalb des Gasts können Sie keine dieser Einstellungen ändern. Ein Support-Ticket oder ein Tarif, dessen Hypervisor das CPU-Modell durchreicht, ist die dauerhafte Lösung.

Lücke mit openssl speed messen

Verlassen Sie sich nicht auf einen veröffentlichten Benchmark. Führen Sie die Chiffre aus, die Ihr eigener Server tatsächlich aushandelt.

openssl version
openssl speed -evp aes-128-gcm

Die Ergebniszeile ist mit AES-128-GCM bezeichnet und gibt den Durchsatz bei sechs Blockgrößen in Einheiten von 1000 Byte pro Sekunde an. Lesen Sie für die Übertragung großer Datenmengen die Spalte mit 8192 Byte. Die Spalte mit 16 Byte wird vom Overhead pro Aufruf dominiert und sagt über einen Dateidownload nichts aus.

Führen Sie denselben Befehl nun mit deaktiviertem AES-NI und deaktiviertem PCLMULQDQ in der Software aus:

OPENSSL_ia32cap="~0x200000200000000" openssl speed -evp aes-128-gcm

Dieser Wert stammt aus der Dokumentation zum Capability-Vektor von OpenSSL. Ein führendes ~ bedeutet „diese Bits löschen“. Bit 57 ist AES-NI und Bit 33 ist PCLMULQDQ. 0x200000200000000 bezeichnet daher genau diese beiden Bits und keine weiteren. Ein zweiter Wert, der deutlich unter dem ersten liegt, bedeutet, dass AES-NI auf Ihrem Server funktioniert. Damit ist die Prüfung abgeschlossen. Sind die beiden Werte gleich, verwendete OpenSSL bereits den Softwarepfad, weil das Flag nicht gesetzt war und daher nichts gelöscht werden konnte.

ChartAES-128-GCM throughput, one core, 8192-byte blocks (representative published figures)
The data behind this chart
[
  {
    "label": "AES-NI and PCLMULQDQ",
    "mb_per_sec": "4,850",
    "cycles_per_byte": 0.7
  },
  {
    "label": "Software fallback",
    "mb_per_sec": 310,
    "cycles_per_byte": 11.0
  }
]

Dies sind repräsentative veröffentlichte Werte für einen modernen x86-Kern mit etwa 3.4 GHz. Sie wurden nicht auf einem bestimmten Host gemessen. Betrachten Sie sie als Größenordnung. Der Hardwarepfad benötigt ungefähr 0.7 Zyklen pro Byte. Der Software-Fallback benötigt ungefähr 11.0 Zyklen pro Byte. Das entspricht auf einem Kern etwa 4,850 MB/s gegenüber 310 MB/s. Ihre beiden Befehle oben liefern die einzige Zahl, die Ihren Server beschreibt. Wenden Sie dieselbe Vorgehensweise auf den restlichen Server an. Kombinieren Sie diese Prüfung daher mit einer reproduzierbaren Methode zum Benchmarking eines VPS, bevor Sie Schlussfolgerungen über ein Angebot ziehen.

Die Bits mit OPENSSL_ia32cap wieder aktivieren

Hier liegt der überraschende Teil. Die AES-NI-Instruktionen sind nicht privilegiert, und der Hypervisor fängt sie nicht ab. Nur CPUID wird abgefangen. Ein Host kann dem Gast daher mitteilen, dass AES-NI nicht vorhanden ist, während AESENC weiterhin nativ mit voller Geschwindigkeit ausgeführt wird. Die Software überspringt den schnellen Pfad, weil sie CPUID abgefragt und eine falsche Antwort erhalten hat. Die Instruktion selbst funktioniert weiterhin.

OpenSSL kann die Antwort stellvertretend für die CPU liefern. Ein einfacher Hexadezimalwert in OPENSSL_ia32cap überschreibt den Capability-Vektor, anstatt ihn nur zu maskieren.

OPENSSL_ia32cap="0x0200020207000000" openssl speed -evp aes-128-gcm

Wenn dieser Lauf mehrere Male schneller ist als der normale Lauf, unterstützt die CPU AES-NI und der Host verbirgt die Funktion. Das ist zunächst eine Diagnose. Für OpenSSL ist es zugleich eine Lösung.

So wird dieser Hexadezimalwert erstellt

Der erste logische Vektor packt CPUID leaf 1 EDX in die unteren 32 Bit und leaf 1 ECX in die oberen 32 Bit. In der unteren Hälfte steht Bit 24 für FXSR, Bit 25 für SSE und Bit 26 für SSE2. Daraus ergibt sich 0x07000000. In der oberen Hälfte steht Bit 33 für PCLMULQDQ, Bit 41 für SSSE3 und Bit 57 für AES-NI. Daraus ergibt sich 0x02000202. Zusammengesetzt ergibt das 0x0200020207000000. SSSE3 ist enthalten, weil der auf PCLMULQDQ basierende GHASH von OpenSSL pshufb zum Vertauschen von Bytes verwendet und ein generisches Gast-CPU-Modell SSSE3 zusammen mit AES-NI ausblendet.

Dabei gelten zwei Warnungen. Beide lassen sich absichtlich auslösen.

Wenn nur der erste Vektor gesetzt wird, bleiben die späteren Vektoren auf null. Dadurch werden die Codepfade für AVX2 und AVX-512 deaktiviert. Das ist hier beabsichtigt. Versuchen Sie nicht, AVX-Bits bei einem maskierten Gast zu erzwingen. AVX-Register benötigen, dass das Betriebssystem den erweiterten Zustand in XCR0 aktiviert. Ihr Kernel hat dies aufgrund derselben maskierten CPUID abgelehnt. Eine VEX-kodierte Instruktion löst dann einen Fehler wegen eines undefinierten Opcodes aus, und der Prozess wird beendet.

AES-NI auf einem Kern zu erzwingen, der diese Funktion tatsächlich nicht unterstützt, beendet den Prozess sofort:

Illegal instruction (core dumped)

Dabei löst AESENC einen Fehler wegen eines undefinierten Opcodes aus, weil auf diesem Kern keine entsprechende Instruktion ausgeführt werden kann. Manche Server-Firmware kann AES-NI außerdem bis zum nächsten Neustart hardwareseitig deaktivieren. Das Symptom ist identisch. In beiden Fällen benötigen Sie einen anderen Host und keine andere Umgebungsvariable.

Für einen dauerhaft laufenden Dienst verwenden Sie ein systemd-Drop-in.

sudo systemctl edit nginx
[Service]
Environment="OPENSSL_ia32cap=0x0200020207000000"
sudo systemctl daemon-reload
sudo systemctl restart nginx
sudo systemctl show nginx -p Environment

Der letzte Befehl sollte die Variable wieder ausgeben. Machen Sie sich klar, worauf Sie sich damit festlegen: Wenn dieser Server jemals auf einen Host migriert wird, dessen CPU AES-NI tatsächlich nicht unterstützt, beendet sich nginx bei der ersten TLS-Verbindung mit einer Illegal-Instruction-Fehlermeldung. Nehmen Sie diesen Sonderfall in Ihr Runbook auf. Alternativ lassen Sie die Überschreibung in der Produktionsumgebung vollständig weg und verwenden sie nur, um den Fehler bei der Erstellung eines Tickets nachzuweisen.

Was der Override nicht behebt

OPENSSL_ia32cap wird von OpenSSL verarbeitet und hat keine weitere Wirkung. Jede andere Software führt ihre eigene Feature-Erkennung durch und liest diese Variable nicht.

Der Kernel ist der wichtige Sonderfall. dm-crypt und LUKS verwenden die Kernel-Crypto-API. Das Modul aesni_intel verweigert das Laden, wenn das CPU-Feature-Bit fehlt:

modprobe: ERROR: could not insert 'aesni_intel': No such device

Dafür gibt es keine Variable im User-Space. Der Kernel liest CPUID einmal beim Booten. Diese Entscheidung bleibt bestehen, bis Sie auf einem anderen Host neu booten. Ihr verschlüsseltes Volume verwendet daher weiterhin die Software-Chiffre, unabhängig davon, was OpenSSL tut. Messen Sie, welche Leistung tatsächlich erreicht wird:

sudo cryptsetup benchmark -c aes-xts -s 256

Die Zeile aes-xts 256b erreicht mit Hardware-AES mehrere tausend MiB/s und ohne Hardware-Unterstützung nur wenige hundert MiB/s. Sprachlaufzeiten mit eigener Erkennung, darunter Go und Java, sind ebenfalls nicht betroffen. crypto/aes von Go prüft CPUID direkt und verwendet unauffällig seine konstante Software-Implementierung, wenn das Bit nicht gesetzt ist. Wenn der Dienst, der Ihre TLS-Verbindungen beendet, ein Go-Binary ist, ändert die OpenSSL-Variable für ihn nichts.

Wenn Sie kein AES-NI nutzen können, bevorzugen Sie ChaCha20

ChaCha20-Poly1305 wurde für eine hohe Geschwindigkeit in reiner Software entwickelt. Auf einem Kern ohne nutzbares AES-NI ist es AES-GCM normalerweise deutlich überlegen. Auf einem solchen Host sollten Sie daher AES nicht länger bevorzugen.

Für nginx 1.19.4 und neuer, kompiliert mit OpenSSL 1.1.1 oder neuer:

ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384;

ssl_ciphers gilt für TLS 1.2. ssl_conf_command Ciphersuites gilt für TLS 1.3. Dafür gibt es in nginx keine eigene Direktive. nginx übergibt die Zeichenfolge ohne Prüfung direkt an OpenSSL. Ein Tippfehler wird deshalb stillschweigend akzeptiert. Laden Sie die Konfiguration neu und prüfen Sie, was einem Client angeboten wird:

sudo nginx -t && sudo systemctl reload nginx
openssl s_client -connect example.com:443 -tls1_3 </dev/null 2>/dev/null | grep -i cipher

Ein korrektes Ergebnis nennt TLS_CHACHA20_POLY1305_SHA256. Bevor Sie die Änderung übernehmen, führen Sie openssl speed -evp chacha20-poly1305 neben dem AES-Lauf auf demselben Host aus. Entscheiden Sie anhand der beiden Werte.

ARM-Hosts verwenden andere Erweiterungen

AES-NI ist auf x86 beschränkt. Ein ARM-VPS verwendet die kryptografischen Erweiterungen von ARMv8, einen separaten Befehlssatz für dieselbe Aufgabe. Auf aarch64 befinden sich die Flags unter Features statt unter flags:

grep -m1 Features /proc/cpuinfo

Suchen Sie nach aes und pmull. pmull ist das ARM-Gegenstück zu PCLMULQDQ, und GCM benötigt es aus demselben Grund. Die Override-Variable von OpenSSL auf ARM lautet OPENSSL_armcap. Die zugehörige Bitanordnung ist in crypto/arm_arch.h im OpenSSL-Quellcode definiert. Der x86-Hexadezimalwert aus dieser Anleitung hat dort daher keine Bedeutung. In der Praxis stellen die als VPS-Hosts eingesetzten ARM-Server diese Erweiterungen bereit. Das Problem mit maskierten Features betrifft daher überwiegend x86.

Fehlerbilder mit den angezeigten Zeichenfolgen

Kein aes in /proc/cpuinfo und der erzwungene Lauf ist deutlich schneller. Der Host blendet CPUID aus. Prüfen Sie, ob der Modellname generisch ist. Fragen Sie anschließend Ihren Provider, welches Gast-CPU-Modell der Hypervisor bereitstellt.

Kein aes in /proc/cpuinfo und der erzwungene Lauf gibt Illegal instruction aus. Die Anweisungen sind tatsächlich nicht vorhanden oder wurden von der Firmware deaktiviert. Verschieben Sie die Arbeitslast auf einen anderen Host.

aes ist vorhanden, aber der Durchsatz bleibt niedrig. Prüfen Sie, ob Sie die Spalte mit 8192 Byte auswerten und ob kein anderer Prozess den Core verwendet. Bei einem Shared-Hosting-Tarif wirkt ein lauter Nachbar exakt wie ein fehlendes CPU-Feature, bis Sie den Test zu zwei verschiedenen Tageszeiten ausführen.

Der maskierte Lauf und der normale Lauf liefern dieselbe Zahl. OpenSSL hat bereits den Softwarepfad verwendet. Dieses Ergebnis ist die Feststellung und kein Fehler im Test.

Nested Virtualisation ändert das Ergebnis eine Ebene tiefer. Ein Gast in einem Gast erhält genau die CPUID, deren Weitergabe die mittlere Ebene ausgewählt hat. AES-NI geht dort leicht unbemerkt verloren. Wenn Sie verschachtelte virtuelle Maschinen auf einem VPS ausführen, prüfen Sie das Flag sowohl im inneren Gast als auch auf der gemieteten Maschine.

FAQ

Warum enthält mein VPS in /proc/cpuinfo kein aes-Flag?

Der Hypervisor stellt ein generisches Gast-CPU-Modell bereit. qemu64 und kvm64 enthalten AES-NI nicht in ihren Feature-Sets. Deshalb meldet CPUID die Funktion als nicht vorhanden, unabhängig vom physischen Prozessor. Hosts machen dies, damit ein laufender Gast zwischen Maschinen mit unterschiedlichen CPUs migriert werden kann. Führen Sie grep -m1 'model name' /proc/cpuinfo aus: Eine Zeichenfolge wie QEMU Virtual CPU version 2.5+ oder Common KVM processor ist der entscheidende Hinweis. Eine echte Xeon- oder EPYC-Modellbezeichnung bedeutet dagegen, dass das CPU-Modell durchgereicht wird und das Flag auf der Hardware tatsächlich fehlt.

Aktiviert OPENSSL_ia32cap AES-NI wirklich, oder wird die Funktion nur vorgetäuscht?

Die echten Instruktionen werden aktiviert. AES-NI-Instruktionen sind nicht privilegiert, und der Hypervisor fängt sie nicht ab. Daher führt AESENC sie unabhängig vom Ergebnis von CPUID nativ aus. Nur die CPUID-Instruktion wird abgefangen. Wenn Sie OPENSSL_ia32cap auf einen einfachen Hexadezimalwert setzen, ersetzt dieser die Antwort, die OpenSSL von CPUID erhalten hat. OpenSSL wählt dadurch seinen Hardware-Codepfad, und die Hardware führt ihn mit voller Geschwindigkeit aus. Fehlen die Instruktionen tatsächlich auf der CPU, beendet sich der Prozess beim ersten AES-Vorgang mit Illegal instruction (core dumped).

Beschleunigt der Override mein mit LUKS verschlüsseltes Volume?

Nein. OPENSSL_ia32cap wird von OpenSSL und von keiner anderen Komponente gelesen. LUKS und dm-crypt verwenden die Kernel-Crypto-API. Dort kann das aesni_intel-Modul mit modprobe: ERROR: could not insert 'aesni_intel': No such device nicht geladen werden, wenn das Feature-Bit nicht gesetzt ist. Der Kernel liest CPUID beim Booten. Keine Variable im User-Space ändert dieses Verhalten. Messen Sie den tatsächlichen Wert mit sudo cryptsetup benchmark -c aes-xts -s 256 und vergleichen Sie die Zeile aes-xts 256b mit einem Host, der das Flag meldet.

Verlangsamt ein fehlendes AES-NI-Flag WireGuard?

Nein. WireGuard verwendet für alle Daten ChaCha20-Poly1305 und keine AES-Instruktionen. Daher ist der Durchsatz auf einem maskierten Host und auf einem nicht maskierten Host gleich. OpenVPN und IPsec mit AES-GCM verlieren auf einem Host ohne AES-NI dagegen an Durchsatz. Zwei Tunnel auf demselben VPS können sich daher sehr unterschiedlich verhalten. Das sollten Sie wissen, bevor Sie die Ursache beim Netzwerk suchen.

Wie prüfe ich AES-NI auf einem ARM-VPS?

ARM-Kerne verfügen nicht über AES-NI. Sie haben die kryptografischen Erweiterungen von ARMv8. Diese erfüllen dieselbe Aufgabe mit anderen Instruktionen. Führen Sie grep -m1 Features /proc/cpuinfo aus und suchen Sie nach aes und pmull. Bei aarch64 werden sie unter Features statt unter flags aufgeführt. Der x86-Wert OPENSSL_ia32cap hat auf ARM keine Bedeutung. Das entsprechende OpenSSL-Variable dort ist OPENSSL_armcap. Das Bitlayout ist in crypto/arm_arch.h im OpenSSL-Quellcode definiert.

#aes-ni#cpu#openssl#encryption#performance