Как проверить и включить AES-NI на VPS
Узнайте, поддерживает ли ваш VPS инструкции AES-NI. Мы покажем, как замерить падение производительности при скрытом CPUID и принудительно активировать ускорение через OPENSSL_ia32cap.
Что на самом деле дает AES-NI на VPS
AES-NI на VPS — это набор из шести инструкций x86, которые выполняют один раунд AES (advanced encryption standard) на аппаратном уровне. Если модель процессора вашего провайдера скрывает их, физически они все равно присутствуют в кристалле, но OpenSSL не видит их и переключается на программную реализацию, которая требует примерно в десять раз больше циклов на байт. Вы можете проверить наличие этой функции одной командой, измерить разницу в производительности двумя и часто принудительно включить быстрый путь с помощью одной переменной окружения.
Эти инструкции — AESENC, AESENCLAST, AESDEC, AESDECLAST, AESIMC и AESKEYGENASSIST. Intel внедрила их в 2010 году, AMD последовала за ней, поэтому любой серверный процессор, который вы арендуете, имеет эти аппаратные возможности. Сопутствующая инструкция PCLMULQDQ выполняет умножение без переноса (carry-less multiplication), которое необходимо режиму GCM (Galois/counter mode) для создания аутентификационного тега. AES-GCM работает быстро только при наличии обеих инструкций, так как шифрование и создание тега — это отдельные задачи.
Четыре области на VPS, где это отражается в мониторинге:
- TLS (transport layer security) termination. Веб-сервер, использующий AES-128-GCM или AES-256-GCM, тратит большую часть времени на криптографические операции внутри AES.
- Шифрованные тома. LUKS (Linux unified key setup) и dm-crypt выполняют
aes-xtsпри каждой операции чтения и записи в ядре на уровне процессора. - VPN-трафик на базе AES. OpenVPN с
AES-256-GCMи IPsec с AES-GCM активно используют эти инструкции. - Шифрованные резервные копии. Любой процесс, шифрующий поток данных с помощью AES перед отправкой с сервера, несет те же затраты ресурсов.
Одна распространенная рабочая нагрузка не зависит от этого вовсе. WireGuard использует ChaCha20-Poly1305 для своих данных и никогда не обращается к AES, поэтому самостоятельно развернутый WireGuard VPN работает с той же скоростью даже на хосте, где этот флаг скрыт. Эта разница — весомый практический аргумент, чтобы сравнить WireGuard и OpenVPN перед выбором туннеля для недорогого VPS.
Как проверить наличие AES-NI на вашем VPS
Ядро копирует биты функций CPUID в /proc/cpuinfo, поэтому один вызов grep дает ответ на этот вопрос.
grep -m1 -o '\baes\b' /proc/cpuinfo
lscpu | grep -i -o '\baes\b'Если любая из команд выводит aes, это означает, что процессор сообщает гостевой системе о поддержке AES-NI. Отсутствие вывода означает, что поддержка отсутствует. lscpu считывает те же флаги, поэтому результаты всегда совпадают. Используйте ту утилиту, которая установлена в системе.
Теперь посмотрите, какой процессор, по утверждению хоста, вы используете.
grep -m1 'model name' /proc/cpuinfoНазвание реальной модели, например Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz или AMD EPYC 7443P 24-Core Processor, означает, что хост передает вам данные о физической модели процессора. QEMU Virtual CPU version 2.5+ или Common KVM processor означают, что происходит что-то иное, и этот случай стоит изучить подробнее.
Почему флаг отсутствует, хотя процессор его поддерживает
CPUID — это инструкция, которую программа использует для запроса поддерживаемых процессором функций. Внутри виртуальной машины CPUID всегда перехватывается гипервизором, поэтому именно гипервизор решает, что именно «видит» гостевая система. Большинство панелей управления представляют это решение в виде выбора модели гостевого процессора. qemu64 и kvm64 — это базовые универсальные модели, ни одна из которых не включает AES-NI или SSSE3 в свой набор функций, поэтому гостевая система не видит флаг aes, даже если физический хост — современный EPYC. VPS является гостевой системой на чужом оборудовании, поэтому каждая функция, о которой она сообщает, — это решение, принятое на уровень выше. Если эта многоуровневая архитектура для вас в новинку, начните с что такое VPS.
Хосты намеренно выбирают универсальную модель, так как «живая» миграция между машинами с разными процессорами работает только в том случае, если гостевой системе не сообщалось о функциях, отсутствующих на целевом узле. Издержки ложатся на вас. Ваше ядро и ваша копия OpenSSL считывают этот маскированный CPUID один раз при запуске, после чего выбирают медленный путь выполнения кода на всё время работы процесса.
Решение на стороне источника — это настройка на стороне хоста: -cpu host в терминах QEMU, именованная модель, включающая AES-NI, или явное добавление +aes к модели. Вы не можете настроить это изнутри гостевой системы. Единственный надежный способ — открыть тикет в службу поддержки или выбрать тарифный план, гипервизор которого пробрасывает модель процессора напрямую (passthrough).
Измерение разрыва производительности с помощью openssl speed
Не принимайте опубликованные результаты тестов на веру. Запустите тест того шифра, который фактически использует ваш сервер.
openssl version
openssl speed -evp aes-128-gcmСтрока результата помечена как AES-128-GCM и показывает пропускную способность для шести размеров блоков в единицах 1000 байт в секунду. Для оценки массовой передачи данных смотрите столбец 8192 байта, так как столбец 16 байт отражает в основном накладные расходы на вызов и не дает представления о скорости загрузки файлов.
Теперь выполните ту же команду с отключенными программно AES-NI и PCLMULQDQ:
OPENSSL_ia32cap="~0x200000200000000" openssl speed -evp aes-128-gcmЭто значение взято из документации OpenSSL по вектору возможностей. Префикс ~ означает «очистить эти биты». Бит 57 соответствует AES-NI, а бит 33 — PCLMULQDQ, поэтому 0x200000200000000 указывает именно на эти два бита и ничего больше. Если второе число значительно меньше первого, значит, на вашем сервере работает AES-NI, и вы закончили. Если два числа совпадают, значит, OpenSSL уже использовал программный путь, так как флаг не был установлен и очищать было нечего.
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
}
]Это репрезентативные опубликованные показатели для современного ядра x86 с частотой около 3.4 ГГц, а не измерения, полученные на конкретном хосте. Воспринимайте их как ориентир. Аппаратный путь работает примерно с затратами 0.7 циклов на байт, а программный — примерно 11.0, что составляет около 4,850 МБ/с против 310 МБ/с на одно ядро. Две команды выше дают единственные числа, описывающие именно ваш сервер. Тот же подход применим и к остальным компонентам системы, поэтому используйте это вместе с воспроизводимым способом тестирования VPS, прежде чем делать выводы о выборе тарифного плана.
Принудительное включение битов через OPENSSL_ia32cap
Этот момент часто вызывает удивление. Инструкции AES-NI являются непривилегированными, и гипервизор не перехватывает их выполнение. Перехватывается только CPUID. Таким образом, хост может сообщить гостевой системе об отсутствии AES-NI, в то время как AESENC продолжает выполняться на полной скорости. Программное обеспечение пропускает быстрый путь выполнения, так как получило неверный ответ от CPUID. Сама инструкция при этом никогда не переставала работать.
OpenSSL позволяет ответить за процессор самостоятельно. Шестнадцатеричное значение в OPENSSL_ia32cap перезаписывает вектор возможностей, а не маскирует его.
OPENSSL_ia32cap="0x0200020207000000" openssl speed -evp aes-128-gcmЕсли этот запуск проходит в несколько раз быстрее обычного, значит, кремний поддерживает AES-NI, а хост скрывает это. Это прежде всего способ диагностики. Для OpenSSL это также является исправлением.
Как формируется это шестнадцатеричное значение
Первый логический вектор упаковывает EDX из листа CPUID 1 в младшие 32 бита, а ECX из того же листа — в старшие 32 бита. В младшей половине бит 24 отвечает за FXSR, бит 25 — за SSE, а бит 26 — за SSE2, что дает 0x07000000. В верхней половине бит 33 — это PCLMULQDQ, бит 41 — SSSE3, а бит 57 — AES-NI, что дает 0x02000202. В сумме это составляет 0x0200020207000000. SSSE3 включен в список, так как GHASH на базе PCLMULQDQ в OpenSSL использует pshufb для перестановки байтов, а стандартная модель гостевого процессора скрывает SSSE3 вместе с AES-NI.
Существует два предупреждения, и вы можете намеренно спровоцировать оба.
Установка только первого вектора оставляет последующие векторы нулевыми, что отключает пути выполнения AVX2 и AVX-512. В данном случае это сделано намеренно. Не пытайтесь принудительно включить биты AVX на гостевой системе с маскировкой, так как для регистров AVX требуется, чтобы операционная система включила расширенное состояние в XCR0, а ваше ядро отказалось это делать на основе того же замаскированного CPUID. В результате VEX-кодированная инструкция вызовет ошибку «неопределенный код операции» (undefined-opcode fault), и процесс завершится.
Принудительная активация AES-NI на ядре, которое физически не поддерживает эту инструкцию, немедленно завершает процесс:
Illegal instruction (core dumped)Это AESENC вызывает ошибку «неопределенный код операции», так как на этом ядре нет такой инструкции для выполнения. Некоторые версии прошивок серверов также могут аппаратно отключать AES-NI до следующей перезагрузки, и симптом будет идентичным. В любом случае решением является смена хоста, а не переменной окружения.
Чтобы сохранить переопределение для долго работающего сервиса, используйте 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Последняя команда должна вывести значение переменной обратно. Понимайте, на что вы идете: если этот сервер будет перенесен на хост, процессор которого действительно не поддерживает AES-NI, nginx завершится с ошибкой «недопустимая инструкция» (illegal instruction) при первом же TLS-соединении. Запишите это в свой регламент или вовсе не используйте переопределение в production, применяя его только для подтверждения проблемы при открытии тикета.
Что не исправляет переопределение
OPENSSL_ia32cap воздействует только на OpenSSL и больше ни на что. Любое другое программное обеспечение выполняет собственное определение функций и никогда не считывает эту переменную.
Ядро является важным случаем. dm-crypt и LUKS используют криптографический API ядра, и модуль aesni_intel отказывается загружаться, если отсутствует бит функции процессора:
modprobe: ERROR: could not insert 'aesni_intel': No such deviceДля этого не существует переменной пользовательского пространства. Ядро считывает CPUID один раз при загрузке, и это решение остается в силе до тех пор, пока вы не перезагрузитесь на другом хосте, поэтому ваш зашифрованный том остается на программном шифре независимо от того, что делает OpenSSL. Измерьте то, что вы получаете на самом деле:
sudo cryptsetup benchmark -c aes-xts -s 256Строка aes-xts 256b достигает тысяч МБ/с при аппаратном AES и сотен — без него. Среды выполнения языков программирования с собственным механизмом обнаружения, включая Go и Java, также остаются вне зоны влияния. Пакет crypto/aes в Go напрямую проверяет CPUID и без уведомлений использует свою программную реализацию с постоянным временем выполнения, если бит не установлен. Если сервис, терминирующий ваш TLS, является бинарным файлом Go, переменная OpenSSL ничего для него не изменит.
Если AES-NI недоступен, используйте ChaCha20
Алгоритм ChaCha20-Poly1305 был разработан для высокой производительности при программной реализации. На процессорах без поддержки AES-NI он обычно значительно быстрее, чем AES-GCM, поэтому на таких хостах целесообразно отказаться от приоритета AES.
Для nginx версии 1.19.4 и новее, собранного с OpenSSL 1.1.1 или новее:
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 отвечает за TLS 1.2. ssl_conf_command Ciphersuites отвечает за TLS 1.3, где у nginx нет отдельной директивы, и он передает строку напрямую в OpenSSL без проверки, поэтому опечатки там принимаются без предупреждений. Перезагрузите конфигурацию и проверьте, что предлагается клиенту:
sudo nginx -t && sudo systemctl reload nginx
openssl s_client -connect example.com:443 -tls1_3 </dev/null 2>/dev/null | grep -i cipherКорректный результат содержит TLS_CHACHA20_POLY1305_SHA256. Перед окончательным применением изменений запустите openssl speed -evp chacha20-poly1305 на том же сервере параллельно с тестом AES и примите решение на основе полученных показателей.
Хосты на архитектуре ARM используют другие расширения
AES-NI доступно только для x86. VPS на базе ARM использует криптографические расширения ARMv8 — отдельный набор инструкций, выполняющий те же функции. В архитектуре aarch64 флаги находятся в Features, а не в flags:
grep -m1 Features /proc/cpuinfoИщите aes и pmull. pmull является аналогом PCLMULQDQ для ARM, и GCM требует его по той же причине. Переменная переопределения OpenSSL на ARM называется OPENSSL_armcap и имеет собственную структуру битов, определённую в crypto/arm_arch.h в исходном коде OpenSSL, поэтому шестнадцатеричное значение для x86 из этого руководства там не применимо. На практике серверные ядра ARM, используемые в качестве хостов VPS, предоставляют эти расширения, поэтому проблема маскирования функций характерна в основном для архитектуры x86.
Режимы сбоев и соответствующие им сообщения
Отсутствует aes в /proc/cpuinfo, принудительный запуск выполняется значительно быстрее. Хост скрывает CPUID. Убедитесь, что имя модели является общим (generic), затем уточните у провайдера, какую модель процессора их гипервизор предоставляет гостевой системе.
Отсутствует aes в /proc/cpuinfo, принудительный запуск выводит Illegal instruction. Инструкции действительно отсутствуют или отключены на уровне прошивки. Перенесите рабочую нагрузку на другой хост.
aes присутствует, но пропускная способность всё равно низкая. Убедитесь, что вы смотрите на столбец 8192-byte и что ядро не занято другими процессами. На тарифах с общими ресурсами «шумный сосед» выглядит в точности как отсутствие функции процессора, пока вы не запустите тест дважды в разное время суток.
Маскированный и обычный запуски дают одинаковый результат. OpenSSL уже использовал программный путь выполнения. Этот результат является итогом теста, а не ошибкой.
Вложенная виртуализация меняет ответ на уровень ниже. Гостевая система внутри другой гостевой системы получает тот CPUID, который был передан промежуточным слоем, и в таких условиях легко незаметно потерять поддержку AES-NI. Если вы используете вложенные виртуальные машины на VPS, проверяйте наличие флага как внутри внутренней гостевой системы, так и на арендованной вами машине.
FAQ
Почему на моем VPS отсутствует флаг aes в /proc/cpuinfo?
Потому что гипервизор предоставляет гостевой системе обобщенную модель процессора. qemu64 и kvm64 не включают AES-NI в свои наборы функций, поэтому CPUID сообщает об их отсутствии, независимо от того, какой физический процессор установлен. Хосты делают это для того, чтобы работающую гостевую систему можно было перенести между машинами с разными процессорами. Запустите grep -m1 'model name' /proc/cpuinfo: строка вроде QEMU Virtual CPU version 2.5+ или Common KVM processor указывает на виртуализацию, тогда как реальное название модели Xeon или EPYC означает, что модель процессора пробрасывается напрямую и флаг действительно отсутствует на уровне «железа».
Действительно ли OPENSSL_ia32cap включает AES-NI или только имитирует это?
Она включает выполнение реальных инструкций. Инструкции AES-NI являются непривилегированными, и гипервизор никогда не перехватывает их, поэтому AESENC выполняется нативно, независимо от того, что сообщает CPUID. Перехватывается только инструкция CPUID. Установка OPENSSL_ia32cap в шестнадцатеричное значение заменяет ответ, который OpenSSL получил от CPUID, поэтому OpenSSL выбирает программный путь для аппаратного ускорения, и оборудование выполняет его на полной скорости. Если в процессоре действительно отсутствуют эти инструкции, процесс завершится с ошибкой Illegal instruction (core dumped) при первой же операции AES.
Ускорит ли это переопределение мой зашифрованный том LUKS?
Нет. Переменная OPENSSL_ia32cap считывается только библиотекой OpenSSL. LUKS и dm-crypt используют API криптографии ядра, где модуль aesni_intel не загружается с ошибкой modprobe: ERROR: could not insert 'aesni_intel': No such device, если бит функции не установлен. Ядро считывает CPUID при загрузке, и никакие переменные пользовательского пространства на это не влияют. Измерьте реальные показатели с помощью sudo cryptsetup benchmark -c aes-xts -s 256 и сравните строку aes-xts 256b с результатами на хосте, где этот флаг отображается.
Замедляет ли отсутствие флага AES-NI работу WireGuard?
Нет. WireGuard использует ChaCha20-Poly1305 для всех данных и никогда не применяет инструкции AES, поэтому его пропускная способность одинакова как на хосте с маскировкой флага, так и без нее. OpenVPN и IPsec, настроенные с использованием AES-GCM, действительно теряют в производительности на хосте без AES-NI. Таким образом, два туннеля на одном и том же VPS могут вести себя совершенно по-разному, и об этом стоит знать, прежде чем винить сеть.
Как проверить наличие AES-NI на VPS с архитектурой ARM?
Ядра ARM не имеют AES-NI. У них есть криптографические расширения ARMv8, которые выполняют ту же задачу с помощью других инструкций. Запустите grep -m1 Features /proc/cpuinfo и найдите aes и pmull, так как в aarch64 они перечислены в Features, а не в flags. Значение OPENSSL_ia32cap для x86 не имеет смысла на ARM. Эквивалентная переменная OpenSSL для этой архитектуры — OPENSSL_armcap, а ее битовая структура определена в crypto/arm_arch.h в исходном коде OpenSSL.