Fallo de client-connect en OpenVPN: está garantida a limpeza ao desconectar?
Un cliente rexeitado non desfai unha regra de devasas nin un rexistro escrito. Compare o contrato da devolución de chamada coa reversión que o seu hook debe facer por si mesmo.
Cando un script client-connect sae cun código distinto de cero, OpenVPN rexeita o cliente, e os operadores adoitan supor que o script client-disconnect correspondente o ordena todo. A miúdo nunca se executa, porque o procesamento de disconnect depende do que a fase connect conseguiu completar. Tratar o rexeitamento e a reversión como un único mecanismo é o que converte un inicio de sesión fallido nunha regra de devasas filtrada ou nun rexistro orfo. Son dúas obrigas separadas, e só unha delas é automática. Lea a ruta de saída propia do hook antes de culpar o servidor por deixar estado atrás.
Unha saída distinta de cero rexeita o cliente; non desfai o seu traballo
O estado de saída de client-connect é unha decisión de admisión. Cero significa admitir; calquera outra cousa significa derrubar a instancia antes de que estea plenamente establecida. Nada nese contrato inspecciona o que o script xa fixo no mundo exterior. Se o script engadiu unha regra de iptables, escribiu unha fila ou incrementou un contador, eses cambios sobreviven ao rexeitamento.
A mesma asimetría se aplica aos complementos, onde varias funcións connect poden executarse para unha soa instancia. Un fallo posterior non cancela retroactivamente un éxito anterior, e a función anterior non ten forma de saber que unha función irmá fallou.
O rexeitamento é automático, a limpeza non
O manual de OpenVPN 2.6 vincula o procesamento de client-disconnect co éxito do procesamento de connect anterior. Unha excepción documentada cobre as cadeas en cascada: se un paso connect xa devolveu éxito, os pasos disconnect correspondentes execútanse igualmente cando se derruba a instancia, aínda que un paso connect posterior fallase. Esa excepción é estreita e depende de cantas devolucións de chamada estean configuradas, por iso probar un só script de shell non lle di nada sobre unha cadea.
Trazar a configuración real antes de deseñar arredor dela: que script, que complemento e en que orde se executan. A táboa seguinte enumera o que deixa atrás unha configuración a medio terminar.
| What the hook did before failing | What the client sees | What stays behind |
|---|---|---|
| Created a firewall entry | A rejection, with no tunnel | A rule matching an address nobody owns |
| Wrote a state row | A rejection, with no tunnel | A record of a session that never started |
| Allocated a tunnel address | A rejection, with no tunnel | An address marked busy until something releases it |
Cada fila termina igual para o cliente e de forma distinta para o host. O cliente non pode saber cal delas ocorreu, así que o hook debe desfacer o que fixo antes de devolver o estado de fallo.
Compense dentro do script que fallou
Valide todo o que poida antes de tocar estado compartido: campos do certificado, dispoñibilidade do enderezo, o grupo ao que pertence o cliente. O rexeitamento máis barato é o que ocorre antes de calquera asignación. Unha vez que a configuración abrangue varias operacións externas, leve un rexistro de cales se completaron e desfaga as en orde inversa antes de devolver o estado de fallo.
Un caso concreto: o script crea unha entrada de devasas por cliente e logo falla ao escribir a fila de estado que a describe. A rama que falla debería eliminar a entrada que acaba de crear e despois saír cun código distinto de cero. Esperar un evento disconnect que quizais nunca se programe é como unha regra sobrevive á sesión para a que estaba pensada.
Faga a compensación idempotente e con propietario
A limpeza debe ser segura de executar dúas veces e precisa sobre que pode borrar. Un evento tardío dun intento anterior non debería eliminar a asignación dunha sesión máis nova só porque ambas comparten un nome común ou un enderezo de túnel reciclado. Rexistre o identificador ou o id de operación concreto xunto co recurso, e borre por esa clave en lugar de polo nome. As transaccións de base de datos só axudan cos cambios propios da base de datos; unha regra de devasas fóra dese límite necesita un paso de compensación explícito.
Supoña que o proceso pode morrer entre o cambio e a limpeza, e engada un reconciliador que compare o estado externo coa lista de clientes admitidos segundo un calendario.
Probe os tres camiños que realmente difiren
Practique tres casos: un rexeitamento antes de que se asigne nada, un despois dunha asignación parcial e unha sesión admitida normal que logo remata. Eses tres producen liñas de rexistro distintas e residuos distintos. Verifique o estado externo despois de cada un, non só a mensaxe de fallo do cliente, porque o fallo do cliente é o resultado esperado en dous dos tres.
Rexistre a fase do hook, o estado de saída e que se asignou ou liberou, sen as credenciais. Un rexistro de hook que nomea o recurso que creou é o que fai que unha regra filtrada sexa localizable seis meses despois.