ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

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.namebridge1 \ --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/24、198.51.100.0/24、203.0.113.0/24)。测试通过 createBridgeNetworks 用 daemon client 以com.docker.network.bridge.name、com.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 ACCEPT3.1 规则链的转发路径数据面视角下,一条从外部主机进入容器 80 端口的转发包会经历:filter-FORWARD首先生效:DOCKER-USER链为空(留给用户自定义规则),随后进入DOCKER-FORWARD;DOCKER-FORWARD依次跳入DOCKER-CT(放行 conntrack 状态为RELATED,ESTABLISHED的回程/相关包,保证入站连接的双向放行不依赖 conntrack 之前的匹配)、DOCKER-INTERNAL(空)、DOCKER-BRIDGE(按出接口docker0/bridge1二次跳入DOCKER链),最后由按入接口排列的ACCEPT(第 4、5 条)放行容器出站流量;DOCKER 链完成核心判定:每端口ACCEPT与每网络DROP的先后顺序决定了只放行已发布端口。3.2 场景文档指出的三个关键变化创建bridge1 容器c1之后,相对新 daemon 状态(filter 表变化)要点如下,与原文档一致:在DOCKER-FORWARD链中,针对新网络的出站ACCEPT 规则(第 5 条,-i bridge1)被追加到链尾;DOCKER-CT和DOCKER-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参数在Hairpinfalse时追加(即默认情况),意味着从 bridge 接口本身进入的包不做 DNAT——容器之间或网关侧直连不会误触发端口重定向;POSTROUTING 的 MASQUERADE 规则:-s 子网 ! -o bridge -j MASQUERADE对每个启用了 Masquerade 的网络一条(本例为192.0.2.0/24与docker0的172.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 行)还可以读出几条适用边界:网络为internal、nat-unprotected或routed模式时不添加该规则;daemon 级开启 direct routing 或通过DOCKER_INSECURE_NO_IPTABLES_RAW1禁用 raw 规则(如内核缺少相应支持)时,规则同样会被跳过/删除,见 rawRulesDisabled;对TrustedHostInterfaces列出的接口会额外插入 ACCEPT,使受信任接口与 bridge 本身同等对待;宿主对本机容器始终保留直连能力(来自 bridge 接口自身的包不受该 DROP 影响)。此外,dropLegacyFilterDirectAccess 说明了一段版本演进:28.0.0 曾引入按端口的 filter 直连 DROP 规则;自 28.2.0 起改为不按端口的 raw-PREROUTING DROP(规则更少,且可与端点同时创建),旧规则在每次端口发布时被删除,该清理函数计划在后续版本移除。6. 规则 - 源码定位速查场景中的规则产生时机源码位置filter-DOCKER 每端口ACCEPT(链首)容器端口发布时setPerPortForwardingfilter-DOCKER 每网络DROP(链尾)网络创建时setDefaultForwardRulenat-DOCKER 每端口DNAT容器端口发布时setPerPortNATnat-POSTROUTING 每网络MASQUERADE网络创建时setupNonInternalNetworkRulesnat-PREROUTING/OUTPUTdst-type LOCAL跳转驱动初始化时addNATJumpRulesfilter-DOCKER-CT / DOCKER-BRIDGE 规则网络创建时setupIPTablesfilter-DOCKER-FORWARD 出站ACCEPT网络创建时setupNonInternalNetworkRulesraw-PREROUTING 容器地址DROP端点创建时filterDirectAccess7. 文档生成机制:测试即文档这套文档的可靠性来自 TestBridgeIptablesDoc 的完整流水线:隔离环境:通过networking.NewL3Segment搭建 L3 测试段(192.168.124.0/24 与 IPv6 前缀),每个 section 一个独立 netns 宿主,并ip link set eth0 down降低随机报文干扰计数;启动真实 daemon:在每个 netns 内以 busybox 镜像启动 daemon(禁用 OTEL 导出,swarm 场景附加--swarm-default-advertise-addr);执行场景:按 index 中声明的网络/容器/端口映射创建资源;捕获 iptables:runIptables 先iptables -Z清零计数,再分别执行iptables -vL --line-numbers -t filter/nat/raw与iptables -S -t filter/nat/raw六组命令(常量定义见 iptCmds),并用正则把\d packets, \d bytes统一替换为0 packets, 0 bytes以消除 CI 波动,然后缩进 4 空格嵌入 markdown;模板渲染与 golden diff:generate 用 templates/usernet-portmap.md 这类 Gotext/template(其中{{index . LFilter4}}、{{index . SNat4}}等占位符对应上述命令输出)渲染文档,golden.Assert与generated/下同名文件比对,不一致则测试失败。前置约束:测试在 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/raw与iptables -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),仅供参考
返回列表