为什么在网络命名空间分离的透明代理设置中不会发生 iptables NAT?

为什么在网络命名空间分离的透明代理设置中不会发生 iptables NAT?

我正在尝试在我的主机上设置透明代理网络。

真实客户端和代理目标是容器,但在这个实验中我使用 netns(网络命名空间)分离的环境。

为了将客户端流量透明地重定向到代理,我使用策略路由。

 Client (C)         Proxy (P)
 10.10.1.1/24      10.10.2.1/24
     veth0             veth0
      |                 |
   veth pair         veth pair
      |                 |
  -----------(HOST)--------------
 client-veth0       proxy-veth0
 10.10.1.2/24      10.10.2.2/24
      |                 |            172.16.202.30
      +-----------------+-------------- enp4s0 ---- INTERNET

# Policy Routing on Host
# [Client->Proxy]
# ip rule:  from 10.10.1.0/24 iif client-veth0 lookup 100
# ip route: (100) default via 10.10.2.1 dev proxy-veth0
# [Proxy->Internet]
# ip route: (master) default via 172.16.202.1 dev enp4s0 proto static metric 100
# iptables: -t nat -A POSTROUTING -s 10.10.1.1/32 -o enp4s0 -j MASQUERADE
# [Internet->Proxy]
# ip rule:  from all to 10.10.1.0/24 iif enp4s0 lookup 100
# ip route: (100) default via 10.10.2.1 dev proxy-veth0
# [Proxy->Client]
# ip rule:  from all to 10.10.1.0/24 iif proxy-veth0 lookup 101
# ip route: (101) default via 10.10.1.1 dev client-veth0

问题是,当我从客户端 ping 8.8.8.8 时,在客户端网络内,源 IP 伪装不会发生。 iptables 伪装规则不匹配,默认为 ACCEPT 。我预计 enp4s0 上的 tcpdump 会显示172.16.202.30 --> 8.8.8.8,但它显示10.10.1.1 --> 8.8.8.8,没有源 IP 伪装。

我记录了互联网线路上的 pcap 以澄清没有发生 SNAT。client_to_goolge是从 enp4s0 外面的单独机器记录的:

$ tcpdump -r client_to_google -n
reading from file client_to_google, link-type EN10MB (Ethernet)
23:35:40.852257 IP 10.10.1.1 > 8.8.8.8: ICMP echo request, id 14867, seq 1, length 64
23:35:41.865269 IP 10.10.1.1 > 8.8.8.8: ICMP echo request, id 14867, seq 2, length 64

当我检查 iptables mangle 表时,数据包按照给定的策略流动:

  PREROUTING: client-veth0, 10.10.1.1 --> 8.8.8.8
  POSTROUTING: proxy-veth0, 10.10.1.1 --> 8.8.8.8
  PREROUTING: proxy-veth0, 10.10.1.1 --> 8.8.8.8
  POSTROUTING: enp4s0, 10.10.1.1 --> 8.8.8.8

proxy-veth0但是,当我更改接口上的伪装规则时,就像这样iptables: -t nat -A POSTROUTING -s 10.10.10.1/32 -o proxy-veth0 -j MASQUERADE,伪装就会发生。也就是说 10.10.2.2 --> 8.8.8.8数据包被捕获了。

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
...
11       0     0 MASQUERADE  all  --  *      enp4s0  10.10.1.1            0.0.0.0/0
12       1    84 MASQUERADE  all  --  *      proxy-veth0  10.10.1.1            0.0.0.0/0

上表显示规则 #11enp4s0输出条件未触发。规则 #12 是在对规则 #11 进行多次测试后插入的。规则 #12 表明输出条件确实触发了。主网卡和虚拟接口与 iptablesproxy-veth0之间有什么区别吗?enp4s0proxy-veth0

任何评论都将受到高度赞赏,谢谢。

答案1

我必须假设透明代理充当路由器,至少对于 ICMP 而言,因此将把 ICMP 回显路由回其来源地(veth0)。

发现问题

在重现您的设置并发现您的问题时,我添加了一个痕迹在主机上使用iptables(可能与旧版略有不同)iptables-nft的版本)像这样(我还强制创建了过滤表(iptables -S)以便将其包含在跟踪中):

iptables -t raw -A PREROUTING -j TRACE

内核日志中显示一个 ping 操作(提示,如果主持人不是实际的初始主机sysctl -w net.netfilter.nf_log_all_netns=1:):

TRACE: raw:PREROUTING:policy:2 IN=client-veth0 OUT= MAC=66:f2:08:79:d0:df:be:1e:05:c1:c1:4b:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=7200 DF PROTO=ICMP TYPE=8 CODE=0 ID=3508 SEQ=1 
TRACE: nat:PREROUTING:policy:1 IN=client-veth0 OUT= MAC=66:f2:08:79:d0:df:be:1e:05:c1:c1:4b:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=7200 DF PROTO=ICMP TYPE=8 CODE=0 ID=3508 SEQ=1 
TRACE: filter:FORWARD:policy:1 IN=client-veth0 OUT=proxy-veth0 MAC=66:f2:08:79:d0:df:be:1e:05:c1:c1:4b:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=63 ID=7200 DF PROTO=ICMP TYPE=8 CODE=0 ID=3508 SEQ=1 
TRACE: nat:POSTROUTING:policy:2 IN=client-veth0 OUT=proxy-veth0 MAC=66:f2:08:79:d0:df:be:1e:05:c1:c1:4b:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=63 ID=7200 DF PROTO=ICMP TYPE=8 CODE=0 ID=3508 SEQ=1 
TRACE: raw:PREROUTING:policy:2 IN=proxy-veth0 OUT= MAC=16:c9:3c:d4:ad:8c:8a:84:06:5d:88:e2:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=62 ID=7200 DF PROTO=ICMP TYPE=8 CODE=0 ID=3508 SEQ=1 
TRACE: filter:FORWARD:policy:1 IN=proxy-veth0 OUT=enp4s0 MAC=16:c9:3c:d4:ad:8c:8a:84:06:5d:88:e2:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=61 ID=7200 DF PROTO=ICMP TYPE=8 CODE=0 ID=3508 SEQ=1 

与此同时,conntrack -E在主机上运行显示匹配:

# conntrack -E
    [NEW] icmp     1 30 src=10.10.1.1 dst=8.8.8.8 type=8 code=0 id=3508 [UNREPLIED] src=8.8.8.8 dst=10.10.1.1 type=0 code=0 id=3508

发生了什么:

  • conntrack(处理 NAT)不关心路由(例如:conntrack 数据库中没有接口),只关心地址,
  • 纳特表只会看到处于 NEW 状态的数据包,
  • 连接跟踪在数据库中添加了一个新条目,这是当数据包从客户端-veth0代理-veth0:不符合 POSTROUTING 规则,
  • 第二轮路由时代理-veth0enp4s0数据包与连接跟踪纳特表没有再次被调用
  • 数据包以非 NAT 方式流向互联网。

从此连接跟踪的限制阻碍了过去的一些用例,比如您的用例,添加了一个附加功能:

连接跟踪区域

区域只是与网络设备相关联的数字标识符,它被合并到各种哈希中并用于区分连接元组之外的条目。

[...]

这主要适用于当使用相同的地址连接多个私有网络(不幸的是,这种情况偶尔会发生)时,将数据包通过一组 veth 设备并将每个网络 SNAT 到唯一地址,之后它们可以通过“主”区域并像常规非冲突数据包一样处理和/或根据传出接口第二次应用 NAT。

它允许复制连接跟踪设施,包括 NAT 处理,但必须手动完成并匹配问题:这里是路由拓扑。

因此,这里的客户端<->代理流量,连接跟踪的观点,必须与其他流量分离开来。

我本来希望将代理<-> Internet 流量与通用主机流量分开,但这太难了,因为生的表(其中必须将区域分配给数据包)仅看到非去 NAT 流量,因此 Internet 回复将全部到达目的地 172.16.202.30)。无论如何,这里没有像客户端 <-> 代理流那样的重复流,因此这不是真正需要的。

  • 区域 0(0 表示没有特殊区域):通用主机流量以及代理<-> Internet 流量。

    没什么特别的,这是默认的。

  • 区域 1:客户端 <-> 代理流量。CT --zone使用目标。此处的值是任意选择的,在本例中不需要在其他地方使用。

    iptables -t raw -A PREROUTING -i client-veth0 -j CT --zone 1
    iptables -t raw -A PREROUTING -i proxy-veth0 -d 10.10.1.0/24 -j CT --zone 1
    

现在正确的结果(我合并了两个工具的输出):

TRACE: raw:PREROUTING:rule:2 IN=client-veth0 OUT= MAC=4e:e7:2f:3f:a3:6c:4a:b9:40:66:60:32:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=58185 DF PROTO=ICMP TYPE=8 CODE=0 ID=4079 SEQ=1 
TRACE: raw:PREROUTING:policy:4 IN=client-veth0 OUT= MAC=4e:e7:2f:3f:a3:6c:4a:b9:40:66:60:32:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=58185 DF PROTO=ICMP TYPE=8 CODE=0 ID=4079 SEQ=1 
TRACE: nat:PREROUTING:policy:1 IN=client-veth0 OUT= MAC=4e:e7:2f:3f:a3:6c:4a:b9:40:66:60:32:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=58185 DF PROTO=ICMP TYPE=8 CODE=0 ID=4079 SEQ=1 
TRACE: filter:FORWARD:policy:1 IN=client-veth0 OUT=proxy-veth0 MAC=4e:e7:2f:3f:a3:6c:4a:b9:40:66:60:32:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=63 ID=58185 DF PROTO=ICMP TYPE=8 CODE=0 ID=4079 SEQ=1 
TRACE: nat:POSTROUTING:policy:2 IN=client-veth0 OUT=proxy-veth0 MAC=4e:e7:2f:3f:a3:6c:4a:b9:40:66:60:32:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=63 ID=58185 DF PROTO=ICMP TYPE=8 CODE=0 ID=4079 SEQ=1 
    [NEW] icmp     1 30 src=10.10.1.1 dst=8.8.8.8 type=8 code=0 id=4079 [UNREPLIED] src=8.8.8.8 dst=10.10.1.1 type=0 code=0 id=4079 zone=1
TRACE: raw:PREROUTING:policy:4 IN=proxy-veth0 OUT= MAC=86:c8:4b:5f:16:fc:ba:76:80:0f:20:7d:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=62 ID=58185 DF PROTO=ICMP TYPE=8 CODE=0 ID=4079 SEQ=1 
TRACE: nat:PREROUTING:policy:1 IN=proxy-veth0 OUT= MAC=86:c8:4b:5f:16:fc:ba:76:80:0f:20:7d:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=62 ID=58185 DF PROTO=ICMP TYPE=8 CODE=0 ID=4079 SEQ=1 
TRACE: filter:FORWARD:policy:1 IN=proxy-veth0 OUT=enp4s0 MAC=86:c8:4b:5f:16:fc:ba:76:80:0f:20:7d:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=61 ID=58185 DF PROTO=ICMP TYPE=8 CODE=0 ID=4079 SEQ=1 
TRACE: nat:POSTROUTING:rule:1 IN=proxy-veth0 OUT=enp4s0 MAC=86:c8:4b:5f:16:fc:ba:76:80:0f:20:7d:08:00 SRC=10.10.1.1 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=61 ID=58185 DF PROTO=ICMP TYPE=8 CODE=0 ID=4079 SEQ=1 
    [NEW] icmp     1 30 src=10.10.1.1 dst=8.8.8.8 type=8 code=0 id=4079 [UNREPLIED] src=8.8.8.8 dst=172.16.202.30 type=0 code=0 id=4079
TRACE: raw:PREROUTING:policy:4 IN=enp4s0 OUT= MAC=5e:e8:0c:bf:96:d9:b2:e7:bc:df:1f:8e:08:00 SRC=8.8.8.8 DST=172.16.202.30 LEN=84 TOS=0x00 PREC=0x00 TTL=62 ID=12099 PROTO=ICMP TYPE=0 CODE=0 ID=4079 SEQ=1 
TRACE: filter:FORWARD:policy:1 IN=enp4s0 OUT=proxy-veth0 MAC=5e:e8:0c:bf:96:d9:b2:e7:bc:df:1f:8e:08:00 SRC=8.8.8.8 DST=10.10.1.1 LEN=84 TOS=0x00 PREC=0x00 TTL=61 ID=12099 PROTO=ICMP TYPE=0 CODE=0 ID=4079 SEQ=1 
 [UPDATE] icmp     1 30 src=10.10.1.1 dst=8.8.8.8 type=8 code=0 id=4079 src=8.8.8.8 dst=172.16.202.30 type=0 code=0 id=4079
TRACE: raw:PREROUTING:rule:3 IN=proxy-veth0 OUT= MAC=86:c8:4b:5f:16:fc:ba:76:80:0f:20:7d:08:00 SRC=8.8.8.8 DST=10.10.1.1 LEN=84 TOS=0x00 PREC=0x00 TTL=60 ID=12099 PROTO=ICMP TYPE=0 CODE=0 ID=4079 SEQ=1 
TRACE: raw:PREROUTING:policy:4 IN=proxy-veth0 OUT= MAC=86:c8:4b:5f:16:fc:ba:76:80:0f:20:7d:08:00 SRC=8.8.8.8 DST=10.10.1.1 LEN=84 TOS=0x00 PREC=0x00 TTL=60 ID=12099 PROTO=ICMP TYPE=0 CODE=0 ID=4079 SEQ=1 
TRACE: filter:FORWARD:policy:1 IN=proxy-veth0 OUT=client-veth0 MAC=86:c8:4b:5f:16:fc:ba:76:80:0f:20:7d:08:00 SRC=8.8.8.8 DST=10.10.1.1 LEN=84 TOS=0x00 PREC=0x00 TTL=59 ID=12099 PROTO=ICMP TYPE=0 CODE=0 ID=4079 SEQ=1 
 [UPDATE] icmp     1 30 src=10.10.1.1 dst=8.8.8.8 type=8 code=0 id=4079 src=8.8.8.8 dst=10.10.1.1 type=0 code=0 id=4079 zone=1

这里来自新流的第一个数据包触发两次 iptables'纳特表,第一次没有效果。实际上连接跟踪认为有两个流,因为第一个流具有附加属性zone=1

答案2

这是正常的 iptables NAT 行为。现在使用 tcpdump 进行测试,我使用三台虚拟机,在每个虚拟机上我都设置了 SNAT 规则

vm1(10.10.1.1) ---> vm2(10.10.2.2) ---> vm3(1.2.3.4) (公网 IP 地址)

然后我尝试从 10.10.1.1 ping 8.8.8.8,在所有三台机器上运行 tcpdump,结果如下:在 vm2 上 10.10.1.1 ---> 8.8.8.8 在 vm3 上 10.10.2.2 ---> 8.8.8.8

理论上,如果我有 vm4,我会看到 1.2.3.4 ---> 8.8.8.8

您能显示一下您的代理配置吗?

鲍里斯

相关内容