Як перевірити й увімкнути AES-NI на VPS
Перевірте AES-NI на VPS, виміряйте вплив прихованого CPUID на AES-GCM і поверніть інструкції через OPENSSL_ia32cap без зміни конфігурації CPU.
Що насправді дає AES-NI на VPS
AES-NI на VPS — це набір із шести інструкцій x86, які виконують один раунд AES (advanced encryption standard) апаратно. Якщо модель CPU, яку показує ваш провайдер, приховує ці інструкції, вони все одно доступні у фізичному процесорі, але OpenSSL їх не бачить і переходить на програмну реалізацію, яка потребує приблизно в десять разів більше тактів на байт. Перевірити наявність цієї функції можна однією командою, виміряти різницю — двома, а швидкий шлях часто можна знову ввімкнути за допомогою однієї змінної середовища.
Ці інструкції мають назви AESENC, AESENCLAST, AESDEC, AESDECLAST, AESIMC і AESKEYGENASSIST. Intel почала постачати їх у 2010 році, а AMD — пізніше, тому вони є у фізичній частині майже будь-якого серверного CPU, який ви можете орендувати. Супутня інструкція PCLMULQDQ виконує множення без перенесення, потрібне режиму GCM (Galois/counter mode) для формування тегу автентифікації. AES-GCM працює швидко лише за наявності обох можливостей, оскільки шифрування та формування тегу — це окремі операції.
Ось чотири компоненти VPS, де це позначається на моніторингу:
- Завершення TLS (transport layer security). Вебсервер, який обслуговує AES-128-GCM або AES-256-GCM, витрачає більшу частину часу на масові криптографічні операції всередині AES.
- Зашифровані томи. LUKS (Linux unified key setup) і dm-crypt виконують
aes-xtsпід час кожного читання та запису в ядрі, на CPU. - VPN-трафік на основі AES. OpenVPN із
AES-256-GCMта IPsec з AES-GCM активно використовують цю можливість. - Зашифровані резервні копії. Будь-який процес, який шифрує потік за допомогою AES перед передаванням за межі сервера, має такі самі витрати.
Один поширений тип навантаження взагалі від цього не залежить. WireGuard використовує ChaCha20-Poly1305 для передавання даних і не звертається до AES, тому self-hosted VPN WireGuard працює з тією самою швидкістю на хості, де цей прапорець приховано. Це практична причина порівняти WireGuard та OpenVPN, перш ніж вибирати тунель для дешевого VPS.
Як перевірити, чи підтримує ваш VPS AES-NI
Ядро копіює біти функцій CPUID у /proc/cpuinfo, тому для перевірки достатньо однієї команди grep.
grep -m1 -o '\baes\b' /proc/cpuinfo
lscpu | grep -i -o '\baes\b'Якщо будь-яка з команд виводить aes, це означає, що CPU повідомляє цьому гостьовому середовищу про підтримку AES-NI. Порожній вивід означає, що підтримки немає. lscpu читає ті самі прапорці, тому результати завжди збігаються. Використовуйте встановлену команду.
Тепер перевірте, який CPU, за твердженням хоста, використовується у вашому середовищі.
grep -m1 'model name' /proc/cpuinfoРядок із реальною моделлю, наприклад Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz або AMD EPYC 7443P 24-Core Processor, означає, що хост передає вам модель фізичного CPU. QEMU Virtual CPU version 2.5+ або Common KVM processor означає, що відбувається щось інше. Саме цей випадок варто дослідити.
Чому прапорець відсутній, хоча процесор його підтримує
CPUID — це інструкція, за допомогою якої програма запитує в CPU, які можливості він підтримує. Усередині віртуальної машини CPUID завжди перехоплює гіпервізор, тому саме гіпервізор визначає, що повідомити гостьовій системі. Більшість панелей надає доступ до цього рішення як до моделі CPU гостя. qemu64 і kvm64 — це універсальні базові моделі, і жодна з них не містить AES-NI або SSSE3 у наборі можливостей. Тому гостьова система не бачить прапорець aes, навіть якщо фізичний хост — сучасний EPYC. VPS працює як гість на чужому обладнанні, тому кожна можливість, яку він повідомляє, визначається на рівень вище. Якщо ця багаторівнева схема для вас нова, почніть зі статті що таке VPS.
Хости навмисно використовують універсальну модель, оскільки жива міграція між машинами з різними процесорами працює лише тоді, коли гостьовій системі не повідомляли про можливість, якої немає на цільовому хості. Витрати несе користувач. Ваш kernel і ваша копія OpenSSL зчитують замаскований CPUID під час запуску, після чого протягом усього часу роботи процесу використовують повільний шлях виконання.
Виправлення на джерелі полягає в налаштуванні на стороні хоста: -cpu host у термінах QEMU, іменована модель, що містить AES-NI, або явний +aes, доданий до моделі. Налаштувати це зсередини гостьової системи неможливо. Надсилання запиту до служби підтримки або вибір тарифу, гіпервізор якого передає модель CPU без змін, є надійним довгостроковим рішенням.
Виміряйте різницю за допомогою 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 GHz, а не вимірювання на конкретному хості. Розглядайте їх як загальну картину. Апаратний шлях потребує приблизно 0.7 тактів на байт, а програмний резервний шлях — приблизно 11.0, що на одному ядрі відповідає близько 4,850 MB/s проти 310 MB/s. Єдине число, що описує саме ваш сервер, дають дві наведені вище команди. Такий самий підхід застосовується до решти системи, тому перед висновками щодо тарифу скористайтеся відтворюваним способом протестувати 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 це водночас спосіб виправити проблему.
Як формується це шістнадцяткове значення
Перший логічний вектор містить CPUID leaf 1 EDX у молодших 32 бітах, а leaf 1 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 спричинить помилку невизначеного коду операції, і процес завершиться.
Примусове ввімкнення 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 завершиться з помилкою неприпустимої інструкції під час першого TLS-з’єднання. Додайте це до runbook або взагалі не використовуйте перевизначення в production. Застосовуйте його лише для підтвердження причини, коли відкриваєте заявку.
Що не виправляє override
OPENSSL_ia32cap надходить до OpenSSL і більше ні до чого. Усі інші компоненти програмного забезпечення виконують власне визначення можливостей і не читають цю змінну.
Ядро — важливий виняток. dm-crypt і LUKS використовують криптографічний API ядра, а модуль aesni_intel відмовляється завантажуватися, якщо біт можливості CPU відсутній:
modprobe: ERROR: could not insert 'aesni_intel': No such deviceДля цього немає змінної user space. Ядро читає CPUID один раз під час завантаження, і це рішення залишається чинним, доки ви не перезавантажите систему на іншому хості. Тому зашифрований том використовуватиме програмний шифр незалежно від того, що робить OpenSSL. Виміряйте фактичну продуктивність:
sudo cryptsetup benchmark -c aes-xts -s 256Рядок aes-xts 256b дає показники на рівні тисяч MiB/s з апаратним 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. Для 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.
Режими збою та рядки, які ви побачите
У /proc/cpuinfo немає aes, а примусовий запуск працює значно швидше. Хост приховує CPUID. Переконайтеся, що назва моделі є узагальненою, а потім запитайте провайдера, яку модель гостьового CPU його гіпервізор надає віртуальній машині.
У /proc/cpuinfo немає aes, а примусовий запуск виводить Illegal instruction. Ці інструкції справді відсутні або вимкнені мікропрограмою. Перенесіть робоче навантаження на інший хост.
aes присутній, але пропускна здатність усе ще низька. Перевірте, що ви читаєте стовпець для 8192-байтових блоків і що інші процеси не використовують це ядро. У спільному тарифному плані активність сусідньої віртуальної машини має такий самий вигляд, як відсутність функції CPU, доки ви не запустите тест двічі в різний час доби.
Запуск із маскуванням і звичайний запуск дають однакове значення. OpenSSL уже використовував програмний шлях. Цей результат і є висновком, а не помилкою в тесті.
Вкладена віртуалізація змінює результат на один рівень нижче. Гостьова система всередині іншої гостьової системи отримує ті значення CPUID, які середній рівень вирішив передати далі. У такій схемі AES-NI легко втратити, не помітивши цього. Якщо ви запускаєте вкладені віртуальні машини на VPS, перевірте цей прапорець і всередині внутрішньої гостьової системи, і на орендованій машині.
FAQ
Чому в моєму VPS немає прапорця aes у /proc/cpuinfo?
Тому що гіпервізор подає узагальнену модель гостьового CPU. qemu64 і kvm64 не містять AES-NI у своїх наборах функцій, тому CPUID повідомляє, що ця функція відсутня, незалежно від фізичного процесора. Хости роблять це, щоб запущену гостьову систему можна було мігрувати між машинами з різними CPU. Виконайте grep -m1 'model name' /proc/cpuinfo: рядок на кшталт QEMU Virtual CPU version 2.5+ або Common KVM processor є показовим, тоді як справжній рядок моделі Xeon або EPYC означає, що модель CPU передається без маскування, і прапорець справді відсутній у процесорі.
Чи справді OPENSSL_ia32cap вмикає AES-NI, чи лише імітує його наявність?
Він вмикає використання справжніх інструкцій. Інструкції AES-NI непривілейовані, і гіпервізор ніколи їх не перехоплює, тому AESENC виконується безпосередньо на CPU незалежно від результату CPUID. Перехоплюється лише інструкція CPUID. Якщо встановити OPENSSL_ia32cap у звичайне шістнадцяткове значення, воно замінює відповідь, яку OpenSSL отримав від CPUID, тому OpenSSL вибирає апаратний шлях виконання, а CPU виконує його на повній швидкості. Якщо процесор справді не підтримує ці інструкції, процес завершиться з 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 під час завантаження, і жодна змінна в user space цього не змінює. Виміряйте фактичний показник за допомогою 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 на ARM VPS?
ARM-ядра не мають AES-NI. Вони мають криптографічні розширення ARMv8, які виконують ту саму функцію за допомогою інших інструкцій. Виконайте grep -m1 Features /proc/cpuinfo і знайдіть aes та pmull, оскільки aarch64 перелічує їх у Features, а не в flags. Значення x86 OPENSSL_ia32cap не має сенсу на ARM. Еквівалентна змінна OpenSSL там — OPENSSL_armcap, а її розклад бітів визначено в crypto/arm_arch.h у вихідному коді OpenSSL.