Я столкнулся с непростой проблемой с исходным NAT при использовании нескольких VRF на маршрутизаторе на базе Debian. Объяснить это немного сложно, поэтому я постараюсь быть понятным, но это не будет коротко, извините за это. Хотя проблема должна быть легко воспроизведена.
Чтобы изолировать «управляющую» часть маршрутизатора (ssh и другие службы) от его работы маршрутизатора (маршрутизация и NAT-преобразование пакетов), я попытался настроить VRF «mgmt» в VRF по умолчанию (так проще работать с сокетами служб), а маршрутизацию — в VRF под названием «брандмауэр».
Диаграмму можно обобщить следующим образом:
Сеть "управления" - 192.168.100.0/24, и она маршрутизируется коммутатором L3, у которого есть L3 с VRF "брандмауэра" маршрутизатора через сеть 10.254.5.0/24. Третий интерфейс маршрутизатора - это его "интернет", и пакеты, которые проходят через него, подвергаются NAT источника. Эта настройка работает довольно хорошо для всего в подсети mgmt, за исключением собственных пакетов маршрутизатора, из-за conntrack.
О правилах 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
О таблице маршрутизации:
# 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
Итак, когда пакет из VRF по умолчанию пытается получить доступ к интернету, он выходит из eth0, маршрутизируется коммутатором L3, входит в VRF брандмауэра через eth1 и маршрутизируется и NATируется через eth2. Поскольку я отслеживаю соединения INPUT и FORWARD, conntrack немного сбит с толку, когда пакет возвращается, и он не может знать, что с ним делать.
Мне удалось исправить это для ICMP и UDP, используя зону conntrack в таблице raw.
# Table raw
# chain PREROUTING
-A PREROUTING -i eth0 -j CT --zone 5
# chain OUTPUT
-A OUTPUT -o eth0 -j CT --zone 5
Благодаря этим правилам пакеты, исходящие от маршрутизатора и проходящие через него, eth0
тегируются zone 5
, и когда пакеты поступают на него, eth0
они также тегируются zone 5
.
При пинге на 8.8.8.8 это выглядит так (с помощью команды 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
Мы видим здесь, что первое NEW
соединение создается, когда пакет проходит eth0
с zone=5
тегом, затем новое, когда он входит в брандмауэр VRF eth1
без тега. Когда приходит ответ, сначала обновляется второе соединение (так как оно обращено к интернету), а затем первое.
Это также работает с UDP, например, с DNS-запросом на 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
Но с TCP это не работает. Запрос telnet на 172.16.10.10 порт 80 выглядит так:
[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)
Если я выполню tcpdump, eth2
то ответ будет следующим:
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
Но поскольку SIN ACK никогда не подтверждается, маршрутизатор продолжает отправлять новые SIN.
Теперь, если я выполню 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
Мы видим, что ответ никогда не направляется обратно на 192.168.100.90.
Если я отключил отслеживание подключений и разрешил все в iptables, то все работает. Так что я думаю, что conntrack имеет проблемы с управлением TCP-подключениями из себя в другую зону, когда они NAT? Если что-то не понятно, я с радостью отвечу на любые вопросы по этому поводу.
решение1
Проблема присутствовала в Debian 10 с ядром 4.19.0-12-amd64, но после обновления до Debian 11 с ядром 5.10.0-11-amd64 она работает так, как и ожидалось, даже для потоков TCP.