我正在尝试将客户端流量引导至正在监听的 kubernetes 集群 NodePort 192.168.1.100.30000
。
客户端需要发出请求,192.168.1.100.8000
因此我在 iptables 中添加了以下 REDIRECT 规则:
iptables -t nat -I PREROUTING -p tcp --dst 192.168.1.100 --dport 8000 -j REDIRECT --to-port 30000
然后我发出一个 curl 192.168.1.100:8000
,但是在 tcpdump 中我看到了不同的端口:
# tcpdump -i lo -nnvvv host 192.168.1.100 and port 8000
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
[Interface: lo] 20:39:22.685968 IP (tos 0x0, ttl 64, id 20590, offset 0, flags [DF], proto TCP (6), length 40)
[Interface: lo] 192.168.1.100.8000 > 192.168.1.100.49816: Flags [R.], cksum 0xacda (correct), seq 0, ack 3840205844, win 0, length 0
[Interface: lo] 20:39:37.519256 IP (tos 0x0, ttl 64, id 34221, offset 0, flags [DF], proto TCP (6), length 40)
我希望 tcpdump 显示类似
192.168.1.100.8000 > 192.168.1.100.30000
但是,由于没有列出任何进程,因此它显示并导致连接被拒绝错误192.168.1.100.49816
。
192.168.1.100.8000 > 192.168.1.100.49816
我正在使用测试环境,因此我无法访问远程设备,这就是我使用它curl
来测试 iptables REDIRECT 路径的原因。
添加 REDIRECT 规则会导致 tcpdump 将流量重定向到与指定端口不同的端口,这有什么原因吗?
编辑:
按照@AB建议添加了以下OUTPUT规则:
iptables -t nat -I OUTPUT -d 192.168.1.100 -p tcp --dport 8000 -j REDIRECT --to-port 30000
并且 curl 确实继续进行,OUTPUT 链的数据包数量确实增加了(但是 PREROUTING REDIRECT 链数据包没有增加):
2 10 600 REDIRECT tcp -- * * 0.0.0.0/0 192.168.1.100 tcp dpt:8000 redir ports 30000
但是,出现以下错误:
# curl -vk https://192.168.1.100:8000/v1/api
* About to connect() to 192.168.1.100 port 8000 (#0)
* Trying 192.168.1.100...
* Connected to 192.168.1.100 (192.168.1.100) port 8000 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* NSS error -12263 (SSL_ERROR_RX_RECORD_TOO_LONG)
* SSL received a record that exceeded the maximum permissible length.
* Closing connection 0
curl: (35) SSL received a record that exceeded the maximum permissible length.
另外,尝试添加远程系统网络,这次执行后 PREROUTING REDIRECT CHAIN 数据包数量增加remotesystem curl ...
(但 OUTPUT CHAIN 没有增加):
2 34 2040 REDIRECT tcp -- * * 0.0.0.0/0 172.16.128.1 tcp dpt:8000 redir ports 30000
错误:
# ip netns exec remotesystem curl -vk https://192.168.1.100:8000/v1/api
* About to connect() to 192.168.1.100 port 8000 (#0)
* Trying 192.168.1.100...
* Connection timed out
* Failed connect to 192.168.1.100:8000; Connection timed out
* Closing connection 0
curl: (7) Failed connect to 192.168.1.100:8000; Connection timed out
答案1
需要明确的是:原帖者的测试是从系统 192.168.1.100 向其自身进行的,而不是从远程系统进行的,这就是问题的原因。在这种情况下,端口没有改变,因为没有匹配的 NAT 规则,而如果从远程系统进行测试,端口就会匹配。
下图显示了如何对数据包执行操作顺序:
原因是 NAT 在 Linux 上的工作方式:iptablesnat
仅在新的 conntrack 流的第一个数据包(因此处于 NEW 状态)中看到该数据包。
此规则在远程系统上工作正常。在这种情况下,看到的第一个数据包将是一个传入数据包:
to port 8000 --> AF_PACKET (tcpdump) --> conntrack --> nat/PREROUTING (iptables REDIRECT): to port 30000
--> routing decision --> ... --> local process receiving on port 30000
同一流中的所有后续数据包都将让 conntrack 直接处理端口更改(或回复的端口还原),并跳过表中的任何 iptables 规则nat
(如示意图中所述:nat
仅针对NEW
连接查询的表)。因此,(跳过回复数据包部分),下一个传入数据包将经历此过程:
to port 8000 --> AF_PACKET (tcpdump) --> conntrack: to port 30000
--> routing decision --> ... --> local process receiving on port 30000
对于系统自身的测试,第一个数据包不是传入数据包,而是传出数据包。相反,使用传出lo
接口会发生这种情况:
local process client curl --> routing decision --> conntrack --> nat/OUTPUT (
no rule here
)
--> reroute check --> AF_PACKET (tcpdump) --> to port 8000
现在这个数据包在接口上被环回lo
,它重新出现为一个数据包,不再是连接中的第一个数据包,因此遵循上述第二种情况:conntrack 单独处理 NAT 并且不调用nat/PREROUTING
。除非在之前的步骤中没有指示执行任何 NAT:
to port 8000 --> AF_PACKET (tcpdump) --> conntrack
--> routing decision --> ... -->
no
local process receiving on port
8000
由于没有任何内容在端口 8000 上监听,因此操作系统发回了 TCP RST。
为了使其在本地系统上发挥作用,REDIRECT
还必须在链中加入一条规则nat/OUTPUT
:
iptables -t nat -I OUTPUT -d 192.168.1.100 -p tcp --dport 8000 -j REDIRECT --to-port 30000
补充笔记
如果案例旨在用于远程使用,请不要从本地系统进行测试:测试所遍历的规则并不相同。这使得测试无法反映现实。
只需使用网络命名空间即可创建袖珍远程系统,以防没有其他系统可用。示例应适用于仅具有 OP
nat/PREROUTING
规则并执行curl http://192.168.1.100/
(不需要 DNS)的系统:ip netns add remotesystem ip link add name vethremote up type veth peer netns remotesystem name eth0 ip address add 192.0.2.1/24 dev vethremote ip -n remotesystem address add 192.0.2.2/24 dev eth0 ip -n remotesystem link set eth0 up ip -n remotesystem route add 192.168.1.100 via 192.0.2.1 ip netns exec remotesystem curl http://192.168.1.100:8000/
tcpdump
和 NATtcpdump
在上述示意图中的步骤中会发生这种情况AF_PACKET
:入口非常早,出口非常晚。这意味着对于远程系统案例,即使它在工作,它也永远不会捕获端口 30000。对于本地系统案例,一旦nat/OUTPUT
添加规则,它将捕获端口 30000。不要盲目相信
tcpdump
执行 NAT 时显示的地址/端口:这取决于情况以及捕获发生的位置。