Duas máquinas virtuais no mesmo host chegam à LAN através de uma ponte, e uma regra colocada na cadeia ip forward mostra um contador que nunca se move. Os quadros são reais, o tráfego passa e a regra simplesmente não está no seu caminho. Comparar as duas formas como o Linux expõe o tráfego em ponte explica este silêncio, e também explica porque é que carregar um módulo antigo é uma resposta pior do que parece.

Dois caminhos através de um só host

Um pacote pode sair deste host de duas formas muito diferentes, e a diferença decide que hooks o veem. Se o destino está para lá de uma porta física e o quadro é comutado entre duas portas da ponte, o host reencaminha um quadro Ethernet; o código de encaminhamento não é consultado, e não ocorre nenhum salto encaminhado. Se o destino é o próprio host, ou uma rede alcançada por uma interface de túnel, o pacote é encaminhado e os hooks da camada IP aplicam-se de facto. Tenha esta bifurcação presente, porque cada pergunta posterior sobre cadeias e famílias decorre de qual dos dois caminhos um quadro toma.

Essa distinção é toda a história por trás de uma regra que não conta nada. Um quadro em ponte de veth9a1 para enp3s0 atravessa a ponte, não a pilha IP, por isso uma cadeia ip forward não vê nada para corresponder. Os nomes das interfaces num exemplo mais antigo não determinam que caminho tem, e por isso o primeiro passo é olhar para a topologia e não para o conjunto de regras.

Um túnel acrescenta um terceiro caso que merece um nome. Um dispositivo TUN entrega pacotes IP ao host, por isso são encaminhados e atravessam de facto cadeias IP, enquanto um dispositivo TAP ligado a uma ponte entrega quadros e comporta-se como o caso da ponte. É o tipo de dispositivo, não o nome do produto, que decide.

Compare as duas famílias que conseguem ver um quadro em ponte

O Linux suporta filtragem de ponte nativa através da família bridge do nftables, e não precisa de qualquer módulo antigo para inspecionar e descartar quadros em voo. A documentação da ponte do próprio kernel faz essa recomendação diretamente, o que vale a pena saber antes de um resultado de pesquisa lhe dizer para carregar um módulo.

Observation pointWhat it seesWhat it costs
nftables bridge family chainsEthernet frames forwarded between bridge ports, in both directionsRules must be written in bridge syntax with link-layer matches
Legacy br_netfilter plus an ip chainBridged IPv4 and IPv6 packets, re-exposed through IP-layer hooksExisting host rules may begin matching traffic they never saw before
inet chain for routed trafficPackets the host routes, including traffic arriving from a TUN deviceNo visibility into purely switched frames

Usar o comando nft em vez do iptables não coloca, por si só, uma regra na família correta. Uma cadeia declarada na família bridge vê quadros; uma cadeia declarada em ip, ip6 ou inet não os vê, independentemente do binário que a criou. A família é uma propriedade da cadeia, não da ferramenta que a escreveu.

O caminho antigo funciona de forma diferente, e é por isso que vale a pena comparar em vez de escolher um lado por hábito. Carregar o br_netfilter e ativar as definições bridge-nf-call volta a expor o tráfego IPv4 e IPv6 em ponte através dos hooks da camada IP, para que uma regra normal de iptables finalmente possa corresponder-lhe. Os quadros não mudam; o conjunto de hooks por que passam muda, o que significa que cada regra de host existente tem agora um novo público.

Leia o caminho efetivo antes de acrescentar uma regra

Três comandos estabelecem onde está: a que está ligada a ponte, se o módulo antigo está ativo e que cadeias já existem na família bridge. Execute-os antes de escrever o que quer que seja, porque a resposta decide qual dos dois desenhos está a ampliar. Saltar este passo é a forma como uma regra correta acaba numa cadeia que nada atravessa.

bridge link show
lsmod | grep br_netfilter
nft list ruleset | grep -A4 "chain forward"

A saída separa os casos com clareza. As portas listadas sob a ponte dizem-lhe que interfaces são comutadas; um resultado vazio de lsmod significa nenhuma visibilidade na camada IP dos quadros em ponte; uma cadeia forward existente da família bridge significa que já existe um dono da política e que a sua regra pertence ali e não a uma nova cadeia própria.

Depois acrescente uma regra fácil de reler, com um contador e sem mudar o veredicto, e faça passar um único fluxo representativo pela ponte. Um contador que se move prova que o quadro chegou à regra. É tudo o que prova, por isso siga também com o caminho negado, uma vez que uma política que aceita tudo também produz um contador em movimento.

nft add rule bridge filter forward iifname "veth9a1" oifname "enp3s0" counter accept

Onde o módulo antigo muda o comportamento

Ativar o caminho antigo numa ponte em funcionamento não é um ato neutro de acrescentar visibilidade. As regras que antes eram inalcançáveis para o tráfego em ponte tornam-se alcançáveis, incluindo regras de host escritas para outra interface, e uma política drop predefinida na cadeia ip forward pode passar a aplicar-se de repente a quadros que antes passavam intactos. Num host gerido através dessa ponte, o resultado é um bloqueio sem consola. Este não é um modo de falha hipotético; é a consequência comum de alargar um conjunto de hooks numa máquina a que chega pelo caminho que acabou de alargar.

A sequência mais segura é decidir qual família detém a política, escrever aí as regras e manter um caminho de recuperação independente antes de alterar qualquer sysctl. Se duas famílias têm regras, documente qual é a autoridade; duas políticas sobrepostas são a forma como um pacote é aceite num sítio e descartado noutro sem um único ficheiro para ler.

O isolamento em si ainda precisa de um desenho completo. Permitir uma origem para um destino prova apenas que um fluxo funciona, quando o requisito costuma ser que uma VM não consiga alcançar os vizinhos ou o host. Os contadores das regras de negação importam mais do que o contador da regra de aceitação, porque o contador de aceitação é o que já esperava que se movesse.

Por fim, mantenha o caso da entrada do host separado. O tráfego dirigido ao próprio IP da ponte chega a input, não a forward, e uma regra em forward nunca o verá, escolha a família que escolher. Três casos, duas famílias, uma lição: o caminho decide o hook, e o hook decide se a sua regra existe sequer.

Artigos relacionados