拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Moby 网桥网络的 nftables 规则深解:禁用容器间通信(ICC)并发布端口的完整规则链

Moby 网桥网络的 nftables 规则深解禁用容器间通信ICC并发布端口的完整规则链【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby本文围绕 Moby 仓库中 usernet-portmap-noicc.md 这一自动生成文档展开完整讲解当一个用户自定义网桥网络设置enable_iccfalse且容器发布了端口时Docker Engine 在 nftables 后端下生成的ip docker-bridges表结构与规则语义结合 nftabler 源码 与文档生成测试 nftablesdoc_linux_test.go读者可以掌握 ICCInter-Container Communication开关在 nftables 层面的确切实现、发布端口的 DNAT/转发/MASQUERADE 完整数据流以及如何验证这些规则随代码演进的机制。场景还原等价的 docker 命令该文档描述的是一种典型的生产隔离场景在禁用容器间通信的网络中运行一个发布端口的容器。仓库文档中给出的等价命令是docker network create \ -o com.docker.network.bridge.namebridge1 \ -o com.docker.network.bridge.enable_iccfalse \ --subnet 192.0.2.0/24 --gateway 192.0.2.1 bridge1 docker run --network bridge1 -p 8080:80 --name c1 busybox关键参数含义与 bridge_linux.go 中的网络选项解析对应com.docker.network.bridge.namebridge1指定内核网桥接口名源码中该 label 定义见 labels.gocom.docker.network.bridge.enable_iccfalse即EnableICC选项同为 labels.go 中定义的com.docker.network.bridge.enable_icc关闭同网络内容器之间的直接通信在 bridge_linux.go 中经strconv.ParseBool解析后存入网络配置--subnet 192.0.2.0/24 --gateway 192.0.2.1显式 IPAM 网段与网关。注意文档使用的 192.0.2.0/24 属于 IANA 文档保留网段避免与真实网络冲突-p 8080:80将宿主机 8080 端口经 DNAT 映射到容器 c1 的 80 端口。容器在该网段中分配到地址192.0.2.2网关之后的第一个地址。完整规则表ip docker-bridges当上述网络与容器创建后Docker 以 nftables 后端写入的ip docker-bridges表结构如下与 生成文档 逐字一致此处完整保留table ip docker-bridges { map filter-forward-in-jumps { type ifname : verdict elements { docker0 : jump filter-forward-in__docker0, bridge1 : jump filter-forward-in__bridge1 } } map filter-forward-out-jumps { type ifname : verdict elements { docker0 : jump filter-forward-out__docker0, bridge1 : jump filter-forward-out__bridge1 } } map nat-postrouting-in-jumps { type ifname : verdict elements { docker0 : jump nat-postrouting-in__docker0, bridge1 : jump nat-postrouting-in__bridge1 } } map nat-postrouting-out-jumps { type ifname : verdict elements { docker0 : jump nat-postrouting-out__docker0, bridge1 : jump nat-postrouting-out__bridge1 } } chain filter-FORWARD { type filter hook forward priority filter; policy accept; oifname vmap filter-forward-in-jumps iifname vmap filter-forward-out-jumps } chain nat-OUTPUT { type nat hook output priority dstnat; policy accept; ip daddr ! 127.0.0.0/8 fib daddr type local counter jump nat-prerouting-and-output } chain nat-POSTROUTING { type nat hook postrouting priority srcnat; policy accept; iifname vmap nat-postrouting-out-jumps oifname vmap nat-postrouting-in-jumps } chain nat-PREROUTING { type nat hook prerouting priority dstnat; policy accept; fib daddr type local counter jump nat-prerouting-and-output } chain nat-prerouting-and-output { iifname ! bridge1 tcp dport 8080 counter dnat to 192.0.2.2:80 comment DNAT } chain raw-PREROUTING { type filter hook prerouting priority raw; policy accept; ip daddr 192.0.2.2 iifname ! bridge1 counter drop comment DROP DIRECT ACCESS } chain filter-forward-in__docker0 { ct state established,related counter accept iifname docker0 counter accept comment ICC counter drop comment UNPUBLISHED PORT DROP } chain filter-forward-out__docker0 { ct state established,related counter accept counter accept comment OUTGOING } chain nat-postrouting-in__docker0 { } chain nat-postrouting-out__docker0 { oifname ! docker0 ip saddr 172.17.0.0/16 counter masquerade comment MASQUERADE } chain filter-forward-in__bridge1 { ct state established,related counter accept iifname bridge1 counter drop comment ICC ip daddr 192.0.2.2 tcp dport 80 counter accept counter drop comment UNPUBLISHED PORT DROP } chain filter-forward-out__bridge1 { ct state established,related counter accept counter accept comment OUTGOING } chain nat-postrouting-in__bridge1 { } chain nat-postrouting-out__bridge1 { oifname ! bridge1 ip saddr 192.0.2.0/24 counter masquerade comment MASQUERADE } }从整体设计看这张表把每个网桥docker0与用户自定义的bridge1抽象为一组同名前缀的链并通过四个ifname : verdict类型的 nftables 动态映射vmap在 hook 链中按接口名分发filter-FORWARDhook按出接口查filter-forward-in-jumps、按入接口查filter-forward-out-jumps。注意其语义方向——filter-forward-in__bridge链处理“进入该网桥”的转发流量即以该网桥为出接口的包filter-forward-out__bridge链处理“离开该网桥”的流量nat-POSTROUTINGhook同理经两个 postrouting 映射分发到各网桥的源地址转换链nat-PREROUTING与nat-OUTPUT汇聚到nat-prerouting-and-output执行 DNATraw-PREROUTINGhook在连接跟踪之前丢弃对容器地址的“直接访问”。这种“hook 链只做分发、每个网桥独立一组策略链”的结构让增删网络时无需触碰全局链只需要增删映射元素和该网桥的专用链。核心差异ICC 规则由 accept 变为 drop文档明确说明与 ICC 启用时的规则 相比绝大多数规则完全相同唯一的实质区别在filter-forward-in链对“来自本网桥自身”流量的裁决。ICC 启用时默认filter-forward-in__bridge1中对应规则是放行chain filter-forward-in__bridge1 { ct state established,related counter accept iifname bridge1 counter accept comment ICC ip daddr 192.0.2.2 tcp dport 80 counter accept counter drop comment UNPUBLISHED PORT DROP }ICC 禁用后同一条规则改为丢弃chain filter-forward-in__bridge1 { ct state established,related counter accept iifname bridge1 counter drop comment ICC ip daddr 192.0.2.2 tcp dport 80 counter accept counter drop comment UNPUBLISHED PORT DROP }逐行解读该链按 nftables 自上而下的匹配顺序ct state established,related counter accept已建立/关联连接的包直接放行保证已发起会话的回包与相关流量不受影响iifname bridge1 counter drop comment ICC入接口是本网桥的包——即同网络内容器之间的流量——在 ICC 关闭时被丢弃。这条就是enable_iccfalse的唯一落点ip daddr 192.0.2.2 tcp dport 80 counter accept放行发往容器 192.0.2.2:80 的包。注意此时包的目的地址已经是 DNAT 改写后的容器端口 80且来源不是 bridge1 自身被第 2 条拦下的不会到达这里因此这条实际承接的是“经 DNAT 后进入网桥的端口映射流量”counter drop comment UNPUBLISHED PORT DROP其余目标端口未发布的包全部丢弃形成默认拒绝的收敛。对比同表中docker0的对应链可见语义差异docker0上iifname docker0 counter accept comment ICC默认网桥保持 ICC 开启而bridge1上为drop。也就是说 ICC 开关是逐网桥生效的用户自定义网络关闭 ICC 不会影响docker0上的行为。这一行为在源码层面可以对应到 nftabler/network.go构建filter-forward-in链时根据n.config.ICC选择iccVerdictaccept 或 drop规则统一写成counter verdict comment ICC。而 bridge_linux.go 在EnableICC为 false 时还会调整桥接 netfilter 调用行为如enableBrNfCallIptables的取值逻辑确保转发路径上由 nftables 规则主导裁决。从源码结构看ICC 开关除了改变该条规则的方向外还参与决定端口映射规则的写入方式这与“ICC 关闭时发布端口流量必须走 DNAT 专用 accept 规则”的表结构一致。对照 iptables 后端同一场景下DOCKER-FORWARD链中会出现bridge1 → bridge1 DROP与bridge1 → !bridge1 ACCEPT两条规则替代默认的放行规则详见 iptables 版本文档。两种后端裁决结果一致只是表达形态不同链跳转 vs. 动态映射 vmap。其余规则发布端口的完整数据流虽然本文档的主题是 ICC但要理解“为什么 ICC 关闭后发布端口仍然可达”需要把未改变的部分也纳入数据流入向 DNAT宿主机 8080 → 容器 80nat-prerouting-and-output链中iifname ! bridge1 tcp dport 8080 counter dnat to 192.0.2.2:80 comment DNAT从外部进入nat-PREROUTING目的地址为本地时或宿主机本地发起经nat-OUTPUT的流量只要入接口不是bridge18080 端口的流量被重写到容器地址 192.0.2.2:80。fib daddr type local的跳转条件保证只对目的地址为本机路由可达的流量执行 DNAT。禁止绕过 NAT 直连容器地址raw-PREROUTING中ip daddr 192.0.2.2 iifname ! bridge1 counter drop comment DROP DIRECT ACCESS外部主机不能直接访问 192.0.2.2该地址只在网桥网段内有效防止绕过端口映射机制直接打容器地址。raw 表优先级最高先于连接跟踪生效。这与默认网关模式nat的语义对应——usernet-portmap.md 中对同一规则的解释相同。转发放行与收敛DNAT 后的包进入filter-FORWARD按出接口分发到filter-forward-in__bridge1命中第 3 条ip daddr 192.0.2.2 tcp dport 80 counter accept后放行至容器。回程包则命中各链首条ct state established,related counter accept。出站 MASQUERADEnat-postrouting-out__bridge1中oifname ! bridge1 ip saddr 192.0.2.0/24 counter masquerade comment MASQUERADE容器访问外部网络时源地址 192.0.2.0/24 在离开bridge1后被改写为宿主机地址filter-forward-out__bridge1的counter accept comment OUTGOING则放行全部出站转发。同网络内容器互访若 ICC 打开不经过 MASQUERADE因为出接口仍是bridge1本身。ICC 关闭对“经宿主机中转”的通信的影响从规则集可以推断同网络两个容器之间的流量只有一种途径——直接经网桥转发被 ICC drop 规则拦截。即便容器 A 访问宿主机 IP 再由宿主机转发到容器 B其回包/转发路径同样受filter-forward-in__bridge1的 ICC 规则约束因此enable_iccfalse提供的是网络层面的硬隔离。规则的来源与再生成机制TestBridgeNftablesDoc生成文档 首行的DO NOT EDIT注释表明它不是手写产物。整个 nftablesdoc 目录由集成测试 TestBridgeNftablesDoc 驱动生成测试为每个场景分配独立网络命名空间L3Segment 下的“dockerN”主机在其中启动一个真实 dockerd依次执行docker network create/docker run——本场景对应 测试索引 中的usernet-portmap-noicc.md条目noICC: true 8080:80 端口映射网段 192.0.2.0/24随后用nft -s list table ip docker-bridges抓取真实规则见 runNftables按 map/chain 切块存入键值表再经 text/template 渲染为 Markdown渲染结果与generated/下的 golden 文件 diff不一致则测试失败。因此本文引用的每一条规则都是“daemon 实际写入内核 nftables 的内容”而不是推测。运行前提需注意测试在 firewalld 运行中skip.If(t, networking.FirewalldRunning(), ...)、rootless 模式、或防火墙后端不是 nftables 时会跳过所以该文档反映的是nftables 后端firewallDrivernftables下的规则形态。若规则随代码演进发生变化需检查 diff、同步修改模板描述并以-update标志重跑测试刷新 golden 文档该流程在 nftablesdoc_linux_test.go 包注释中有说明。另外index.md 中有两条重要的适用范围声明其一Docker 的 nftables 规则结构是开发用途参考跨版本不是稳定接口不应作为依赖其内部链名的自动化基础其二IPv6 规则遵循与 IPv4 相同的模式只是位于ip6 docker-bridges表故文档只展示 IPv4。表在每次 Docker 启动时重建filter-INPUT/filter-OUTPUThook 未被 Docker 使用宿主侧流量统一经由filter-FORWARD裁决。小结把这张表当排障清单结合本文档运维人员可以在 nftables 后端的宿主机上用nft -s list table ip docker-bridges对照排查端口映射不通依次确认nat-prerouting-and-output的 DNAT 规则、filter-forward-in__bridge中对应的ip daddr 容器IP tcp dport 容器端口 counter accept规则、以及raw-PREROUTING是否误拦默认只 drop 外部直连容器地址不影响 DNAT 路径counter计数可定位包在哪一跳消失容器间访问被拒确认filter-forward-in__bridge中comment ICC规则是accept还是drop它由com.docker.network.bridge.enable_icc选项决定修改需重建网络网桥选项在创建后不可变更容器无法出网检查filter-forward-out__bridge的OUTGOING规则与nat-postrouting-out__bridge的MASQUERADE规则是否成对存在。这套“文档即测试产物”的组织方式使 nftablesdoc 中每个场景新 daemon、循环回环地址、无用户态代理、internal 网络、routed/nat-unprotected 网关模式、swarm 等的规则都能与实际内核状态逐字节对齐是理解 Moby 网桥防火墙行为的权威参考。【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门