Por que un hook de firewall de VPN engade a mesma regra dúas veces
Unha reconexión pode volver executar o hook, así que deseña para o estado desexado: cadeas propias, unha proba antes de inserir, un cambio serializado e un desmontaxe estreito.
O hook funciona a primeira vez, e tras tres reconexións a cadea contén tres copias da mesma regra. Nada fallou ruidosamente; o script simplemente fixo o que se lle dixo cada vez que se executou. Un hook de VPN é unha operación de configuración que pode executarse máis dunha vez, así que necesita un propietario, un estado desexado e un cambio serializado. Segue esa orde e os duplicados paran.
Dalle un propietario ás regras antes de escribir ningunha
Decide que cadea ou táboa posúe esta integración antes de escribir unha soa regra. Unha cadea adicada que o hook crea e baleira é a forma máis clara de propiedade, porque a limpeza pode despois eliminar esa cadea sen tocar a política de ninguén máis. Se debes engadir regras a unha cadea compartida, márcaas cun comentario para que unha busca posterior atope exactamente as túas. A propiedade é o que fai seguro o desmontaxe, e o desmontaxe é a parte que falla en silencio.
iptables -N VPN_HOOK 2>/dev/null || true
iptables -C INPUT -j VPN_HOOK 2>/dev/null || iptables -I INPUT -j VPN_HOOK
iptables -S VPN_HOOK
Un comentario aquí non é decoración. É a diferenza entre eliminar a túa regra e eliminar unha regra que casualmente parece similar, e sobrevive no conxunto de regras gardado. Escribe o comentario unha vez nunha variable e usa a mesma cadea en cada comando que engade, proba ou elimina.
Proba o estado desexado en lugar de engadir outra vez
Escribe que debe existir mentres o túnel está levantado: que interfaces, que orixe e destino, que protocolo e que porto. Despois expresa cada regra como unha proba seguida dunha inserción, para que unha segunda execución atope a regra e non faga nada. O comando de comprobación remata con cero cando a regra existe, e esa é exactamente a sinal que necesita o script. A referencia de iptables documenta as formas de comprobación, adición e inserción que se usan aquí.
iptables -C VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT ||
iptables -A VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT
Ten coidado coa posición. Comprobar unha regra e inserila ao principio pode mover un permiso diante dunha denegación deliberada que antes ía primeiro, así que a segunda execución cambia o comportamento aínda que non engada nada. Engade ao final dentro da túa propia cadea e controla a orde polo lugar onde está o salto a esa cadea. Se a orde importa dentro da cadea, reconstrúe a cadea a partir dunha lista coñecida en lugar de parcheala regra por regra.
Détente se a regra que necesitas non se pode expresar como unha proba estable. Iso normalmente significa que o estado desexado aínda é vago.
Serializa o cambio en lugar de esperar que aguante
Dúas invocacións do hook poden solaparse, e ambas poden ver unha regra ausente antes de que ningunha a engada. A opción de espera de xtables fai que un comando espere o bloqueo, pero non fai atómica unha secuencia de comprobar-e-engadir entre dous comandos. Serializa toda a operación co teu propio bloqueo, ou usa un xestor que aplique un lote declarado como unha soa transacción. nftables pode facer isto último, e é unha razón para preferilo en hosts onde está dispoñible.
flock /run/vpn-hook.lock /usr/local/sbin/vpn-firewall-apply
nft -f /etc/nftables/vpn-hook.nft
Sexa cal sexa o mecanismo que elixas, mantén a sección crítica curta e faina reentrante: un hook que falla a metade de camiño debería ser seguro de volver executar. Rexistra o resultado para que unha execución repetida sexa visible no journal en lugar de deducirse do reconto de regras. Se dous supervisores distintos poden invocar o hook, arranxa iso primeiro; ningún bloqueo arranxa dous propietarios.
Desmonta só o que é teu
O desmontaxe ten que distinguir unha conexión de substitución da que retirou, e ten que sobrevivir a un evento que nunca recibe unha limpeza ordenada. Elimina cos mesmos identificadores que usaches para engadir, e nunca baleires unha cadea compartida para simplificar a limpeza. Tras unha parada dura, executa unha pasada de conciliación no seguinte arranque en lugar de supor que o hook de baixada se executou.
iptables -D VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT
iptables -F VPN_HOOK
ip -brief link show type tun
A vida útil da interface tamén forma parte do deseño. Unha reconexión pode crear un dispositivo cun nome distinto, así que unha regra que fixa o nome do segundo túnel queda obsoleta mentres aínda parece válida. Prefire a identidade que expón a propia conexión e volve lela en cada execución. Proba os tres eventos por separado: unha conexión repetida, unha conexión de substitución e un apagado. O tráfico permitido debe sobrevivir aos tres sen duplicados, e os servizos non relacionados deben quedar intactos.
O percorrido remata cando o hook ten un propietario, unha proba para cada regra que engade, un camiño serializado e un desmontaxe que elimina só o seu propio estado. Un hook que é seguro de executar dúas veces vale máis que un hook que é enxeñoso unha vez.