Usas un VPN self-host. Te preguntas qué pasa si el túnel cae 12 segundos en medio de una subida sensible. En la mayoría de configs Linux por defecto: el tráfico pasa en claro por tu ruta por defecto, sin indicación visible. El DNS local se filtra, la IP real aparece en logs de servidores remotos. Un kill switch de aplicación no te salva — hay que bloquearlo a nivel kernel.
Esta guía monta un kill switch iptables + systemd que garantiza que nada sale de tu máquina en claro, incluso durante los 50 ms de cambio entre dos handshakes WireGuard.
El modelo que construimos
La idea central en 3 frases:
- Todo el tráfico OUTPUT está DROP por defecto salvo si sale por la interfaz VPN (
wg0). - Excepción: la conexión UDP hacia el servidor VPN (handshake) queda autorizada — sin esto el túnel no puede restablecerse tras una caída.
- systemd garantiza que esas reglas iptables se apliquen antes que WireGuard y sobrevivan al reinicio del túnel.
Resultado: si WireGuard muere, tu máquina literalmente no puede enviar nada salvo el handshake VPN. Ves "no connection" en navegador, pero ningún paquete se fuga.
Preparación
En Ubuntu/Debian (probado Ubuntu 22.04 y 24.04, kernel 5.15+):
sudo apt update
sudo apt install -y iptables iptables-persistent
# En prompt netfilter-persistent: YES para IPv4 y IPv6
Anota lo siguiente antes de continuar:
- La IP pública del VPN:
nslookup vpn.example.como mira enwg0.confla líneaEndpoint. - El puerto UDP del VPN: normalmente
51820para WireGuard. - La interfaz VPN:
wg0por defecto. - La subred LAN local a conservar accesible (192.168.1.0/24, 10.0.0.0/8, etc.) — para no romper impresora y NAS.
Reglas iptables IPv4
Crea /etc/iptables/rules.v4 con este contenido (adapta valores encajados):
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
# Loopback siempre permitido
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
# Conexiones ya establecidas (respuestas handshake VPN, etc.)
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# SSH entrante (si administras remoto — si no, quita estas 2 líneas)
-A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT
# Handshake VPN saliente: único tráfico autorizado fuera de túnel
-A OUTPUT -p udp -d 1.2.3.4 --dport 51820 -j ACCEPT
# DNS local en LAN (Pi-hole, router) — opcional
-A OUTPUT -d 192.168.1.0/24 -j ACCEPT
# Todo el resto del OUTPUT pasa por la interfaz VPN
-A OUTPUT -o wg0 -j ACCEPT
# Permitir paquetes entrantes en wg0
-A INPUT -i wg0 -j ACCEPT
COMMIT
Reemplaza:
1.2.3.4por la IP pública real de tu servidor VPN51820por tu puerto WireGuard192.168.1.0/24por tu subred LAN- Quita la línea
--dport 22si no necesitas admin SSH entrante
Carga las reglas:
sudo iptables-restore < /etc/iptables/rules.v4
sudo iptables -L -v -n
# Verifica que los counters están a 0 en las chains DROP
Reglas ip6tables (CRÍTICO)
Sin IPv6 explícitamente bloqueado, muchas distros activan IPv6 por defecto y el tráfico se fuga. Crea /etc/iptables/rules.v6:
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Si tu VPN soporta IPv6 (raro en self-host):
# -A OUTPUT -p udp -d 2001:db8::1 --dport 51820 -j ACCEPT
# -A OUTPUT -o wg0 -j ACCEPT
# -A INPUT -i wg0 -j ACCEPT
# Si no, todo IPv6 fuera de loopback es DROP
COMMIT
Carga:
sudo ip6tables-restore < /etc/iptables/rules.v6
A este punto puedes probar: sin el túnel WireGuard, ningún ping a Internet pasa. Es lo esperado.
Servicio systemd para ordenar WireGuard tras iptables
Problema por defecto: wg-quick@wg0 puede arrancar antes que netfilter-persistent. Durante esa ventana (~1-3 segundos), el túnel está UP pero las reglas iptables no cargadas → fuga posible.
Solución: override systemd que fuerza el orden.
Crea /etc/systemd/system/wg-quick@wg0.service.d/killswitch.conf:
sudo mkdir -p /etc/systemd/system/wg-quick@wg0.service.d
sudo nano /etc/systemd/system/wg-quick@wg0.service.d/killswitch.conf
Contenido:
[Unit]
After=netfilter-persistent.service
Wants=netfilter-persistent.service
Requires=netfilter-persistent.service
[Service]
# Si wg-quick crashea, forzamos OUTPUT DROP para evitar fuga
ExecStopPost=/sbin/iptables -P OUTPUT DROP
ExecStopPost=/sbin/ip6tables -P OUTPUT DROP
Recarga systemd:
sudo systemctl daemon-reload
sudo systemctl enable wg-quick@wg0
sudo systemctl enable netfilter-persistent
sudo systemctl restart netfilter-persistent
sudo systemctl restart wg-quick@wg0
En cada boot, systemd arranca netfilter-persistent primero (reglas cargadas), luego wg-quick@wg0 (túnel montado). Sin ventana de fuga.
Tests de validación
Test 1 — Corta el túnel y verifica silencio
# Túnel up
sudo wg show
# Debe mostrar handshake reciente
# Probar acceso Internet
curl -s -m 5 https://ifconfig.me
# Debe devolver IP del VPS
# Cortar túnel
sudo wg-quick down wg0
# Re-test
curl -s -m 5 https://ifconfig.me
# Debe timeout sin respuesta — KILL SWITCH ACTIVO
Si ves IP en segundo comando → fuga, reglas no aplicadas. Verifica sudo iptables -L -v -n y confirma que OUTPUT default es DROP.
Test 2 — Simula un crash WireGuard
sudo wg-quick up wg0
ping -c 2 1.1.1.1
# Debe ping OK
# Kill brutal del proceso WireGuard
sudo pkill -9 wg
sudo ip link delete wg0 2>/dev/null
# Re-test
ping -c 2 1.1.1.1
# Debe fallar "Operation not permitted"
El kernel rechaza el ping porque ninguna ruta queda autorizada. Sin ExecStopPost en el drop-in systemd, algunas distros dejan una ventana de fuga tras el crash.
Test 3 — DNS leak
# Túnel UP
dig @9.9.9.9 ifconfig.me +short
# Debe devolver IP del VPS
# Corta túnel
sudo wg-quick down wg0
# Re-test
dig @9.9.9.9 ifconfig.me +short
# Debe timeout / "no servers could be reached"
Hasta el DNS está bloqueado fuera del túnel. Objetivo cumplido.
Casos especiales
Usas varios túneles (wg0 + wg1)
Duplica la excepción OUTPUT:
-A OUTPUT -p udp -d 1.2.3.4 --dport 51820 -j ACCEPT
-A OUTPUT -p udp -d 5.6.7.8 --dport 51820 -j ACCEPT
-A OUTPUT -o wg0 -j ACCEPT
-A OUTPUT -o wg1 -j ACCEPT
Tailscale en paralelo
Tailscale usa UDP 41641 por defecto. Añade:
-A OUTPUT -p udp --dport 41641 -j ACCEPT
-A OUTPUT -o tailscale0 -j ACCEPT
Conservar LAN local (NAS, impresora)
La regla -A OUTPUT -d 192.168.1.0/24 -j ACCEPT del template ya lo permite. Ajusta subred.
Logging de paquetes bloqueados (debug)
Para ver qué se habría fugado sin el kill switch:
sudo iptables -I OUTPUT 1 -j LOG --log-prefix "OUTPUT-DROP: " --log-level 4
Luego:
sudo journalctl -k -f | grep "OUTPUT-DROP"
Verás todos los paquetes que tu OS intenta enviar en claro cuando el túnel está down. Educativo.
Tras debug, elimina la regla LOG:
sudo iptables -D OUTPUT -j LOG --log-prefix "OUTPUT-DROP: " --log-level 4
Bonus: kill switch en wg0.conf directamente
Si prefieres no gestionar iptables separadamente, WireGuard acepta PostUp/PreDown que hacen el mismo trabajo:
[Interface]
# ...
PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PostUp = ip6tables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = ip6tables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
Ventaja: cero fichero extra. Inconveniente: las reglas desaparecen si WireGuard crashea sin llamar PreDown (kernel panic, OOM kill). El approach systemd es más robusto para prod long-term.
Ver también el template "travel kill switch" de la guía WireGuard 2026.
Verificación final
# Reboot completo
sudo reboot
# Tras reinicio, sin tocar nada
ip a show wg0
# Debe mostrar túnel UP
iptables -L OUTPUT -v -n | head -5
# Default policy debe ser DROP, accept counters deben crecer
curl -s ifconfig.me
# Debe devolver IP del VPS
Todo verde: kill switch en su sitio, funcional al boot, sobrevive a crashes. Puedes conectar la máquina en red hostil sin riesgo de fuga.
Para profundizar
El kill switch corta las fugas. Pero para estar tranquilo de verdad, añade:
- Monitoring Prometheus + Grafana para tu VPN VPS — estado del túnel en tiempo real, débito, peers.
- Split-tunnel con tablas de rutas — útil si quieres que ciertas apps pasen en claro (descargas Steam, backup Time Machine en NAS local) manteniendo el resto tunelizado.
- Plantillas WireGuard listas para usar — 8 configuraciones probadas en prod.
Y la base: setup WireGuard sobre Contabo en 20 min.
★ Datacenter Nuremberg GDPR · ✓ IPv4 dédiée incluse · 200+ Mbps garantis
Probar Contabo30 jours satisfait ou remboursé→