Conntrack, falhou ao NAT seus próprios pacotes TCP de outro VRF

Conntrack, falhou ao NAT seus próprios pacotes TCP de outro VRF

Me deparei com um problema complicado com o NAT de origem ao usar vários VRF em um roteador baseado em Debian. É um pouco complexo de explicar, então tentarei ser claro, mas não será curto, desculpe por isso. O problema deve ser fácil de reproduzir.

Para isolar a parte de "gerenciamento" do roteador (ssh e outros serviços) de seu trabalho de roteador (roteamento e pacotes NATing), tentei configurar o VRF "mgmt" no VRF padrão (mais fácil de lidar com soquetes de serviços) e o roteamento em um VRF chamado "firewall".

O diagrama pode ser resumido assim:

Diagrama de rede

A rede de "gerenciamento" é 192.168.100.0/24, e é roteada por um switch L3 que possui um L3 com o VRF "firewall" do roteador através da rede 10.254.5.0/24. A terceira interface do roteador é a da "internet", e os pacotes que passam por ela são de origem NAT. Esta configuração funciona muito bem para tudo na sub-rede mgmt, exceto os próprios pacotes do roteador, por causa do conntrack.

Sobre regras do iptables:

# Table filter

# chain INPUT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
(some INPUT rules, for ssh, snmp, etc)
-A INPUT -j DROP

# chain FORWARD
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -m conntrack --ctstate INVALID -j DROP
-A FORWARD -o eth2 -j ACCEPT
-A FORWARD -j DROP

# Table nat

# chain POSTROUTING
-A POSTROUTING -o eth2 -j SNAT --to-source 192.168.122.100

Sobre a tabela de roteamento:

# default VRF
default via 192.168.100.1 dev eth0 proto static metric 20 
192.168.100.0/24 dev eth0 proto kernel scope link src 192.168.100.90

# firewall VRF
default via 192.168.122.1 dev eth2 proto static metric 20
10.254.5.0/24 dev eth1 proto kernel scope link src 10.254.5.2
192.168.100.0/24 proto bgp metric 20 nexthop via 10.254.5.10 dev eth1 weight 1 
192.168.122.0/24 dev eth2 proto kernel scope link src 192.168.122.100 

Assim, quando um pacote do VRF padrão tenta acessar a internet, ele sai da eth0, é roteado pelo switch L3, entra no firewall VRF pela eth1 e é roteado e NAT pela eth2. Como eu acompanho as conexões INPUT e FORWARD, o conntrack fica um pouco confuso quando o pacote volta e não consegue saber o que fazer com o pacote.

Consegui corrigir isso para ICMP e UDP usando a zona conntrack na tabela bruta

# Table raw
# chain PREROUTING
-A PREROUTING -i eth0 -j CT --zone 5
# chain OUTPUT
-A OUTPUT -o eth0 -j CT --zone 5

Com essas regras, os pacotes que se originam do roteador e passam eth0são marcados zone 5e quando os pacotes entram eth0eles também são marcados zone 5.

Com um ping para 8.8.8.8, fica assim (com o comando conntrack -E):

    [NEW] icmp     1 30 src=192.168.100.90 dst=8.8.8.8 type=8 code=0 id=1999 [UNREPLIED] src=8.8.8.8 dst=192.168.100.90 type=0 code=0 id=1999 zone=5
    [NEW] icmp     1 30 src=192.168.100.90 dst=8.8.8.8 type=8 code=0 id=1999 [UNREPLIED] src=8.8.8.8 dst=192.168.122.100 type=0 code=0 id=1999
 [UPDATE] icmp     1 30 src=192.168.100.90 dst=8.8.8.8 type=8 code=0 id=1999 src=8.8.8.8 dst=192.168.122.100 type=0 code=0 id=1999
 [UPDATE] icmp     1 30 src=192.168.100.90 dst=8.8.8.8 type=8 code=0 id=1999 src=8.8.8.8 dst=192.168.100.90 type=0 code=0 id=1999 zone=5

Podemos ver aqui que a primeira NEWconexão é criada quando o pacote passa eth0com a zone=5tag, e uma nova quando entra no firewall VRF eth1sem a tag. Quando a resposta chega, a segunda conexão é atualizada primeiro (já que é a que está voltada para a internet) e depois a primeira.

Isso também funciona com UDP, por exemplo, com uma consulta DNS para 8.8.8.8

    [NEW] udp      17 30 src=192.168.100.90 dst=8.8.8.8 sport=53369 dport=53 [UNREPLIED] src=8.8.8.8 dst=192.168.100.90 sport=53 dport=53369 zone=5
    [NEW] udp      17 30 src=192.168.100.90 dst=8.8.8.8 sport=53369 dport=53 [UNREPLIED] src=8.8.8.8 dst=192.168.122.100 sport=53 dport=53369
 [UPDATE] udp      17 30 src=192.168.100.90 dst=8.8.8.8 sport=53369 dport=53 src=8.8.8.8 dst=192.168.122.100 sport=53 dport=53369
 [UPDATE] udp      17 30 src=192.168.100.90 dst=8.8.8.8 sport=53369 dport=53 src=8.8.8.8 dst=192.168.100.90 sport=53 dport=53369 zone=5

Mas com TCP isso não funciona. Uma consulta telnet para 172.16.10.10 porta 80 se parece com isto:

    [NEW] tcp      6 120 SYN_SENT src=192.168.100.90 dst=172.16.10.10 sport=60234 dport=80 [UNREPLIED] src=172.16.10.10 dst=192.168.100.90 sport=80 dport=60234 zone=5
    [NEW] tcp      6 120 SYN_SENT src=192.168.100.90 dst=172.16.10.10 sport=60234 dport=80 [UNREPLIED] src=172.16.10.10 dst=192.168.122.100 sport=80 dport=60234
 [UPDATE] tcp      6 58 SYN_RECV src=192.168.100.90 dst=172.16.10.10 sport=60234 dport=80 src=172.16.10.10 dst=192.168.122.100 sport=80 dport=60234
 [UPDATE] tcp      6 57 SYN_RECV src=192.168.100.90 dst=172.16.10.10 sport=60234 dport=80 src=172.16.10.10 dst=192.168.122.100 sport=80 dport=60234
(The last line repeat multiple times)

Se eu tcpdump eth2a resposta lá:

IP 192.168.122.100.60236 > 172.16.10.10.80: Flags [S], seq 4203590660, win 62720, options [mss 1460,sackOK,TS val 1511828881 ecr 0,nop,wscale 7], length 0
IP 172.16.10.10.80 > 192.168.122.100.60236: Flags [S.], seq 3672808466, ack 4203590661, win 65535, options [mss 1430,sackOK,TS val 2474659117 ecr 1511828881,nop,wscale 8], length 0
IP 192.168.122.100.60236 > 172.16.10.10.80: Flags [S], seq 4203590660, win 62720, options [mss 1460,sackOK,TS val 1511829887 ecr 0,nop,wscale 7], length 0
IP 172.16.10.10.80 > 192.168.122.100.60236: Flags [S.], seq 3672808466, ack 4203590661, win 65535, options [mss 1430,sackOK,TS val 2474660123 ecr 1511828881,nop,wscale 8], length 0

Mas como o SIN ACK nunca é reconhecido, o roteador continua a enviar novos SIN.

Agora, se eu tcpdump eth1:

IP 192.168.100.90.60238 > 172.16.10.10.80: Flags [S], seq 3124513394, win 62720, options [mss 1460,sackOK,TS val 1511928806 ecr 0,nop,wscale 7], length 0
IP 192.168.100.90.60238 > 172.16.10.10.80: Flags [S], seq 3124513394, win 62720, options [mss 1460,sackOK,TS val 1511929823 ecr 0,nop,wscale 7], length 0
IP 192.168.100.90.60238 > 172.16.10.10.80: Flags [S], seq 3124513394, win 62720, options [mss 1460,sackOK,TS val 1511931839 ecr 0,nop,wscale 7], length 0

Podemos ver que a resposta nunca é roteada de volta para 192.168.100.90.

Se eu desativei o rastreamento de conexão e permiti tudo no iptables, funcionou. Então eu acho que o conntrack tem problemas para gerenciar conexões TCP de si mesmo para outra zona quando são NAT? Se algo não estiver claro, terei prazer em responder a quaisquer perguntas sobre isso.

Responder1

O problema estava presente no debian 10 com kernel 4.19.0-12-amd64, mas após uma atualização para o debian 11 com kernel 5.10.0-11-amd64, ele funciona como esperado, mesmo para fluxos TCP.

informação relacionada