Script auth-user-pass-verify do OpenVPN: o código de saída certo
O estado de saída do hook é a decisão, não a sua linha de log. Mapeie os resultados do verificador para 0, 1 ou adiado, leia o arquivo de credenciais como dados e falhe fechado.
OpenVPN atua sobre exatamente uma saída de um script de login: o estado de saída. Tudo o mais que o script faz é decoração. Um hook pode chegar ao verificador certo, analisar a resposta e imprimir uma linha amigável enquanto devolve um estado que significa o oposto do que o autor pretendia. Leia primeiro o contrato e escreva o adaptador em torno dele, e toda essa classe de bugs desaparece.
O contrato é pequeno: identifique a conta que foi enviada, faça ao verificador aprovado a pergunta prevista e aceite apenas o resultado positivo documentado desse verificador para essa solicitação. Todo o resto é infraestrutura.
Os três estados que o hook pode devolver
Para auth-user-pass-verify, o manual do OpenVPN 2.6 define o código de saída 0 como aceitação, 1 como rejeição e 2 como autenticação adiada. Essas são as únicas três respostas que o servidor entende, e cada uma tem uma consequência diferente para a conexão. Um script que sai com o estado do seu último comando deixou, na prática, que uma chamada de log ou uma etapa de limpeza vote. Escreva o estado explicitamente em cada ramo, incluindo os ramos que nunca chegaram ao verificador.
O sucesso HTTP e o sucesso da autenticação são camadas separadas, e confundi-los é a forma mais comum de este hook falhar. Uma resposta 202 significa que a solicitação foi aceita para processamento, não que uma palavra-passe foi verificada. Defina o estado esperado, a forma da resposta e o veredito final a partir do contrato de API que lhe deram, e não invente um nome de campo porque parece plausível.
Se a resposta chegar mesmo mais tarde, esse é o caminho adiado e tem de ser construído como tal. Uma tentativa adiada que nunca é concluída retém recursos até o tempo limite expirar.
Leia o arquivo de credenciais exatamente como está escrito
Com o método documentado via arquivo, o servidor escreve o nome de utilizador e a palavra-passe como as duas primeiras linhas de um arquivo temporário, passa esse caminho ao script e remove o arquivo quando o script regressa. Leia essas duas linhas como dados: nunca faça source do arquivo, nunca o avalie e nunca cole o seu conteúdo numa linha de comandos onde a tabela de processos o possa ver. Preserve a palavra-passe byte a byte, porque normalizá-la como normalizaria um nome de utilizador altera a credencial em silêncio. O diretório usado para esse arquivo temporário é uma decisão de implementação, por isso confirme que existe e que não é legível por todos no anfitrião.
Mantenha a credencial do próprio verificador separada da do utilizador: pertence à integração do gateway, vive no armazém de segredos da plataforma e só precisa da permissão de verificar, pelo que não é prova de que uma pessoa concreta se autenticou. Também não deixe o hook tornar-se um segundo verificador de palavras-passe. Reimplementar à mão uma comparação de hash não se torna seguro por otimizar o ciclo, e um verificador mantido já compreende o esquema salgado e dispendioso do armazém de contas.
Decida o que significa uma resposta não classificável
Tempos limite, erros de transporte, respostas malformadas e um backend indisponível partilham uma propriedade: nenhum deles é uma verificação positiva. O hook deve falhar fechado para cada um deles e registar um motivo sobre o qual um operador possa agir. Esse motivo pertence a um log protegido, não a uma mensagem devolvida à pessoa que tenta ligar-se.
Seja igualmente cuidadoso com os caminhos que nunca chegam ao verificador. Um valor de configuração em falta, um segredo ilegível ou uma dependência não instalada devem produzir uma rejeição com um marcador operacional distinto em vez de uma aceitação acidental. Teste esses ramos removendo a dependência num laboratório em vez de raciocinar sobre eles. Onde uma falha de registo pode mudar o estado de saída, o registo tornou-se parte da decisão e tem de ser tornado inofensivo.
A configuração do servidor também importa aqui: os scripts definidos pelo utilizador só são respeitados quando a configuração em execução os permite no nível de segurança exigido, e essa definição é uma escolha deliberada, não um valor predefinido a subir.
Verifique o veredito, não a linha impressa
Construa um teste que inspecione diretamente o estado de saída. Escreva um arquivo de credenciais de exemplo, execute o hook contra ele com uma conta sabidamente boa e uma sabidamente má, e leia o estado que a shell reporta em vez do texto que o script imprime. Essa única verificação apanha a maioria dos bugs de contrato antes de um cliente sequer se ligar.
Depois passe os mesmos casos por um cliente real contra um ouvinte que não seja de produção e confirme o que o servidor registou: aceite, rejeitado ou adiado. Cubra uma conta desativada, uma palavra-passe errada, uma resposta malformada do verificador, um tempo limite e um verificador inalcançável. Se algum caso terminar num estado que não consegue nomear, o adaptador não está terminado.
Mantenha o mapeamento do resultado do verificador para o estado de saída ao lado do script no repositório, para que o próximo engenheiro possa ler o contrato sem fazer engenharia inversa do código.