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 failingWhat the client seesWhat stays behind
Created a firewall entryA rejection, with no tunnelA rule matching an address nobody owns
Wrote a state rowA rejection, with no tunnelA record of a session that never started
Allocated a tunnel addressA rejection, with no tunnelAn 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.

Artigos relacionados