Falha de RAND_bytes no OpenVPN: erro de biblioteca ou nó ausente
Uma falha de RAND_bytes pode significar uma fonte de entropia recusada ou um nó escondido por uma jail. Leia a fila de erros e a visão do processo antes de criar dispositivos.
RAND_bytes() failed é uma afirmação sobre uma primitiva local, não sobre a conta, a chave pré-partilhada ou a porta remota. Duas falhas muito diferentes produzem essa mesma linha e deixam evidências diferentes. Uma é um motor criptográfico que se recusa a emitir bytes; a outra é um processo que não alcança a fonte de entropia para a qual o seu código foi compilado. Separá-las exige uma leitura da fila de erros e um olhar sobre a visão do sistema de ficheiros do próprio processo em jail, e essa verificação vem antes de criar qualquer coisa em /dev.
O que o valor de retorno distingue
O contrato documentado separa três resultados, não dois: 1 para o sucesso, -1 quando o método RAND carregado não suporta o pedido e 0 para as outras falhas, com a explicação deixada na fila de erros. A referência de RAND_bytes acrescenta um ponto fácil de ignorar: o gerador predefinido é semeado a partir do sistema operativo nas plataformas suportadas e, quando essa semente falha, fica num estado de erro em vez de devolver bytes previsíveis.
Só um desses números descreve o ambiente. Um -1 é uma questão de compilação, de fornecedor ou de funcionalidade, porque o código pediu algo que a implementação ativa não oferece. Um 0 com uma queixa de semente é uma questão de ambiente, porque o gerador não conseguiu obter entropia de todo. O número por si só nunca diz qual dos dois, e o nome da função também não.
Quando um nó de dispositivo escondido é a causa real
Um serviço confinado vê uma árvore diferente da shell em que está a escrever. As jails chroot, as imagens de contentores e as unidades endurecidas apresentam habitualmente um /dev com menos nós do que o do anfitrião. Se a implementação que falha abre /dev/urandom pelo caminho, essa diferença basta para quebrar o arranque enquanto o anfitrião parece perfeitamente saudável. Um dispositivo que existe fora da jail não prova nada sobre o que o processo em jail tem permissão de abrir.
Nem toda a interface aleatória precisa de um caminho, porém. A chamada de sistema getrandom do Linux não aceita nome de ficheiro nem descritor de ficheiro, e a sua página de manual nota que essa propriedade é útil precisamente onde um chroot esconde os caminhos dos dispositivos.
Assim, a explicação do nó ausente é uma hipótese sobre um ramo do código, não uma regra sobre números aleatórios em geral. Vale quando as evidências nomeiam um caminho, e é conjectura quando as evidências nomeiam outra coisa.
Leia a fila de erros, depois a visão do processo
A forma mais rápida de resolver a questão é ler o que a biblioteca já registou e depois olhar diretamente para a visão em jail. Comece por a compilação de OpenSSL que o serviço carregou de facto, porque as pilhas de fornecedores diferem entre versões principais e entre distribuições. Pergunte depois ao systemd que processo está em execução e que restrições lhe são aplicadas. Só depois de ambos os factos estarem em cima da mesa vale a pena listar os nós que esse processo consegue ver.
openssl version -a
systemctl show openvpn-server@lab -p MainPID -p PrivateDevices -p DevicePolicy
pid=$(systemctl show -p MainPID --value openvpn-server@lab)
sudo ls -l /proc/"$pid"/root/dev/urandom /proc/"$pid"/root/dev/random
Execute essas leituras no mesmo processo, não na shell que por acaso está aberta. Comparar o /dev do anfitrião com a visão de um processo alheio é como a dependência errada acaba reparada duas vezes.
Adicione a dependência só depois de a saber nomear
Quando as evidências nomeiam mesmo um caminho, a reparação é pequena: torne esse único caminho disponível dentro da jail e não mexa em mais nada. Uma montagem bind do nó único costuma bastar, e mknod com os números major e minor padrão do nó produz o mesmo resultado em instalações onde a montagem não é opção. Alargar a sandbox, conceder acesso a todos os dispositivos de caracteres ou desligar PrivateDevices troca uma correção dirigida por uma regressão de segurança permanente.
Um daemon de entropia não substitui essa reparação. Pacotes como haveged mudam a forma como o pool do kernel é alimentado; não conseguem criar um caminho que um processo em jail tenha permissão de abrir. Instalar um pode fazer um gráfico de saúde parecer melhor enquanto a chamada de abertura continua a falhar. Mantenha os dois problemas separados, ou a próxima falha voltará a ser atribuída à camada errada.
Verifique o gerador e o túnel em separado
Um reinício é o único teste que reutiliza a mesma configuração, a mesma unidade e o mesmo confinamento que produziram a falha. Execute-o e depois leia as entradas mais recentes do journal em vez de uma amostra de um arranque anterior.
sudo systemctl restart openvpn-server@lab
sudo journalctl -u openvpn-server@lab --since -5min --no-pager | grep -iE 'rand|entropy|Initialization Sequence'
Procure duas coisas distintas nessa saída: a mensagem da fonte aleatória a desaparecer e o túnel a atingir o seu marco habitual de arranque. Um gerador que volta a funcionar elimina uma falha local e não diz nada sobre a identidade do servidor, as credenciais ou o encaminhamento. Teste isso com a mesma configuração de cliente que falhava antes, para que o resultado seja comparável e não apenas melhor.