Como verificar e reativar AES-NI numa VPS
Veja se a VPS expõe AES-NI, meça o impacto do CPUID mascarado no AES-GCM e reative o caminho rápido com OPENSSL_ia32cap quando possível.
O que o AES-NI realmente proporciona numa VPS
O AES-NI é um conjunto de seis instruções x86 que executam uma ronda do AES (advanced encryption standard) em hardware numa VPS. Se o modelo de CPU apresentado pelo fornecedor as ocultar, o silício subjacente continua a incluí-las, mas o OpenSSL não as consegue detetar e recorre a uma implementação em software que consome aproximadamente dez vezes mais ciclos por byte. Pode verificar a funcionalidade com um comando, medir a diferença com dois e, muitas vezes, reativar o caminho rápido com uma variável de ambiente.
As instruções são AESENC, AESENCLAST, AESDEC, AESDECLAST, AESIMC e AESKEYGENASSIST. A Intel lançou-as em 2010 e a AMD seguiu-a, pelo que qualquer CPU de servidor que provavelmente alugue já inclui esse silício. Uma instrução complementar, PCLMULQDQ, executa multiplicação sem transporte, necessária para que o GCM (Galois/counter mode) construa a sua tag de autenticação. O AES-GCM só é rápido quando ambas estão disponíveis, porque a cifra e a tag são tarefas distintas.
Quatro áreas numa VPS onde isto aparece na monitorização:
- Terminação de TLS (transport layer security). Um servidor Web que disponibiliza AES-128-GCM ou AES-256-GCM passa a maior parte do tempo de criptografia de dados dentro do AES.
- Volumes encriptados. O LUKS (Linux unified key setup) e o dm-crypt executam
aes-xtsem cada leitura e cada escrita, no kernel e na CPU. - Tráfego VPN baseado em AES. O OpenVPN com
AES-256-GCMe o IPsec com AES-GCM dependem desta funcionalidade. - Backups encriptados. Tudo o que encripta um fluxo com AES antes de este sair do servidor suporta o mesmo custo.
Uma carga de trabalho comum não é afetada. O WireGuard usa ChaCha20-Poly1305 para os seus dados e nunca utiliza AES, pelo que uma VPN WireGuard autoalojada funciona à mesma velocidade num host com a flag mascarada. Essa diferença é uma razão prática para ponderar WireGuard em comparação com OpenVPN antes de escolher um túnel para uma VPS económica.
Como verificar se o seu VPS tem AES-NI
O kernel copia os bits de funcionalidades do CPUID para /proc/cpuinfo, portanto um grep responde à questão.
grep -m1 -o '\baes\b' /proc/cpuinfo
lscpu | grep -i -o '\baes\b'Qualquer um dos comandos que apresente aes significa que a CPU anuncia AES-NI a este guest. Se não apresentar nada, a CPU não anuncia AES-NI. lscpu lê os mesmos flags, portanto os dois comandos têm sempre o mesmo resultado. Use o que estiver instalado.
Agora verifique qual CPU o host afirma que está a utilizar.
grep -m1 'model name' /proc/cpuinfoUma string de modelo real, como Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz ou AMD EPYC 7443P 24-Core Processor, significa que o host disponibiliza o modelo da CPU física ao seu sistema. QEMU Virtual CPU version 2.5+ ou Common KVM processor significa que está a acontecer outra coisa. Esse é o caso que vale a pena compreender.
Por que a flag não aparece quando o silício a suporta
CPUID é a instrução que um programa usa para perguntar à CPU o que ela suporta. Dentro de uma máquina virtual, CPUID gera sempre uma saída para o hypervisor, que decide o que é comunicado ao guest. A maioria dos painéis expõe essa decisão como um modelo de CPU do guest. qemu64 e kvm64 são modelos genéricos de base, e nenhum deles inclui AES-NI ou SSSE3 no conjunto de funcionalidades. Por isso, o guest não vê a flag aes, mesmo quando o host físico é um EPYC atual. Um VPS é um guest no hardware de outra pessoa, portanto cada funcionalidade que ele comunica é uma decisão tomada um nível acima. Se esta camada for nova para si, comece por o que é um VPS.
Os hosts escolhem deliberadamente um modelo genérico, porque a migração em direto entre máquinas com processadores diferentes só funciona se o guest nunca tiver recebido informação sobre uma funcionalidade que não exista no destino. O custo fica do seu lado. O kernel e a sua cópia do OpenSSL leem esse CPUID mascarado uma vez no arranque. Depois, ambos escolhem o caminho de código mais lento durante toda a vida do processo.
A correção na origem é uma definição no host: -cpu host nos termos do QEMU, um modelo nomeado que inclua AES-NI ou um +aes explícito adicionado ao modelo. Não pode definir nada disso a partir do guest. Abrir um pedido de suporte ou escolher um plano cujo hypervisor transmita o modelo de CPU é a solução duradoura.
Meça a diferença com openssl speed
Não aceite cegamente um benchmark publicado. Execute a cifra que o seu próprio servidor negoceia.
openssl version
openssl speed -evp aes-128-gcmA linha do resultado tem a etiqueta AES-128-GCM e mostra o débito em seis tamanhos de bloco, em unidades de 1000 bytes por segundo. Leia a coluna de 8192 bytes para transferências em massa, porque a coluna de 16 bytes é dominada pela sobrecarga de cada chamada e não fornece informação útil sobre uma transferência de ficheiro.
Agora execute o mesmo comando com AES-NI e PCLMULQDQ desativados no software:
OPENSSL_ia32cap="~0x200000200000000" openssl speed -evp aes-128-gcmEsse valor vem da documentação do vetor de capacidades do próprio OpenSSL. Um ~ inicial significa "limpar estes bits". O bit 57 é AES-NI e o bit 33 é PCLMULQDQ, portanto 0x200000200000000 identifica exatamente esses dois bits, sem outros. Um segundo número muito inferior ao primeiro significa que o seu sistema tem AES-NI funcional e que a verificação terminou. Dois números iguais significam que o OpenSSL já estava no caminho de software, porque o sinalizador nunca esteve presente para ser limpo.
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
}
]Estes são valores publicados representativos de um núcleo x86 moderno a cerca de 3.4 GHz, não uma medição feita num host específico. Interprete-os como uma tendência. O caminho de hardware funciona a aproximadamente 0.7 ciclos por byte e o fallback de software a aproximadamente 11.0, o que corresponde a cerca de 4,850 MB/s contra 310 MB/s num núcleo. Os dois comandos acima produzem o único número que descreve o seu servidor. O mesmo princípio aplica-se ao resto da máquina, por isso combine este teste com uma forma repetível de fazer benchmark de um VPS antes de tirar conclusões sobre um plano.
Forçar a reativação dos bits com OPENSSL_ia32cap
Esta é a parte que surpreende muitas pessoas. As instruções AES-NI não exigem privilégios, e o hypervisor não as intercepta. Apenas CPUID é interceptado. Assim, um host pode informar ao seu guest que AES-NI não está disponível, enquanto AESENC continua a ser executado nativamente à velocidade máxima. O software ignora o caminho rápido porque consultou CPUID e recebeu uma resposta incorreta. A instrução em si nunca deixou de funcionar.
O OpenSSL permite responder em nome da CPU. Um valor hexadecimal simples em OPENSSL_ia32cap substitui o vetor de capacidades, em vez de apenas mascará-lo.
OPENSSL_ia32cap="0x0200020207000000" openssl speed -evp aes-128-gcmSe essa execução for várias vezes mais rápida do que a execução normal, o hardware tem AES-NI e o host está a ocultá-lo. Isto é primeiro um diagnóstico. No OpenSSL, também funciona como correção.
Como esse valor hexadecimal é construído
O primeiro vetor lógico reúne o EDX da folha 1 de CPUID nos 32 bits inferiores e o ECX da folha 1 nos 32 bits superiores. Na metade inferior, o bit 24 é FXSR, o bit 25 é SSE e o bit 26 é SSE2, resultando em 0x07000000. Na metade superior, o bit 33 é PCLMULQDQ, o bit 41 é SSSE3 e o bit 57 é AES-NI, resultando em 0x02000202. Em conjunto, isso corresponde a 0x0200020207000000. SSSE3 aparece na lista porque o GHASH baseado em PCLMULQDQ do OpenSSL usa pshufb para trocar bytes, e um modelo genérico de CPU do guest oculta SSSE3 juntamente com AES-NI.
Há dois avisos importantes, e pode provocar ambos de propósito.
Definir apenas o primeiro vetor deixa os vetores seguintes a zero, desativando os caminhos de código AVX2 e AVX-512. Isso é intencional neste caso. Não tente forçar os bits AVX num guest mascarado, porque os registos AVX precisam que o sistema operativo ative o estado estendido em XCR0, e o kernel recusou fazê-lo com base no mesmo CPUID mascarado. Uma instrução codificada com VEX provoca então uma falha de opcode indefinido e o processo termina.
Forçar AES-NI numa core que realmente não o suporta termina imediatamente o processo:
Illegal instruction (core dumped)Isso é AESENC a provocar uma falha de opcode indefinido, porque nessa core não existe nenhuma instrução desse tipo para executar. Alguns firmwares de servidores também podem desativar AES-NI no hardware até ao próximo reset, e o sintoma é idêntico. Em qualquer dos casos, a solução é usar outro host, não outra variável de ambiente.
Para manter a substituição num serviço de execução prolongada, use um drop-in do systemd.
sudo systemctl edit nginx[Service]
Environment="OPENSSL_ia32cap=0x0200020207000000"sudo systemctl daemon-reload
sudo systemctl restart nginx
sudo systemctl show nginx -p EnvironmentO último comando deve apresentar novamente a variável. Compreenda o que está a configurar: se esse servidor for migrado para um host cuja CPU realmente não suporte AES-NI, o nginx termina com uma instrução ilegal na primeira ligação TLS. Registe isto no seu runbook ou mantenha a substituição totalmente fora de produção e use-a apenas para comprovar o problema ao abrir um ticket.
O que o override não corrige
OPENSSL_ia32cap chega ao OpenSSL e a mais nenhum componente. Todos os outros programas fazem a sua própria deteção de funcionalidades e nunca leem essa variável.
O kernel é o caso importante. dm-crypt e LUKS usam a API de criptografia do kernel, e o módulo aesni_intel recusa-se a carregar quando o bit da funcionalidade da CPU está ausente:
modprobe: ERROR: could not insert 'aesni_intel': No such deviceNão existe uma variável equivalente no espaço do utilizador. O kernel lê o CPUID uma vez durante o boot, e essa decisão mantém-se até reiniciar noutro host. Por isso, o seu volume encriptado continua a usar a cifra de software, independentemente do que o OpenSSL esteja a fazer. Meça o desempenho efetivo:
sudo cryptsetup benchmark -c aes-xts -s 256A linha aes-xts 256b atinge milhares de MiB/s com AES por hardware e apenas algumas centenas sem ele. Os runtimes de linguagens com deteção própria, incluindo Go e Java, também não são afetados. crypto/aes do Go verifica o CPUID diretamente e usa silenciosamente a sua implementação de software em tempo constante quando o bit está limpo. Se o serviço que termina o TLS for um binário Go, a variável do OpenSSL não altera nada nesse serviço.
Se não conseguir usar AES-NI, prefira ChaCha20
ChaCha20-Poly1305 foi desenvolvido para ser rápido em software puro. Num núcleo sem AES-NI utilizável, normalmente supera o AES-GCM por uma margem ampla. Por isso, a decisão adequada nesse host é deixar de dar preferência ao AES.
Para nginx 1.19.4 ou posterior, compilado com OpenSSL 1.1.1 ou posterior:
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 abrange TLS 1.2. ssl_conf_command Ciphersuites abrange TLS 1.3. Nesta versão, o nginx não tem uma diretiva específica e encaminha a cadeia diretamente para o OpenSSL sem a validar. Por isso, um erro de escrita é aceite silenciosamente. Recarregue a configuração e confirme o que é oferecido a um cliente:
sudo nginx -t && sudo systemctl reload nginx
openssl s_client -connect example.com:443 -tls1_3 </dev/null 2>/dev/null | grep -i cipherUm resultado correto identifica TLS_CHACHA20_POLY1305_SHA256. Antes de aplicar a alteração, execute openssl speed -evp chacha20-poly1305 ao lado do teste com AES no mesmo host e deixe os dois números determinar a decisão.
Os hosts ARM usam extensões diferentes
AES-NI existe apenas em x86. Um VPS ARM usa as extensões criptográficas ARMv8, um conjunto de instruções separado que executa a mesma função. Em aarch64, os flags ficam em Features, e não em flags:
grep -m1 Features /proc/cpuinfoProcure aes e pmull. pmull é o equivalente ARM de PCLMULQDQ, e o GCM precisa dele pelo mesmo motivo. A variável de substituição do OpenSSL em ARM é OPENSSL_armcap, com o seu próprio esquema de bits definido em crypto/arm_arch.h no código-fonte do OpenSSL. Por isso, o valor hexadecimal x86 deste guia não tem qualquer significado nesse contexto. Na prática, os núcleos ARM dos servidores vendidos como hosts VPS expõem estas extensões. Portanto, o problema de funcionalidades mascaradas ocorre sobretudo em x86.
Modos de falha e os textos que verá
Sem aes em /proc/cpuinfo, e a execução forçada é muito mais rápida. O host está a ocultar o CPUID. Confirme se o nome do modelo é genérico e pergunte ao fornecedor qual é o modelo de CPU convidada apresentado pelo hipervisor.
Sem aes em /proc/cpuinfo, e a execução forçada apresenta Illegal instruction. As instruções estão realmente ausentes ou o firmware desativou-as. Mova a carga de trabalho para outro host.
aes está presente, mas o débito continua baixo. Confirme se está a ler a coluna de 8192 bytes e se nenhum outro processo está a usar o núcleo. Num plano partilhado, um vizinho ruidoso parece exatamente uma funcionalidade de CPU ausente até executar o teste duas vezes em horas diferentes do dia.
A execução mascarada e a execução normal apresentam o mesmo número. O OpenSSL já estava a usar o caminho de software. Esse resultado é a conclusão, não um erro no teste.
A virtualização aninhada altera a resposta um nível abaixo. Uma máquina convidada dentro de outra recebe o CPUID que a camada intermédia decidiu transmitir, e é fácil perder o AES-NI nesse nível sem perceber. Se executar máquinas virtuais aninhadas num VPS, verifique a flag dentro da máquina convidada interna e também na máquina que alugou.
FAQ
Por que o meu VPS não tem a flag aes em /proc/cpuinfo?
Porque o hypervisor está a apresentar um modelo genérico de CPU convidada. qemu64 e kvm64 não incluem AES-NI nos respetivos conjuntos de funcionalidades, por isso CPUID indica que a extensão está ausente, independentemente do processador físico. Os hosts fazem isto para que uma máquina convidada em execução possa ser migrada entre máquinas com CPUs diferentes. Execute grep -m1 'model name' /proc/cpuinfo: uma string como QEMU Virtual CPU version 2.5+ ou Common KVM processor é o indicador, enquanto uma string de modelo Xeon ou EPYC real significa que o modelo de CPU é passado diretamente e que a flag está realmente ausente no silício.
A variável OPENSSL_ia32cap ativa realmente o AES-NI ou apenas simula a sua presença?
Ativa as instruções reais. As instruções AES-NI não requerem privilégios e o hypervisor nunca as intercepta, por isso AESENC é executado nativamente, independentemente do que CPUID indicar. Apenas a instrução CPUID é interceptada. Definir OPENSSL_ia32cap com um valor hexadecimal simples substitui a resposta obtida pelo OpenSSL através de CPUID, fazendo com que o OpenSSL selecione o caminho de execução de hardware; o hardware executa-o depois à velocidade máxima. Se o silício não tiver realmente essas instruções, o processo termina com Illegal instruction (core dumped) na primeira operação AES.
A substituição vai acelerar o meu volume encriptado com LUKS?
Não. OPENSSL_ia32cap é lida pelo OpenSSL e por mais nenhum componente. LUKS e dm-crypt usam a API de criptografia do kernel, onde o módulo aesni_intel falha ao carregar com modprobe: ERROR: could not insert 'aesni_intel': No such device quando o bit da funcionalidade está limpo. O kernel lê CPUID durante o arranque, e nenhuma variável do espaço de utilizador altera esse comportamento. Meça o valor real com sudo cryptsetup benchmark -c aes-xts -s 256 e compare a linha aes-xts 256b com a de um host que indique a flag.
A ausência da flag AES-NI torna o WireGuard mais lento?
Não. O WireGuard usa ChaCha20-Poly1305 para todos os dados e nunca usa instruções AES, por isso o débito é igual num host com as funcionalidades mascaradas e num host sem mascaramento. OpenVPN e IPsec configurados com AES-GCM perdem débito num host sem AES-NI. Por isso, dois túneis no mesmo VPS podem comportar-se de forma muito diferente, algo que deve saber antes de atribuir o problema à rede.
Como verifico a existência de AES-NI num VPS ARM?
Os cores ARM não têm AES-NI. Têm as extensões criptográficas ARMv8, que executam a mesma função com instruções diferentes. Execute grep -m1 Features /proc/cpuinfo e procure aes e pmull, porque o aarch64 apresenta essas extensões em Features, e não em flags. O valor x86 OPENSSL_ia32cap não tem significado em ARM. A variável equivalente do OpenSSL nesse caso é OPENSSL_armcap, e o respetivo esquema de bits está definido em crypto/arm_arch.h no código-fonte do OpenSSL.