我有一个在 vanilla 设置上运行的 Docker 容器,它监听端口 9999:
docker run --rm -it -p 9999:9999 busybox nc -vvl -p 9999 0.0.0.0
我向 NAT 上的 POSTROUTING 表添加了一条 LOG 规则,以便捕获服务器数据包。但是,当我从外部机器连接到此容器时,系统日志中什么也看不到。
不过,当我登录 mangle 的 POSTROUTING 链的末尾时,我可以看到连接数据包。根据The Diagram(TM),NAT-POSTROUTING 应该是下一个要传输的链。
这是 NAT 表:
# iptables -nL -tnat
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DOCKER 0 -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DOCKER 0 -- 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
LOG 0 -- 172.16.0.0/12 0.0.0.0/0 LOG flags 0 level 4 prefix "Packet:"
MASQUERADE 0 -- 172.17.0.0/16 0.0.0.0/0
MASQUERADE 6 -- 172.17.0.2 172.17.0.2 tcp dpt:9999
Chain DOCKER (2 references)
target prot opt source destination
RETURN 0 -- 0.0.0.0/0 0.0.0.0/0
DNAT 6 -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9999 to:172.17.0.2:9999
观察后journalctl -f | grep Packet:
没有发现任何异常。TRACE 指示 NAT 表被全部跳过,即服务器传出的数据包的所有链都被跳过。
设置如下:
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
为什么会出现这种情况?或者更一般地说,为什么有些数据包会完全跳过 NAT 表?
每个连接仅查询一次 NAT 表。
当收到传入的连接请求时,它具有远程源地址和主机地址作为目标:src=remote和dst=host。
第一个 PREROUTING 规则将数据包抛出到 DOCKER 链,最终将其 DNAT 到容器的地址:dst=container。由于这是预路由,因此数据包被路由到容器。
第三条 POSTROUTING 规则 MASQUERADE 数据包源地址:src=host(在这种情况下相当于 SNAT)。这使容器认为数据包来自主机,因此将直接回复主机。主机将取消 NAT 数据包并发送回远程。
这意味着连接现在已从(远程,主机)完全 NAT到(主机,容器),并且无需再次查询此连接 NAT 表。