iptables NAT 规则不生效的 PREROUTING/POSTROUTING 顺序与接口绑定

13次阅读

iptables NAT规则不生效主因是链触发时机与数据包流向不匹配:PREROUTING在路由前处理入向包(需匹配真实入接口且ip_forward=1),POSTROUTING在路由后处理出向包(需匹配最终出口接口),接口条件必须按实际转发路径设置,不可凭IP或服务猜测。

iptables NAT 规则不生效的 PREROUTING/POSTROUTING 顺序与接口绑定

iptables 的 NAT 规则不生效,常见原因不是规则写错了,而是链的触发时机、数据包流向和接口匹配没对上。PREROUTING 和 POSTROUTING 属于 nat 表的两个关键链,它们在数据包生命周期中位置固定,但是否命中,取决于数据包实际经过的网络路径和 -i / -o 接口条件是否匹配。

PREROUTING 链只处理“刚进设备”的入向数据包

PREROUTING 在路由决策前触发,适用于目标地址需要修改(如 DNAT)的场景,比如把发往本机某端口的请求转给内网服务器。但它**只匹配进入该网络接口的数据包**,且仅对非本机发起、目的地为本机或需转发的数据包生效。

  • 若数据包是从本机进程发出(如 curl 本地地址),它根本不会经过 PREROUTING,而是走 OUTPUT 链
  • 若用 -i eth0 但数据包实际从 docker0 或 lo 进来,规则不匹配
  • 转发未开启(net.ipv4.ip_forward=0)时,即使写了 PREROUTING DNAT,系统也不会把包送进转发流程,DNAT 后续也无意义

POSTROUTING 链只处理“即将发出去”的出向数据包

POSTROUTING 在路由决策后、离开网络接口前触发,常用于 SNAT/MASQUERADE,比如让内网机器通过网关上网。它的关键点是:必须匹配数据包**最终选择的出口接口**(-o),而不是源地址或中间桥接设备。

  • 若写 -o eth0,但因路由表选择走 bond0 或 wg0 出去,规则跳过
  • dockerpodman 默认使用 docker0 桥,容器访问外网时,POSTROUTING 匹配的是宿主机的物理出口(如 eth0),而非 docker0;但若启用了 hairpin 或 host-network 模式,路径可能不同
  • MASQUERADE 依赖 -o,且只在动态 IP(如 DHCP)场景比 SNAT 更安全;但若 -o 条件写错,它就静默不生效

接口绑定要按真实转发路径写,不能凭 IP 或服务猜

很多人习惯根据服务监听 IP 或客户端来源写 -i,但 iptables 不看“谁连的”,而看“包从哪个接口进来、从哪个接口出去”。验证方法很简单:

  • tcpdump 抓包确认流量实际进出的接口:tcpdump -i eth0 port 80
  • 查路由:ip route get 8.8.8.8 看出口设备;查反向路径:ip route get from 192.168.1.100 to 10.0.0.5
  • 启用日志临时排查:-j LOG –log-prefix “NAT-DEBUG: “,配合 dmesg | tail 看哪条规则被命中

常见配置陷阱与检查清单

以下情况会导致 NAT 规则看似存在却无效:

  • nat 表规则放在 Filter 表里(比如误用 -A input 而非 -t nat -A PREROUTING)
  • 规则插入顺序错误:靠前的 ACCEPT 或 RETURN 提前终止了链遍历
  • 使用了 -s 或 -d 匹配,但值与实际包头不符(如 DNAT 后目的 IP 已变,PREROUTING 中 -d 是原始目的,POSTROUTING 中 -s 是原始源)
  • 容器环境绕过 iptables(如启用 nftables 后台、firewalld 管理、或使用 net=host)
text=ZqhQzanResources