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:
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 eth0
são marcados zone 5
e quando os pacotes entram eth0
eles 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 NEW
conexão é criada quando o pacote passa eth0
com a zone=5
tag, e uma nova quando entra no firewall VRF eth1
sem 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 eth2
a 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.