Você adiciona uma regra para o tráfego VPN na cadeia FORWARD, executa a requisição a partir da mesma máquina Linux, e o contador fica em zero. A regra provavelmente está correta. O pacote nunca seguiu esse caminho, porque uma requisição gerada na própria máquina não é encaminhada por ela. Acompanhar a vida do pacote do socket até o cabo explica por quê, e onde a regra deveria ficar em vez disso.

Descubra onde o pacote nasceu

A ordem dos hooks do Netfilter separa três casos, e apenas um deles envolve FORWARD:

OriginPathHook that sees it
Local processOUTPUT then POSTROUTINGOUTPUT, POSTROUTING
Addressed to a local servicePREROUTING then INPUTPREROUTING, INPUT
Received for another hostPREROUTING, FORWARD, POSTROUTINGFORWARD

Uma aplicação local começa no caminho OUTPUT, porque o kernel já estabeleceu que o pacote se origina aqui e nunca é candidato a encaminhamento. Criar um túnel não muda essa classificação; o túnel é um dispositivo pelo qual o pacote pode sair, não uma etapa que converte a saída local em trânsito.

Dois pacotes, uma história

Depois que o túnel sobe, o pacote interno da aplicação e o pacote externo cifrado são objetos diferentes. O pacote interno carrega o destino e a carga que a aplicação escolheu. O pacote externo carrega texto cifrado para o endereço e a porta do par, e é ele que realmente cruza a interface física.

Sua política diz respeito a um deles, e escolher o errado é uma causa comum de uma regra parecer não fazer nada. Uma regra que corresponde ao endereço externo do par vê todos os pacotes do túnel igualmente, faça o que fizer a aplicação lá dentro. Uma regra que corresponde a um destino interno vê o tráfego que lhe interessa, e só disparará se esse pacote chegar ao hook onde você instalou a regra.

Namespaces movem a fronteira

Um contêiner torna isso mais difícil sem mudar o princípio. A requisição da aplicação é saída local dentro do namespace de rede do contêiner. O host recebe esse tráfego em uma interface Ethernet virtual e pode encaminhá-lo, então uma regra FORWARD no host pode fazer sentido mesmo que o processo que gerou a requisição nunca tenha tocado a cadeia OUTPUT do host.

Antes de escolher um hook, estabeleça onde realmente vivem o conjunto de regras e o túnel. Um túnel dentro do contêiner, um túnel no host com o contêiner roteado através dele e um túnel em um namespace separado são três topologias diferentes com três posicionamentos de regras diferentes. Leia as regras dentro do namespace que possui o dispositivo, não apenas no padrão.

Pergunte se você precisa mesmo de uma marca

Se o objetivo é enviar um prefixo de destino pelo túnel, uma rota para esse prefixo pode bastar. Verifique primeiro as rotas efetivas, incluindo tudo o que o servidor empurrou: uma rota padrão redirecionada envia muito mais tráfego pela VPN do que a política estreita que você tinha em mente, e nenhuma quantidade de marcação fará o resultado corresponder à intenção.

A classificação é necessária quando a decisão depende de algo que uma rota não consegue expressar, como a origem, o usuário ou o processo. Para o tráfego recebido, a marcação em PREROUTING funciona. Para o tráfego local, a marcação precisa ocorrer em um estágio de saída que consiga disparar uma nova consulta de rota depois que a marca é definida. Com nftables, isso é uma cadeia do tipo route no hook de saída; uma cadeia de filtro de saída comum definirá o valor e deixará a rota inalterada.

Com iptables, o equivalente vive na disposição mangle OUTPUT, e o mesmo requisito se aplica: a decisão de roteamento precisa ser revista depois que a marca existe. Em seguida, uma regra de política tem de consumir a marca e apontar para uma tabela que contenha uma rota utilizável. Definir uma marca de conntrack é uma terceira operação, separada; ela guarda um valor no fluxo e não coloca por si só esse valor no pacote atual para que o roteamento o leia.

Verifique a consulta e o contador juntos

nft list ruleset
ip route get 198.51.100.25
ip route get 198.51.100.25 mark 0x1

O endereço e a marca são ilustrativos, e as duas consultas de rota respondem a perguntas diferentes. A segunda pergunta o que o kernel faria se um pacote carregasse aquela marca; ela não mostra que seu conjunto de regras atribua uma. Trate-a como um teste de hipótese, não como evidência de que a política funciona.

Depois execute uma requisição inofensiva e observe duas coisas ao mesmo tempo: o contador no hook escolhido e a saída que o pacote realmente tomou. Se o contador se move e o pacote sai por outro lugar, a marca ou a tabela estão erradas. Se nenhum dos dois se move, o pacote não chega a esse hook de jeito nenhum, e a próxima pergunta é o namespace ou o caminho.

Por fim, limite o teste ao que o mecanismo consegue expressar. Uma rota para um endereço não se torna uma política permanente para um serviço cujos endereços rodam. Decida primeiro o escopo dos pacotes e depois verifique se o desenho de roteamento e marcação consegue manter esse escopo após uma reconexão.

Artigos relacionados