VPNSmith
self-host-vpnINFO

Interruptor de segurança VPN no Linux com iptables e systemd (2026)

Interruptor de segurança Netfilter que bloqueia todo o tráfego WireGuard fora do túnel. Serviço systemd ordenado, exceção para interface VPN, teste de reconexão. Garantia de zero vazamento.

Por Eric Gerard · Fondateur · VPNSmith — Spécialiste self-host VPN & VPS GDPR10 min de leituraPhoto via Unsplash

Usas uma VPN auto-hospedada. Perguntas-te o que acontece se o túnel cair durante 12 segundos a meio de um upload de algo sensível. Na maioria das configurações padrão do Linux: o tráfego flui livremente pela tua rota padrão, sem qualquer indicação visível. Vazamentos de DNS local, IP real aparece nos logs do servidor remoto. Um interruptor de segurança a nível de aplicação não te salvará — isto deve ser bloqueado ao nível do kernel.

Este guia configura um interruptor de segurança iptables + systemd que garante que nada sai da tua máquina em claro, mesmo durante o intervalo de 50 ms entre dois handshakes WireGuard.

Por que um interruptor de segurança netfilter é a única garantia séria

A indústria de VPNs comerciais fala sobre "interruptores de segurança" há sete anos, mas a maioria das implementações é frágil porque operam ao nível da aplicação: a aplicação da NordVPN ou ExpressVPN detecta uma queda no túnel e corta o tráfego manipulando a tabela de rotas. O problema: durante os 200 a 800 milissegundos que a aplicação leva para detectar a queda e reagir, os pacotes saem em claro pela interface padrão. Isso é suficiente para uma conexão BitTorrent ativa expor o teu IP real ao tracker, ou para um upload multipart S3 em progresso revelar a tua origem geográfica.

O interruptor de segurança netfilter (iptables ou nftables) contorna este problema colocando a regra de filtragem no próprio kernel do Linux, não num processo em espaço de utilizador. Os pacotes atravessam o subsistema netfilter em cada syscall send/sendmsg/sendto — não há janela de latência da aplicação. Se o WireGuard falhar, os pacotes continuam a ser DROP-ed pelo netfilter até que o túnel volte, independentemente do estado do processo WireGuard. É por isso que a Mullvad recomenda o netfilter como uma segunda camada além da sua aplicação, e por que os guias do Projeto Tor mencionam explicitamente o interruptor de segurança netfilter para hosts que executam relays.

A nossa escolha por iptables em vez de nftables deve-se ao suporte: o Ubuntu Server 22.04 LTS e o Debian 12 ainda têm o iptables como ferramenta de facto, mesmo que o backend seja nftables por baixo. Os comandos iptables são copiáveis de qualquer documentação dos últimos 10 anos, o que reduz o risco de erros de digitação e facilita a manutenção. Se estás a começar do zero em 2026 num novo host e conheces nftables, vai diretamente com nftables — a sintaxe é mais limpa e a ordem das cadeias é explícita.

O modelo que construímos

A ideia central em 3 frases:

  1. Todo o tráfego OUTPUT é DROP por padrão a menos que saia pela interface VPN (wg0).
  2. Exceção: a conexão UDP com o servidor VPN (handshake) permanece permitida — sem ela, o túnel não pode ser restabelecido após uma queda.
  3. systemd garante que essas regras de iptables se aplicam antes do WireGuard e sobrevivem a reinícios do túnel.

Resultado: se o WireGuard falhar, a tua máquina literalmente não pode enviar nada exceto o handshake VPN. Vês "sem conexão" no navegador, mas nenhum pacote vaza.

Preparação

No Ubuntu/Debian (testado no Ubuntu 22.04 e 24.04, kernel 5.15+):

sudo apt update
sudo apt install -y iptables iptables-persistent
# No prompt do netfilter-persistent: SIM para IPv4 e IPv6

Nota estes itens antes de continuar:

  • O IP público da VPN: nslookup vpn.example.com ou verifica a linha Endpoint em wg0.conf.
  • A porta UDP da VPN: geralmente 51820 para WireGuard.
  • A interface VPN: wg0 por padrão, pode ser wg1 com múltiplos túneis.
  • A sub-rede LAN local a manter acessível (192.168.1.0/24, 10.0.0.0/8, etc.) — para que a impressora e o NAS não deixem de funcionar.

Regras iptables para IPv4

Cria /etc/iptables/rules.v4 com este conteúdo (adapta os valores entre colchetes):

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]

# Loopback sempre permitido
-A INPUT  -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT

# Conexões já estabelecidas (respostas de handshake VPN, etc.)
-A INPUT  -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# SSH de entrada (se administras remotamente — caso contrário, remove estas 2 linhas)
-A INPUT  -p tcp --dport 22 -m state --state NEW -j ACCEPT

# Handshake VPN de saída: apenas tráfego fora do túnel permitido
-A OUTPUT -p udp -d 1.2.3.4 --dport 51820 -j ACCEPT

# DNS LAN local (Pi-hole, router) — opcional
-A OUTPUT -d 192.168.1.0/24 -j ACCEPT

# Todo o resto OUTPUT vai via interface VPN
-A OUTPUT -o wg0 -j ACCEPT

# Permitir entrada em wg0
-A INPUT  -i wg0 -j ACCEPT

COMMIT

Substitui:

  • 1.2.3.4 pelo IP público real do teu servidor VPN
  • 51820 pela tua porta WireGuard
  • 192.168.1.0/24 pela tua sub-rede LAN
  • Remove a linha --dport 22 se não precisas de administração SSH de entrada

Carrega as regras:

sudo iptables-restore < /etc/iptables/rules.v4
sudo iptables -L -v -n
# Confirma que os contadores da cadeia DROP estão a 0

Regras ip6tables (CRÍTICO)

Sem o bloqueio explícito de IPv6, muitas distribuições ativam-no por padrão e o tráfego vaza. Cria /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

# Se a tua VPN suporta IPv6 (raro em auto-hospedadas):
# -A OUTPUT -p udp -d 2001:db8::1 --dport 51820 -j ACCEPT
# -A OUTPUT -o wg0 -j ACCEPT
# -A INPUT  -i wg0 -j ACCEPT

# Caso contrário, todo o IPv6 fora do loopback é DROP

COMMIT

Carrega:

sudo ip6tables-restore < /etc/iptables/rules.v6

Neste ponto, podes testar: sem o túnel WireGuard, nenhum ping para a Internet passa. Esperado.

Ordenação do serviço systemd para WireGuard após iptables

Racks de servidores iluminados em azul num centro de dados
Racks de servidores iluminados em azul num centro de dados

Problema padrão: wg-quick@wg0 pode iniciar antes de netfilter-persistent. Durante essa janela (~1-3 segundos), o túnel está ATIVO, mas as regras iptables não estão carregadas → possível vazamento.

Solução: substituição systemd forçando a ordem.

Cria /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

Conteúdo:

[Unit]
After=netfilter-persistent.service
Wants=netfilter-persistent.service
Requires=netfilter-persistent.service

[Service]
# Se wg-quick falhar, força OUTPUT DROP para prevenir qualquer vazamento
ExecStopPost=/sbin/iptables -P OUTPUT DROP
ExecStopPost=/sbin/ip6tables -P OUTPUT DROP

Recarrega o 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

A cada arranque, o systemd inicia netfilter-persistent primeiro (iptables carregado), depois wg-quick@wg0 (túnel ativo). Sem janela de vazamento.

Testes de validação

Teste 1 — Derruba o túnel, verifica o silêncio

# Túnel ativo
sudo wg show
# Deve mostrar um handshake recente

# Testa o acesso à Internet
curl -s -m 5 https://ifconfig.me
# Deve retornar o IP do VPS

# Derruba o túnel
sudo wg-quick down wg0

# Re-testa
curl -s -m 5 https://ifconfig.me
# Deve dar timeout sem resposta — INTERRUPTOR DE SEGURANÇA ATIVO

Se vires um IP no segundo comando → vazamento, as regras iptables não estão aplicadas. Verifica sudo iptables -L -v -n e confirma que o OUTPUT padrão é DROP.

Teste 2 — Simula uma falha do WireGuard

# Túnel ativo
sudo wg-quick up wg0
ping -c 2 1.1.1.1
# Deve pingar OK

# Morte brutal do processo WireGuard (simula falha)
sudo pkill -9 wg
sudo ip link delete wg0 2>/dev/null

# Re-testa
ping -c 2 1.1.1.1
# Deve falhar "Operação não permitida"

O kernel recusa o ping porque nenhuma rota permanece permitida. Sem ExecStopPost na substituição systemd, algumas distribuições deixam uma janela de vazamento após a falha — essa linha é importante.

Teste 3 — Vazamento de DNS

# Túnel ATIVO
dig @9.9.9.9 ifconfig.me +short
# Deve retornar o IP do VPS

# Derruba o túnel
sudo wg-quick down wg0

# Re-testa
dig @9.9.9.9 ifconfig.me +short
# Deve dar timeout / "nenhum servidor pôde ser alcançado"

Mesmo o DNS é bloqueado fora do túnel. Objetivo alcançado.

Casos especiais

Corres vários túneis (wg0 + wg1)

Duplica a exceção OUTPUT:

-A OUTPUT -p udp -d 1.2.3.4 --dport 51820 -j ACCEPT  # túnel principal
-A OUTPUT -p udp -d 5.6.7.8 --dport 51820 -j ACCEPT  # túnel secundário
-A OUTPUT -o wg0 -j ACCEPT
-A OUTPUT -o wg1 -j ACCEPT

Executa Tailscale em paralelo

O Tailscale usa UDP 41641 por padrão. Adiciona:

-A OUTPUT -p udp --dport 41641 -j ACCEPT
-A OUTPUT -o tailscale0 -j ACCEPT

Mantém a LAN local acessível (NAS, impressora)

A regra de modelo -A OUTPUT -d 192.168.1.0/24 -j ACCEPT já faz isso. Adapta a sub-rede.

Não quer SSH de entrada (estação de trabalho móvel)

Remove a linha --dport 22 -m state --state NEW -j ACCEPT. A conexão existente (ESTABLISHED) permanece OK.

Registo de pacotes descartados (debug)

Para ver o que teria vazado sem o interruptor de segurança:

sudo iptables -I OUTPUT 1 -j LOG --log-prefix "OUTPUT-DROP: " --log-level 4

Depois:

sudo journalctl -k -f | grep "OUTPUT-DROP"

Verás cada pacote que o teu SO tenta enviar em claro quando o túnel está inativo (DNS da Apple, NTP, batimento cardíaco do Spotify, etc.). Educativo.

Depois de terminar o debug, remove a regra LOG (senão journalctl enche-se):

sudo iptables -D OUTPUT -j LOG --log-prefix "OUTPUT-DROP: " --log-level 4

Bónus: interruptor de segurança diretamente dentro de wg0.conf

Se preferires não gerir iptables separadamente, o WireGuard aceita PostUp/PreDown fazendo o mesmo trabalho:

[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

Pró: zero ficheiros extra. Contra: as regras desaparecem se o WireGuard falhar sem chamar PreDown (pânico do kernel, OOM kill). A abordagem systemd acima é mais robusta para produção a longo prazo.

Veja também o modelo de interruptor de segurança de viagem no guia WireGuard 2026 para a versão inline-wg0.conf.

Verificação final

# Reboot completo
sudo reboot

# Após o reboot, sem passos manuais
ip a show wg0
# Deve mostrar túnel ATIVO

iptables -L OUTPUT -v -n | head -5
# A política padrão deve ser DROP, os contadores de aceitação devem crescer

curl -s ifconfig.me
# Deve retornar o IP do VPS

Tudo verde: o teu interruptor de segurança está em funcionamento, inicia no arranque, sobrevive a falhas. Podes ligar a máquina a uma rede hostil sem risco de vazamento.

Indo mais além

O interruptor de segurança bloqueia vazamentos. Mas para verdadeira paz de espírito, adiciona:

E a base: Configuração WireGuard na Contabo em 20 minutos.

★ Datacenter GDPR em Nuremberg · ✓ IPv4 dedicado incluído · 200+ Mbps garantidos

Self-host your VPN on your own VPS → ContaboFull root access · public IPv4 · pick your region