news 2026/9/7 6:44:11

Moby(Docker Engine)桥接网络端口映射的 iptables 规则详解:从 `-p 8080:80` 到 filter/nat/raw 三张表

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Moby(Docker Engine)桥接网络端口映射的 iptables 规则详解:从 `-p 8080:80` 到 filter/nat/raw 三张表

Moby(Docker Engine)桥接网络端口映射的 iptables 规则详解:从-p 8080:80到 filter/nat/raw 三张表

【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby

本文基于 Moby 仓库中自动生成的integration/network/bridge/iptablesdoc文档集,聚焦其中"用户自定义网络上带发布端口"这一场景,完整展示docker run -p 8080:80之后 iptables filter、nat、raw 三张表中的全部规则,并逐条对照 bridge 驱动源码 解释每条规则的产生时机与插入位置,帮助读者理解端口发布背后的 DNAT、MASQUERADE 与默认 DROP 机制,以及这套文档如何由集成测试自动生成并做 golden diff 校验。

1. 文档定位:这是怎样一份"活"的参考

integration/network/bridge/iptablesdoc/目录记录了 Docker Engine 在多种网络场景下实际写入 iptables/ip6tables 的规则。index.md 明确说明:

  • 这份文档仅供开发参考——docker 的 iptables(以及 ip6tables)规则结构会随版本变化,不是稳定接口;
  • 文档由测试TestBridgeIptablesDoc生成:测试启动一个真实 daemon,创建网络与容器,然后捕获 iptables 输出,再与各 section 对应的text/template合并渲染,最后与仓库中generated/目录下的 golden 文件做 diff,规则有差异测试即失败;
  • ip6tables 规则与 iptables 模式相同,因此文档只展示 IPv4 规则;
  • 一个容易踩坑的事实:filter 表的 INPUT 链不被 Docker 使用——来自宿主物理网络或宿主本机的数据包因为是路由进 bridge 网络,命中的是 filter-FORWARD 链;同理 filter-OUTPUT 也不被使用。

该测试还说明了重启行为:bridge 驱动在初始化时会删除自建链并在网络恢复时重建规则,但filter-FORWARD 链不会被清空,且网络重建顺序与原始创建顺序无关,因此 daemon 重启后规则排列可能不同;当 firewalld 运行时,其重载会清空 iptables 规则,daemon 通过 dbus 注册重载事件处理器来重建规则。

本文聚焦的场景文档是 generated/usernet-portmap.md,同系列还覆盖新 daemon、loopback 发布端口、无 userland proxy、禁用 ICC、internal 网络、routed 模式、nat-unprotected和 Swarm service 等场景。

2. 场景等价命令

文档描述的等价操作是:创建一个名为bridge1的用户自定义桥接网络(子网192.0.2.0/24、网关192.0.2.1),并在其上运行一个发布 80 端口到宿主 8080 的容器:

docker network create \ -o com.docker.network.bridge.name=bridge1 \ --subnet 192.0.2.0/24 --gateway 192.0.2.1 bridge1 docker run --network bridge1 -p 8080:80 --name c1 busybox

这个场景在测试代码 iptablesdoc_linux_test.go 的index变量中有声明(第 78~89 行):networks: bridge1 + containers: c1(80/tcp -> 8080),网络地址取自测试专用的docNetworks序列(192.0.2.0/24198.51.100.0/24203.0.113.0/24)。测试通过 createBridgeNetworks 用 daemon client 以com.docker.network.bridge.namecom.docker.network.bridge.gateway_mode(默认nat)等 option 创建网络,容器 IP 因此分配为192.0.2.2

3. filter 表:该场景的完整规则

场景文档给出的 filter 表状态(iptables -vL --line-numbers -t filter)为:

Chain INPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 DOCKER-USER all -- any any anywhere anywhere 2 0 0 DOCKER-FORWARD all -- any any anywhere anywhere Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination Chain DOCKER (2 references) num pkts bytes target prot opt in out source destination 1 0 0 ACCEPT tcp -- !bridge1 bridge1 anywhere 192.0.2.2 tcp dpt:http 2 0 0 DROP all -- !docker0 docker0 anywhere anywhere 3 0 0 DROP all -- !bridge1 bridge1 anywhere anywhere Chain DOCKER-BRIDGE (1 references) num pkts bytes target prot opt in out source destination 1 0 0 DOCKER all -- any docker0 anywhere anywhere 2 0 0 DOCKER all -- any bridge1 anywhere anywhere Chain DOCKER-CT (1 references) num pkts bytes target prot opt in out source destination 1 0 0 ACCEPT all -- any docker0 anywhere anywhere ctstate RELATED,ESTABLISHED 2 0 0 ACCEPT all -- any bridge1 anywhere anywhere ctstate RELATED,ESTABLISHED Chain DOCKER-FORWARD (1 references) num pkts bytes target prot opt in out source destination 1 0 0 DOCKER-CT all -- any any anywhere anywhere 2 0 0 DOCKER-INTERNAL all -- any any anywhere anywhere 3 0 0 DOCKER-BRIDGE all -- any any anywhere anywhere 4 0 0 ACCEPT all -- docker0 any anywhere anywhere 5 0 0 ACCEPT all -- bridge1 any anywhere anywhere Chain DOCKER-INTERNAL (1 references) num pkts bytes target prot opt in out source destination Chain DOCKER-USER (1 references) num pkts bytes target prot opt in out source destination

对应的iptables -S -t filter命令:

-P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT -N DOCKER -N DOCKER-BRIDGE -N DOCKER-CT -N DOCKER-FORWARD -N DOCKER-INTERNAL -N DOCKER-USER -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-FORWARD -A DOCKER -d 192.0.2.2/32 ! -i bridge1 -o bridge1 -p tcp -m tcp --dport 80 -j ACCEPT -A DOCKER ! -i docker0 -o docker0 -j DROP -A DOCKER ! -i bridge1 -o bridge1 -j DROP -A DOCKER-BRIDGE -o docker0 -j DOCKER -A DOCKER-BRIDGE -o bridge1 -j DOCKER -A DOCKER-CT -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A DOCKER-CT -o bridge1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A DOCKER-FORWARD -j DOCKER-CT -A DOCKER-FORWARD -j DOCKER-INTERNAL -A DOCKER-FORWARD -j DOCKER-BRIDGE -A DOCKER-FORWARD -i docker0 -j ACCEPT -A DOCKER-FORWARD -i bridge1 -j ACCEPT

3.1 规则链的转发路径

数据面视角下,一条从外部主机进入容器 80 端口的转发包会经历:

  1. filter-FORWARD首先生效:DOCKER-USER链为空(留给用户自定义规则),随后进入DOCKER-FORWARD;
  2. DOCKER-FORWARD依次跳入DOCKER-CT(放行 conntrack 状态为RELATED,ESTABLISHED的回程/相关包,保证入站连接的双向放行不依赖 conntrack 之前的匹配)、DOCKER-INTERNAL(空)、DOCKER-BRIDGE(按出接口docker0/bridge1二次跳入DOCKER链),最后由按入接口排列的ACCEPT(第 4、5 条)放行容器出站流量;
  3. DOCKER 链完成核心判定:每端口ACCEPT与每网络DROP的先后顺序决定了"只放行已发布端口"。

3.2 场景文档指出的三个关键变化

创建bridge1+ 容器c1之后,相对新 daemon 状态(filter 表变化)要点如下,与原文档一致:

  • DOCKER-FORWARD链中,针对新网络的出站ACCEPT 规则(第 5 条,-i bridge1)被追加到链尾;
  • DOCKER-CTDOCKER-FORWARD链各自为bridge1新增了一条规则;
  • DOCKER链中出现一条对"路由到容器地址192.0.2.2的 TCP 80 端口包"的 ACCEPT 规则。这条规则是在容器创建时加入的(其余规则均在驱动初始化或网络创建时加入),即 setPerPortForwarding 的职责;
    • 每端口规则都插入链首,而每网络的 DROP 规则 setDefaultForwardRule 总是追加到链尾,所以 ACCEPT 一定先于 DROP 生效。本例中由于docker0先于bridge1创建,bridge1的规则分别出现在docker0DROP 规则的上方与下方——这正是 DOCKER 链第 1~3 条排列(ACCEPT 80、DROP docker0、DROP bridge1)的由来。

源码上,setPerPortForwarding构造的规则参数为! -i <bridge> -o <bridge> -p tcp -d <容器IP> --dport <端口> -j ACCEPT,并通过 programChainRule 以 "OPEN PORT" 标记插到 filter 表DOCKER链顶部;而setDefaultForwardRule对每个非 internal 网络追加! -i <bridge> -o <bridge> -j DROP(若网关模式为nat-unprotected则改为ACCEPT),注释明确写道该 DROP 规则"必须位于插在链首的每端口 ACCEPT 规则之后"。

其余网络级规则来自 setupIPTables:DOCKER-CT的 conntrack ACCEPT(ctRule)、DOCKER-BRIDGE的按桥跳转(jumpToDockerRule),以及 setupNonInternalNetworkRules 中 ICC 开启时的出站规则outRuleICC(-i <bridge> -j ACCEPT,即 DOCKER-FORWARD 第 5 条,标记为 "ACCEPT OUTGOING",追加到链尾)。

4. nat 表:DNAT 与 MASQUERADE

场景文档给出的 nat 表状态:

Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 DOCKER all -- any any anywhere anywhere ADDRTYPE match dst-type LOCAL Chain INPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 DOCKER all -- any any anywhere !loopback/8 ADDRTYPE match dst-type LOCAL Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 MASQUERADE all -- any !bridge1 192.0.2.0/24 anywhere 2 0 0 MASQUERADE all -- any !docker0 172.17.0.0/16 anywhere Chain DOCKER (2 references) num pkts bytes target prot opt in out source destination 1 0 0 DNAT tcp -- !bridge1 any anywhere anywhere tcp dpt:http-alt to:192.0.2.2:80

对应的iptables -S -t nat命令:

-P PREROUTING ACCEPT -P INPUT ACCEPT -P OUTPUT ACCEPT -P POSTROUTING ACCEPT -N DOCKER -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER -A POSTROUTING -s 192.0.2.0/24 ! -o bridge1 -j MASQUERADE -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE -A DOCKER ! -i bridge1 -p tcp -m tcp --dport 8080 -j DNAT --to-destination 192.0.2.2:80

各部分来源与含义:

  • PREROUTING/OUTPUT 的dst-type LOCAL跳转:由 addNATJumpRules 在驱动初始化时追加,确保发往宿主本地地址的包(包括本机curl 127.0.0.1:8080)都会进入 nat-DOCKER 链做 DNAT 判定。OUTPUT 规则带! -d 127.0.0.0/8(非 hairpin 模式时排除回环地址);
  • DOCKER 链的 DNAT 规则:8080 -> 192.0.2.2:80,由 setPerPortNAT 在容器端口发布时创建。源码中有两个细节值得注意:
    • 宿主机绑定地址未指定时,写入规则用的是0/0而非0.0.0.0——因为 "iptables 会把0.0.0.0解释为0.0.0.0/32,而0/0才会被 iptables 与 ip6tables 一致地解释为任意值"(见 port.go 第 84~90 行);
    • ! -i bridge1参数在Hairpin=false时追加(即默认情况),意味着从 bridge 接口本身进入的包不做 DNAT——容器之间或网关侧直连不会误触发端口重定向;
  • POSTROUTING 的 MASQUERADE 规则:-s <子网> ! -o <bridge> -j MASQUERADE对每个启用了 Masquerade 的网络一条(本例为192.0.2.0/24docker0172.17.0.0/16),来自 setupNonInternalNetworkRules。它把容器出站的源地址改写为主机对外地址,使回程流量经 conntrack 正确还原 DNAT;若用户指定了host_ip,则改用SNAT --to-source

至此完整链路为:外部包进入PREROUTING-> nat-DOCKER 命中 DNAT 改写目的地址为192.0.2.2:80-> filter-FORWARD 经 DOCKER-BRIDGE/DOCKER 链因每端口 ACCEPT 放行 -> 路由至 bridge1 -> 回程包在 POSTROUTING 被 MASQUERADE,经 conntrack 还原后回到外部主机。

5. raw 表:屏蔽对容器地址的直连访问

场景文档给出的 raw 表状态:

Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 DROP all -- !bridge1 any anywhere 192.0.2.2 Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination

对应的iptables -S -t raw命令:

-P PREROUTING ACCEPT -P OUTPUT ACCEPT -A PREROUTING -d 192.0.2.2/32 ! -i bridge1 -j DROP

这条规则由 filterDirectAccess 在端点(容器)创建时加入 raw-PREROUTING 链,作用是阻断远程主机绕过端口映射、直接路由到容器 IP 的访问——只有从 bridge 接口进入(-i bridge1)的合法流量放行,其余一律 DROP,且发生在 conntrack 之前。这与 filter-DOCKER 链里的每网络 DROP 形成双保险:raw 表拦截的是"直接路由到容器地址"的包(无论目的端口),filter 表 DROP 拦截的是经 NAT 后未被每端口规则放行的包。

从源码注释(endpoint.go 第 34~49 行)还可以读出几条适用边界:

  • 网络为internalnat-unprotectedrouted模式时不添加该规则;
  • daemon 级开启 direct routing 或通过DOCKER_INSECURE_NO_IPTABLES_RAW=1禁用 raw 规则(如内核缺少相应支持)时,规则同样会被跳过/删除,见 rawRulesDisabled;
  • TrustedHostInterfaces列出的接口会额外插入 ACCEPT,使受信任接口与 bridge 本身同等对待;
  • 宿主对本机容器始终保留直连能力(来自 bridge 接口自身的包不受该 DROP 影响)。

此外,dropLegacyFilterDirectAccess 说明了一段版本演进:28.0.0 曾引入按端口的 filter 直连 DROP 规则;自 28.2.0 起改为不按端口的 raw-PREROUTING DROP(规则更少,且可与端点同时创建),旧规则在每次端口发布时被删除,该清理函数计划在后续版本移除。

6. 规则 -> 源码定位速查

场景中的规则产生时机源码位置
filter-DOCKER 每端口ACCEPT(链首)容器端口发布时setPerPortForwarding
filter-DOCKER 每网络DROP(链尾)网络创建时setDefaultForwardRule
nat-DOCKER 每端口DNAT容器端口发布时setPerPortNAT
nat-POSTROUTING 每网络MASQUERADE网络创建时setupNonInternalNetworkRules
nat-PREROUTING/OUTPUTdst-type LOCAL跳转驱动初始化时addNATJumpRules
filter-DOCKER-CT / DOCKER-BRIDGE 规则网络创建时setupIPTables
filter-DOCKER-FORWARD 出站ACCEPT网络创建时setupNonInternalNetworkRules
raw-PREROUTING 容器地址DROP端点创建时filterDirectAccess

7. 文档生成机制:测试即文档

这套文档的可靠性来自 TestBridgeIptablesDoc 的完整流水线:

  1. 隔离环境:通过networking.NewL3Segment搭建 L3 测试段(192.168.124.0/24 与 IPv6 前缀),每个 section 一个独立 netns "宿主",并ip link set eth0 down降低随机报文干扰计数;
  2. 启动真实 daemon:在每个 netns 内以 busybox 镜像启动 daemon(禁用 OTEL 导出,swarm 场景附加--swarm-default-advertise-addr);
  3. 执行场景:按 index 中声明的网络/容器/端口映射创建资源;
  4. 捕获 iptables:runIptables 先iptables -Z清零计数,再分别执行iptables -vL --line-numbers -t filter/nat/rawiptables -S -t filter/nat/raw六组命令(常量定义见 iptCmds),并用正则把\d+ packets, \d+ bytes统一替换为0 packets, 0 bytes以消除 CI 波动,然后缩进 4 空格嵌入 markdown;
  5. 模板渲染与 golden diff:generate 用 templates/usernet-portmap.md 这类 Gotext/template(其中{{index . "LFilter4"}}{{index . "SNat4"}}等占位符对应上述命令输出)渲染文档,golden.Assertgenerated/下同名文件比对,不一致则测试失败。

前置约束:测试在 firewalld 运行中、rootless 模式或 nftables 后端下会跳过(第 216~218 行)。规则变更后,按包注释流程:检查 diff 中的规则变化、更新对应模板文件的描述、再以TESTFLAGS='-update'重新运行以刷新 golden 文档。

8. 小结与使用提示

  • 阅读本文档集前请记住其免责声明:规则结构随版本变化,不属于稳定接口,不要基于具体规则顺序编写外部脚本;
  • 阅读generated/下任何场景文档时,可先对照 index.md 的场景清单确认前置状态(例如usernet-portmap依赖 daemon 已有默认docker0网络,故 nat-POSTROUTING 中出现172.17.0.0/16的 MASQUERADE);
  • 端口发布的三张表分工可概括为:nat 表负责"改写"(DNAT/MASQUERADE),filter 表负责"放行与默认拒绝"(每端口 ACCEPT + 每网络 DROP + conntrack 回程),raw 表负责"在连接跟踪之前屏蔽容器地址直连";
  • 若想验证本地规则与文档一致,可执行iptables -vL --line-numbers -t filter/nat/rawiptables -S对照本文第 3~5 节;若规则顺序不同,优先检查是否发生过 daemon 重启或 firewalld 重载。

【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 6:43:46

小程序K线图绘制实战:HQChart从集成到落地全指南

简介&#xff1a;基于HQChart-master的微信小程序股票图表开发源码包&#xff0c;面向需要在小程序中实现沪深/港股K线图、实时走势图及通达信语法指标解析的开发者。压缩包共72个文件&#xff0c;大小仅1.14MB&#xff0c;以js逻辑代码为主&#xff0c;辅以wxml/wxss界面文件、…

作者头像 李华
网站建设 2026/9/7 6:43:03

基于Java Web的校园社团活动管理系统设计与实现

1. 项目背景与意义随着高校社团数量和学生参与度的不断提升&#xff0c;传统的人工管理方式在社团活动组织、成员信息维护、活动报名统计等方面暴露出效率低、易出错、信息不透明等问题。校园社团活动管理系统旨在通过信息化手段&#xff0c;为社团管理员、社团负责人和普通学生…

作者头像 李华
网站建设 2026/9/7 6:38:22

HeteroOpt:面向异构硬件的深度学习计算图全局多目标调度框架

在异构硬件上跑深度学习任务&#xff0c;调度问题永远是绕不开的硬骨头。我这些年经手过不少训练推理项目&#xff0c;从单机多卡到集群部署&#xff0c;最头疼的往往不是模型本身&#xff0c;而是怎么把计算图里那几十上百个算子合理地分配到不同设备上。你手里的硬件资源越“…

作者头像 李华
网站建设 2026/9/7 6:38:20

多模型SDK接入实战:统一网关架构与踩坑避坑指南

前阵子我们内部要做统一的 AI 能力中台&#xff0c;计划接入 3 家模型厂商的 SDK。我在技术选型阶段想得挺简单——各家不都兼容 OpenAI 风格吗&#xff1f;真到自己动手把三家 SDK 全部接完&#xff0c;我才发现自己低估了“接 SDK”这三个字。真正让人崩溃的不是模型效果差多…

作者头像 李华
网站建设 2026/9/7 6:35:27

西门子PROFINET网络调试与诊断实战:从地址规划到故障排查

简介&#xff1a;这是西门子PRONETA专业调试诊断工具的资源包&#xff0c;面向工业自动化现场工程师与PROFINET网络运维人员&#xff0c;可在不连接CPU的情况下完成网络拓扑自动扫描和ET200分布式I/O快速测试&#xff0c;显著提升现场排障与调试效率。压缩包共718个文件&#x…

作者头像 李华
网站建设 2026/9/7 6:35:14

Kubernetes CPU limits 引发延迟尖刺的底层原理与替代方案

先说结论&#xff1a;在 Kubernetes 里给 Pod 设置 CPU limits&#xff0c;是生产环境里最常见的“好心办坏事”之一。CPU limits 不会像内存 limits 那样直接把 Pod 杀掉&#xff0c;但它会让应用的延迟出现莫名其妙的尖刺&#xff0c;并且越是在高并发、突发流量下问题越明显…

作者头像 李华