SSD Nodes Learn 🎉 VPS dès $4.99/mois
Guides Matt ConnorPar Matt Connor

AES-NI sur un VPS : vérifier et réactiver

Vérifiez AES-NI, mesurez le coût d’un CPUID masqué sur AES-GCM et réactivez le chemin rapide avec OPENSSL_ia32cap, sans changer de VPS.

Ce que vous apporte réellement AES-NI sur un VPS

AES-NI est un ensemble de six instructions x86 qui exécutent un tour d’AES (advanced encryption standard) matériellement sur un VPS. Si le modèle de CPU présenté par votre fournisseur masque ces instructions, le processeur sous-jacent les possède toujours, mais OpenSSL ne peut pas les détecter et revient à une implémentation logicielle qui consomme environ dix fois plus de cycles par octet. Vous pouvez vérifier la présence de cette fonctionnalité avec une commande, mesurer l’écart avec deux commandes et souvent réactiver le chemin rapide avec une variable d’environnement.

Les instructions sont AESENC, AESENCLAST, AESDEC, AESDECLAST, AESIMC et AESKEYGENASSIST. Intel les a intégrées en 2010, puis AMD a suivi. Tout CPU de serveur que vous êtes susceptible de louer possède donc généralement le circuit nécessaire. Une instruction complémentaire, PCLMULQDQ, effectue une multiplication sans retenue. C’est ce dont GCM (Galois/counter mode) a besoin pour produire son tag d’authentification. AES-GCM est rapide uniquement lorsque les deux éléments sont disponibles, car le chiffrement et le calcul du tag sont deux opérations distinctes.

Voici quatre situations où cela apparaît dans la supervision d’un VPS :

  • Terminaison TLS (transport layer security). Un serveur web qui utilise AES-128-GCM ou AES-256-GCM consacre la majeure partie de son temps de calcul cryptographique à AES.
  • Volumes chiffrés. LUKS (Linux unified key setup) et dm-crypt exécutent aes-xts à chaque lecture et à chaque écriture, dans le kernel, sur le CPU.
  • Trafic VPN basé sur AES. OpenVPN avec AES-256-GCM et IPsec avec AES-GCM s’appuient tous deux sur ces instructions.
  • Sauvegardes chiffrées. Tout logiciel qui chiffre un flux avec AES avant de l’envoyer hors du serveur paie le même coût.

Une charge de travail courante n’est pas affectée. WireGuard utilise ChaCha20-Poly1305 pour ses données et ne fait jamais appel à AES. Un VPN WireGuard auto-hébergé fonctionne donc à la même vitesse sur un hôte où cette fonctionnalité est masquée. Cette différence constitue une raison pratique de comparer WireGuard et OpenVPN avant de choisir un tunnel pour un VPS à bas coût.

Vérifier si votre VPS dispose d’AES-NI

Le kernel copie les indicateurs de fonctionnalités CPUID dans /proc/cpuinfo. Une commande grep suffit donc pour répondre à la question.

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

L’affichage de aes par l’une ou l’autre commande signifie que le CPU annonce AES-NI à ce guest. Si rien ne s’affiche, le CPU ne l’annonce pas. lscpu lit les mêmes indicateurs, les deux commandes donnent donc toujours le même résultat. Utilisez celle qui est installée.

Vérifiez maintenant quel CPU l’hôte indique pour votre VPS.

grep -m1 'model name' /proc/cpuinfo

Une chaîne correspondant à un modèle réel, comme Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz ou AMD EPYC 7443P 24-Core Processor, signifie que l’hôte vous transmet le modèle du CPU physique. QEMU Virtual CPU version 2.5+ ou Common KVM processor signifie qu’un autre mécanisme est utilisé. C’est ce cas qu’il faut comprendre.

Pourquoi le flag est absent alors que le processeur le prend en charge

CPUID est l’instruction qu’un programme utilise pour demander au processeur quelles fonctions il prend en charge. Dans une machine virtuelle, CPUID intercepte toujours l’appel pour le transmettre à l’hypervisor, qui décide donc de ce qui est communiqué au guest. La plupart des panels présentent cette décision sous la forme d’un modèle de CPU pour le guest. qemu64 et kvm64 sont des modèles de base génériques. Aucun des deux n’inclut AES-NI ni SSSE3 dans son jeu de fonctions. Le guest ne voit donc aucun flag aes, même si l’hôte physique utilise un EPYC récent. Un VPS est un guest exécuté sur le matériel d’un tiers. Toutes les fonctions qu’il annonce résultent donc d’une décision prise au niveau supérieur. Si cette organisation vous est inconnue, commencez par ce qu’est un VPS.

Les hébergeurs choisissent délibérément un modèle générique. La live migration entre des machines équipées de processeurs différents ne fonctionne que si le guest n’a jamais reçu l’information qu’il prend en charge une fonction absente de la machine de destination. Le coût est à votre charge. Votre kernel et votre version d’OpenSSL lisent ce CPUID masqué une fois au démarrage. Ils utilisent ensuite le code path lent pendant toute la durée du processus.

La correction doit être appliquée sur l’hôte : -cpu host dans la terminologie QEMU, un modèle nommé qui inclut AES-NI, ou un +aes explicite ajouté au modèle. Vous ne pouvez rien régler de tout cela depuis le guest. Ouvrir un ticket auprès du support ou choisir une offre dont l’hypervisor transmet le modèle de CPU est la solution durable.

Mesurez l’écart avec openssl speed

Ne prenez pas un benchmark publié pour argent comptant. Exécutez le cipher que votre propre serveur négocie réellement.

openssl version
openssl speed -evp aes-128-gcm

La ligne de résultat est identifiée par AES-128-GCM et indique le débit pour six tailles de bloc, en unités de 1000 octets par seconde. Consultez la colonne 8192 octets pour un transfert important, car la colonne 16 octets est dominée par le surcoût de chaque appel et ne fournit aucune information utile sur le téléchargement d’un fichier.

Exécutez maintenant la même commande en désactivant AES-NI et PCLMULQDQ dans le logiciel :

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

Cette valeur provient de la documentation du vecteur de capacités d’OpenSSL. Un ~ en début de valeur signifie « effacer ces bits ». Le bit 57 correspond à AES-NI et le bit 33 à PCLMULQDQ. 0x200000200000000 désigne donc exactement ces deux capacités, et aucune autre. Une deuxième valeur nettement inférieure à la première signifie que votre machine dispose d’un AES-NI fonctionnel et que le test est terminé. Deux valeurs identiques signifient qu’OpenSSL utilisait déjà le chemin logiciel, car l’indicateur n’était pas présent et ne pouvait donc pas être effacé.

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
  }
]

Il s’agit de valeurs publiées représentatives pour un cœur x86 moderne cadencé à environ 3.4 GHz, et non d’une mesure effectuée sur un hôte particulier. Considérez-les comme une tendance. Le chemin matériel atteint environ 0.7 cycles par octet, contre environ 11.0 pour le fallback logiciel. Cela correspond à environ 4,850 Mo/s contre 310 Mo/s sur un seul cœur. Les deux commandes ci-dessus produisent la seule valeur qui décrit votre serveur. La même méthode s’applique au reste de la machine. Associez donc ce test à une méthode reproductible pour benchmarker un VPS avant de tirer des conclusions sur une offre.

Réactiver les fonctionnalités avec OPENSSL_ia32cap

Voici le point qui surprend souvent. Les instructions AES-NI sont accessibles sans privilèges, et l’hyperviseur ne les intercepte pas. Seul CPUID est intercepté. Un hôte peut donc indiquer à votre guest qu’AES-NI est absent alors que AESENC continue de s’exécuter nativement à pleine vitesse. Le logiciel ignore le chemin rapide parce qu’il a interrogé CPUID et reçu une réponse incorrecte. L’instruction elle-même n’a jamais cessé de fonctionner.

OpenSSL vous permet de répondre à la place du CPU. Une valeur hexadécimale simple dans OPENSSL_ia32cap remplace le vecteur de capacités au lieu de le masquer.

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

Si cette exécution est plusieurs fois plus rapide que l’exécution normale, le processeur dispose d’AES-NI et votre hôte le masque. Il s’agit d’abord d’un diagnostic. Pour OpenSSL, c’est aussi un correctif.

Construction de cette valeur hexadécimale

Le premier vecteur logique place la feuille CPUID 1 EDX dans les 32 bits de poids faible et la feuille 1 ECX dans les 32 bits de poids fort. Dans la moitié basse, le bit 24 correspond à FXSR, le bit 25 à SSE et le bit 26 à SSE2, soit 0x07000000. Dans la moitié haute, le bit 33 correspond à PCLMULQDQ, le bit 41 à SSSE3 et le bit 57 à AES-NI, soit 0x02000202. La valeur complète est donc 0x0200020207000000. SSSE3 figure dans la liste parce que le GHASH basé sur PCLMULQDQ d’OpenSSL utilise pshufb pour inverser l’ordre des octets, et qu’un modèle de CPU guest générique masque SSSE3 en même temps qu’AES-NI.

Deux avertissements s’appliquent, et vous pouvez provoquer les deux volontairement.

Définir uniquement le premier vecteur laisse les vecteurs suivants à zéro, ce qui désactive les chemins de code AVX2 et AVX-512. C’est volontaire ici. N’essayez pas d’activer de force les bits AVX sur un guest masqué, car les registres AVX nécessitent que le système d’exploitation active l’état étendu dans XCR0, ce que votre kernel a refusé de faire sur la base du même CPUID masqué. Une instruction encodée avec VEX déclenche alors une faute d’opcode indéfini et le processus s’arrête.

Activer de force AES-NI sur un cœur qui ne le prend réellement pas en charge arrête immédiatement le processus :

Illegal instruction (core dumped)

C’est AESENC qui déclenche une faute d’opcode indéfini, car ce cœur ne possède aucune instruction correspondante à exécuter. Certains firmwares de serveur peuvent également désactiver AES-NI matériellement jusqu’au prochain reset, avec un symptôme identique. Dans les deux cas, la solution consiste à utiliser un autre hôte, pas une autre variable d’environnement.

Pour conserver cette surcharge pour un service qui s’exécute longtemps, utilisez un drop-in systemd.

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

La dernière commande doit vous réafficher la variable. Comprenez ce que cela implique : si ce serveur est un jour migré vers un hôte dont le CPU ne prend réellement pas AES-NI en charge, nginx s’arrêtera sur une instruction illégale lors de sa première connexion TLS. Ajoutez cette information à votre runbook, ou gardez entièrement cette surcharge hors production et utilisez-la uniquement pour démontrer le problème lors de l’ouverture d’un ticket.

Ce que l’override ne corrige pas

OPENSSL_ia32cap est transmis à OpenSSL, et à rien d’autre. Chaque autre logiciel effectue sa propre détection des fonctionnalités et ne lit jamais cette variable.

Le noyau est le cas important. dm-crypt et LUKS utilisent l’API de chiffrement du noyau, et le module aesni_intel refuse de se charger lorsque le bit indiquant la fonctionnalité CPU est absent :

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

Il n’existe pas de variable en espace utilisateur pour cela. Le noyau lit CPUID une fois au démarrage, et cette décision reste valable jusqu’à ce que vous redémarriez sur un autre hôte. Votre volume chiffré reste donc sur le chiffrement logiciel, quelle que soit l’activité d’OpenSSL. Mesurez ce que vous obtenez réellement :

sudo cryptsetup benchmark -c aes-xts -s 256

La ligne aes-xts 256b atteint plusieurs milliers de MiB/s avec AES matériel, contre quelques centaines sans accélération matérielle. Les environnements d’exécution qui effectuent leur propre détection, notamment Go et Java, sont tout aussi indépendants. Le crypto/aes de Go vérifie directement CPUID et utilise discrètement son implémentation logicielle à temps constant lorsque le bit est absent. Si le service qui termine vos connexions TLS est un binaire Go, la variable OpenSSL ne change rien pour lui.

Si vous ne pouvez pas utiliser AES-NI, préférez ChaCha20

ChaCha20-Poly1305 a été conçu pour être rapide en logiciel. Sur un cœur sans AES-NI utilisable, il est généralement nettement plus rapide qu’AES-GCM. Sur un tel hôte, il est donc préférable de ne plus donner la priorité à AES.

Pour nginx 1.19.4 et les versions ultérieures, compilé avec OpenSSL 1.1.1 ou une version ultérieure :

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 concerne TLS 1.2. ssl_conf_command Ciphersuites concerne TLS 1.3. nginx ne dispose d’aucune directive dédiée pour TLS 1.3 et transmet cette chaîne directement à OpenSSL sans la vérifier. Une faute de frappe est donc acceptée silencieusement. Rechargez la configuration et vérifiez ce qui est proposé à un client :

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

Un résultat correct indique TLS_CHACHA20_POLY1305_SHA256. Avant d’appliquer la modification, exécutez openssl speed -evp chacha20-poly1305 en parallèle du test AES sur le même serveur et laissez les deux valeurs décider.

Les hôtes ARM utilisent des extensions différentes

AES-NI est réservé à x86. Un VPS ARM utilise les extensions cryptographiques ARMv8, un jeu d’instructions distinct qui remplit la même fonction. Sur aarch64, les flags se trouvent dans Features et non dans flags :

grep -m1 Features /proc/cpuinfo

Recherchez aes et pmull. pmull est l’équivalent ARM de PCLMULQDQ, et GCM en a besoin pour la même raison. La variable de surcharge d’OpenSSL sur ARM est OPENSSL_armcap. Sa propre disposition des bits est définie dans crypto/arm_arch.h du code source d’OpenSSL. La valeur hexadécimale x86 utilisée dans ce guide n’a donc aucune signification sur ARM. En pratique, les cœurs ARM des serveurs proposés comme hôtes VPS exposent ces extensions. Le problème des fonctionnalités masquées concerne donc principalement x86.

Modes d’échec et chaînes affichées

Pas de aes dans /proc/cpuinfo, et l’exécution forcée est beaucoup plus rapide. L’hôte masque CPUID. Vérifiez que le nom du modèle est générique, puis demandez à votre fournisseur quel modèle de CPU invité son hyperviseur présente.

Pas de aes dans /proc/cpuinfo, et l’exécution forcée affiche Illegal instruction. Les instructions sont réellement absentes ou le firmware les a désactivées. Déplacez la charge de travail vers un autre hôte.

aes est présent, mais le débit reste faible. Vérifiez que vous lisez la colonne de 8192 octets et qu’aucun autre processus n’utilise le cœur. Avec une offre mutualisée, un voisin bruyant ressemble exactement à une fonctionnalité CPU absente jusqu’à ce que vous exécutiez le test deux fois à des heures différentes.

L’exécution avec masquage et l’exécution normale donnent le même nombre. OpenSSL utilisait déjà le chemin logiciel. Ce résultat est le constat, pas une erreur dans le test.

La virtualisation imbriquée modifie le résultat au niveau inférieur. Un invité exécuté dans un autre invité reçoit le CPUID que la couche intermédiaire a choisi de transmettre. AES-NI peut facilement être perdu à ce niveau sans que cela soit visible. Si vous exécutez des machines virtuelles imbriquées sur un VPS, vérifiez également le flag dans l’invité interne, ainsi que sur la machine que vous avez louée.

FAQ

Pourquoi mon VPS n’a-t-il pas l’indicateur aes dans /proc/cpuinfo ?

Parce que l’hyperviseur présente un modèle de CPU générique pour la machine virtuelle. qemu64 et kvm64 n’incluent pas AES-NI dans leurs jeux de fonctionnalités. CPUID indique donc que cette fonctionnalité est absente, quel que soit le processeur physique. Les hébergeurs font cela pour pouvoir migrer une machine virtuelle en fonctionnement entre des machines dotées de processeurs différents. Exécutez grep -m1 'model name' /proc/cpuinfo : une chaîne comme QEMU Virtual CPU version 2.5+ ou Common KVM processor est révélatrice, tandis qu’une chaîne correspondant à un vrai modèle Xeon ou EPYC signifie que le modèle de CPU est transmis tel quel et que l’indicateur est réellement absent du processeur.

OPENSSL_ia32cap active-t-il réellement AES-NI ou fait-il seulement semblant ?

Il active les instructions réelles. Les instructions AES-NI sont non privilégiées et l’hyperviseur ne les intercepte jamais. AESENC s’exécute donc nativement, quel que soit le résultat de CPUID. Seule l’instruction CPUID est interceptée. Définir OPENSSL_ia32cap avec une valeur hexadécimale simple remplace la réponse obtenue par OpenSSL via CPUID. OpenSSL sélectionne alors son chemin de code matériel, que le processeur exécute à pleine vitesse. Si le processeur ne prend réellement pas en charge ces instructions, le processus s’arrête avec Illegal instruction (core dumped) lors de la première opération AES.

La surcharge accélérera-t-elle mon volume chiffré avec LUKS ?

Non. OPENSSL_ia32cap est lu par OpenSSL, et par aucun autre composant. LUKS et dm-crypt utilisent l’API de chiffrement du kernel. Le module aesni_intel ne se charge pas avec modprobe: ERROR: could not insert 'aesni_intel': No such device lorsque le bit de fonctionnalité est désactivé. Le kernel lit CPUID au démarrage, et aucune variable de l’espace utilisateur ne peut modifier ce résultat. Mesurez la valeur réelle avec sudo cryptsetup benchmark -c aes-xts -s 256, puis comparez la ligne aes-xts 256b avec celle d’un hôte qui signale cet indicateur.

L’absence de l’indicateur AES-NI ralentit-elle WireGuard ?

Non. WireGuard utilise ChaCha20-Poly1305 pour toutes les données et n’utilise jamais les instructions AES. Son débit est donc identique sur un hôte où la fonctionnalité est masquée et sur un hôte où elle est visible. OpenVPN et IPsec configurés avec AES-GCM perdent en revanche du débit sur un hôte dépourvu d’AES-NI. Deux tunnels sur le même VPS peuvent donc avoir des performances très différentes. Il faut le savoir avant d’attribuer le problème au réseau.

Comment vérifier la présence d’AES-NI sur un VPS ARM ?

Les cœurs ARM ne disposent pas d’AES-NI. Ils disposent des extensions cryptographiques ARMv8, qui assurent la même fonction avec des instructions différentes. Exécutez grep -m1 Features /proc/cpuinfo et recherchez aes et pmull. Sur aarch64, ces fonctionnalités sont listées sous Features, et non sous flags. La valeur x86 OPENSSL_ia32cap n’a aucune signification sur ARM. La variable équivalente d’OpenSSL est OPENSSL_armcap, et sa disposition des bits est définie dans crypto/arm_arch.h du code source d’OpenSSL.

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