O pedido é sempre uma variação da mesma frase: mantenha a VPN para navegar, mas deixe o SSH ou uma tarefa de backup usar a conexão normal. O conselho que circula para isso, encaminhar a porta com regras NAT do iptables, não faz isso. Uma regra NAT reescreve endereços e portas dentro de um fluxo; ela nunca escolhe por qual interface o pacote sai. Essa decisão pertence à consulta de rota, e a consulta de rota precisa de ajuda para enxergar uma porta.

Por que uma rota simples não consegue casar com uma porta

O Linux escolhe uma rota consultando o endereço de destino numa tabela, depois de filtrar as tabelas candidatas pelas regras de política. Essas regras podem casar com uma marca de firewall, um endereço de origem, uma interface de entrada ou um UID. A entrada de rota em si não tem campo de porta, porque a rota é escolhida antes de o cabeçalho de transporte importar.

O iproute2 atual e os kernels ampliam o seletor de regras: ipproto, sport e dport são aceitos, então ip rule add ipproto tcp dport 22 table 100 expressa a intenção numa linha. Execute ip rule add help no host real para ver se sua compilação lista dport. Se listar, você termina com um comando; se não, o caminho por marca abaixo é o equivalente.

De todo modo, a regra é só um ponteiro. A tabela 100 ainda precisa de uma rota, e essa é a parte que as pessoas esquecem.

Três peças, ou uma

A forma moderna é a regra única acima mais uma rota na tabela alternativa. A forma clássica põe o netfilter no meio: marque o pacote e depois case a marca numa regra. As duas terminam na mesma consulta; diferem em onde a porta é lida.

A abordagem por marca existe porque o netfilter vê o cabeçalho de transporte e o código de roteamento historicamente não via. Esse é todo o mecanismo, e explica por que o recurso de reserva são três peças cooperando em vez de um comando esperto.

As três peças em ordem

Primeiro, marque os fluxos que quer desviar, numa cadeia que rode cedo o bastante para a marca existir antes da decisão de rota:

nft add table inet vpnexempt
nft 'add chain inet vpnexempt out { type route hook output priority mangle; }'
nft add rule inet vpnexempt out tcp dport 22 meta mark set 0x1

Segundo, envie os pacotes marcados para uma tabela separada:

ip rule add fwmark 0x1/0x1 table 100

Terceiro, dê a essa tabela uma rota padrão pelo link físico. Os endereços são ilustrativos; substitua pelos seus:

ip route add default via 192.168.1.1 dev wlan0 table 100

Pule o terceiro passo e o pacote marcado não encontra rota na tabela 100, então falha. O sintoma parece que a regra quebrou a rede, quando na verdade ela removeu o único caminho que restava ao pacote.

O que mantém isso funcionando depois do primeiro pacote

As marcas valem para pacotes de saída. A resposta chega na interface física com o endereço que o link atribuiu, e a tabela principal pode ainda preferir o túnel para esse destino. Dois mecanismos mantêm o caminho de volta coerente: uma regra baseada na origem para o endereço do link, e o connection tracking, que preserva a decisão durante toda a vida do fluxo. Guardar a marca no conntrack e restaurá-la na resposta é o jeito habitual de tornar estável uma exceção com estado.

A filtragem de caminho reverso é o outro assassino silencioso. Se o kernel valida a rota de retorno com rigor e a resposta chega por uma interface que a tabela principal não escolheria, o pacote é descartado sem mensagem. rp_filter os padrões variam por distribuição e por a interface ser encaminhada, então leia o valor efetivo em vez de supor que está desligado.

Não marque o próprio transporte do túnel. Um túnel WireGuard ou UDP OpenVPN não é TCP 22, então a receita acima é segura, mas uma exceção escrita como tudo exceto o destino do túnel pode capturar os pacotes criptografados e quebrar o túnel de um jeito que parece um laço de roteamento.

Verifique em vez de supor

Pergunte ao kernel qual caminho ele tomaria, para o destino e para a porta, e então faça a transferência real:

ip route get 203.0.113.10
ip rule show
nft list ruleset

Se a marca existe mas os contadores nunca se movem, a regra não está casando. Contadores são a diferença entre uma configuração que você escreveu e uma política que o kernel aplica.

Quando esta é a ferramenta errada

Exceções por porta são frágeis por natureza. Serviços mudam de porta, o HTTP/3 chega como UDP 443, e um aplicativo pode abrir vários fluxos de tipos diferentes. Se o requisito real é que este programa use o link normal, a camada de roteamento é o lugar errado; um namespace de rede ou um proxy por aplicativo se encaixa melhor.

Se o requisito é que esta sub-rede use o link normal, pule as marcas por completo e acrescente uma rota mais específica. É mais simples, fica visível numa tabela, e a próxima pessoa pode auditá-la sem ler um conjunto de regras.

Anote qual dos três requisitos você realmente tem antes de mexer no firewall. O mecanismo acima é preciso, e precisão aplicada à camada errada é só uma queda mais longa.

Artigos relacionados