![Conntrack, 다른 VRF에서 자체 TCP 패킷을 NAT하는 데 실패했습니다.](https://rvso.com/image/768930/Conntrack%2C%20%EB%8B%A4%EB%A5%B8%20VRF%EC%97%90%EC%84%9C%20%EC%9E%90%EC%B2%B4%20TCP%20%ED%8C%A8%ED%82%B7%EC%9D%84%20NAT%ED%95%98%EB%8A%94%20%EB%8D%B0%20%EC%8B%A4%ED%8C%A8%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4..png)
Debian 기반 라우터에서 여러 VRF를 사용할 때 소스 NAT에서 까다로운 문제를 발견했습니다. 설명하기가 좀 복잡해서 명확하게 설명하도록 노력하겠습니다만, 짧지는 않을 것 같습니다. 죄송합니다. 하지만 문제는 재현하기 쉬워야 합니다.
라우터 작업(라우팅 및 NATing 패킷)에서 라우터의 "관리" 부분(Ssh 및 기타 서비스)을 분리하기 위해 기본 VRF에서 "mgmt" VRF를 설정하려고 했습니다(서비스 소켓을 처리하기 더 쉬움). "방화벽"이라는 VRF의 라우팅입니다.
다이어그램은 다음과 같이 요약될 수 있습니다.
"관리" 네트워크는 192.168.100.0/24이며 네트워크 10.254.5.0/24를 통해 라우터의 "방화벽" VRF가 있는 L3이 있는 L3 스위치에 의해 라우팅됩니다. 세 번째 라우터 인터페이스는 "인터넷" 인터페이스이며, 이를 통과하는 패킷은 소스 NAT입니다. 이 설정은 conntrack의 원인인 라우터 자체 패킷을 제외하고 mgmt 서브넷의 모든 항목에 매우 잘 작동합니다.
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 스위치에 의해 라우팅되고 eth1에 의해 방화벽 VRF에 들어가고 eth2를 통해 라우팅 및 NAT됩니다. INPUT 및 FORWARD 연결을 추적하기 때문에 conntrack은 패킷이 돌아올 때 약간 혼란스럽고 패킷으로 무엇을 해야할지 알 수 없습니다.
원시 테이블에서 conntrack zone을 사용하여 ICMP 및 UDP에 대해 이 문제를 해결할 수 있었습니다.
# 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에 대한 ping을 사용하면 다음과 같습니다(명령 사용 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에서도 작동합니다(예: 8.8.8.8에 대한 DNS 쿼리).
[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에서는 작동하지 않습니다. 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이 NAT일 때 자체에서 다른 영역으로의 TCP 연결을 관리하는 데 문제가 있다고 생각합니까? 명확하지 않은 부분이 있으면 이에 대한 질문에 기꺼이 답변해 드리겠습니다.
답변1
이 문제는 커널 4.19.0-12-amd64가 있는 debian 10에서 발생했지만 커널 5.10.0-11-amd64가 있는 debian 11로 업그레이드한 후에는 TCP 흐름에서도 예상대로 작동합니다.