十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Moby Docker Engine:禁用 userland-proxy 时用户定义桥接网络端口映射的 nftables 规则解析

Moby Docker Engine:禁用 userland-proxy 时用户定义桥接网络端口映射的 nftables 规则解析 Moby Docker Engine禁用 userland-proxy 时用户定义桥接网络端口映射的 nftables 规则解析【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby本文基于 moby 仓库中自动生成的 nftables 文档讲解以--userland-proxyfalse启动 Docker 守护进程后用户定义桥接网络user-defined bridge network上发布端口published port所生成的ip docker-bridgesnftables 表规则完整规则集、与启用 userland proxy 场景的三处关键差异以及每条差异规则在nftabler源码中的生成逻辑。读完你可以掌握该场景下 DNAT、hairpin masquerade 与 loopback 端口映射在内核态的处理方式并能对照源码验证规则来源。需要预先说明两点来自该文档集的 index.md该文档集面向开发用途——Docker 的 nftables 规则结构在各版本之间可能变化不是稳定接口文档由测试TestBridgeNftablesDoc通过真实启动 daemon、创建网络与容器、抓取 nftables 输出后与 templates/usernet-portmap-noproxy.md 模板合并生成并与 generated/usernet-portmap-noproxy.md 做 golden diff规则一旦变化测试即失败。IPv6 规则与 IPv4 规则模式相同只是位于不同的表ip docker-bridges与ip6 docker-bridges因此文档只展示 IPv4 规则表在每次 Docker 启动时都会重建。场景复现等效命令该场景等效于以下操作序列引自文档原文完整保留dockerd --userland-proxyfalse 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 busyboxdocker network create通过选项com.docker.network.bridge.name指定网桥名为bridge1并用--subnet 192.0.2.0/24 --gateway 192.0.2.1固定子网与网关192.0.2.0/24 是文档测试专用地址段见 nftablesdoc_linux_test.go 中的docNetworks定义docker run -p 8080:80把容器c1分配 IP 192.0.2.2的 80 端口发布到宿主机 8080 端口dockerd --userland-proxyfalse关闭用户态代理docker-proxy。该 flag 定义于 config_unix.go--userland-proxy描述为 Use userland proxy for loopback traffic默认值在 config_linux.go 中被初始化为true且默认路径会查找docker-proxy二进制。在集成测试中该场景由index里的section{ name: usernet-portmap-noproxy.md, noUserlandProxy: true, ... }描述测试据此以--userland-proxyfalse参数在独立网络命名空间内启动 daemon见 nftablesdoc_linux_test.go 的runTestNet。测试有明确的前提条件firewalld 未运行、非 rootless、且防火墙后端为 nftables。该场景的完整 nftables 规则启用 proxy 时绝大部分规则与 用户定义网络 发布端口启用 proxy 场景相同。本文档给出的完整ip docker-bridges表如下摘自 generated/usernet-portmap-noproxy.mdtable 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; 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 { 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 { fib saddr type local counter masquerade comment MASQUERADE FROM HOST } 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 accept 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 { fib saddr type local counter masquerade comment MASQUERADE FROM HOST ip saddr 192.0.2.2 ip daddr 192.0.2.2 tcp dport 80 counter masquerade comment MASQ TO OWN PORT } chain nat-postrouting-out__bridge1 { oifname ! bridge1 ip saddr 192.0.2.0/24 counter masquerade comment MASQUERADE } }整体结构可以按三层理解四个ifname : verdict跳转映射 filter-FORWARD/nat-POSTROUTING钩子链filter-FORWARD用oifname vmap filter-forward-in-jumps和iifname vmap filter-forward-out-jumps按出/入接口把包分派到每个网桥专属的链nat-POSTROUTING同理按iifname进入网桥方向与oifname离开网桥方向分派到nat-postrouting-in__bridge与nat-postrouting-out__bridge。每个网桥一对 filter 链如filter-forward-in__bridge1先放行established,related的已有连接iifname bridge1规则允许桥内容器间通信ICC针对发布端口放行ip daddr 192.0.2.2 tcp dport 80末尾counter drop comment UNPUBLISHED PORT DROP丢弃其余入向流量。NAT 相关nat-PREROUTING对目的为本地地址fib daddr type local的包跳转共享链nat-prerouting-and-output做 DNATnat-postrouting-out__bridge对离开网桥的容器网段流量做 masqueraderaw-PREROUTING中的DROP DIRECT ACCESS规则保证默认网关模式nat下容器 IP 不可从宿主机外部直接访问。另外index.md 说明filter-INPUT钩子不被 Docker 使用——来自宿主机物理网络或宿主机自身的包经路由进入桥接网络后命中的是filter-FORWARDfilter-OUTPUT同理。与启用 proxy 场景的三处关键差异文档的核心价值在于解释“禁用 userland proxy 后规则有什么不同”。这些差异都指向同一个设计事实没有 docker-proxy 兜底时原本由用户态代理处理的流量必须全部改由内核 NAT 规则处理。差异一nat-OUTPUT 对 loopback 目的地址也跳入 DNAT 链chain nat-OUTPUT { type nat hook output priority dstnat; policy accept; fib daddr type local counter jump nat-prerouting-and-output }文档原话nat-OUTPUT 到 nat-prerouting-and-output 的跳转对 loopback 地址也会发生目的是 DNAT“从某个网络发往、由另一网络中容器发布到 loopback 地址的端口”的包——因为没有 proxy 去捕获它们。对照启用 proxy 的场景见 generated/usernet-portmap.md同一链的规则是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 }即启用 proxy 时显式排除了127.0.0.0/8——因为发布在 loopback 上的端口由 docker-proxy 进程在用户态监听并转发内核 NAT 不再处理禁用 proxy 后-p 127.0.0.1:8080:80这类绑定只能靠内核 DNAT 完成于是排除条件消失。差异二DNAT 规则不再限制入向接口hairpin 由内核完成本场景chain nat-prerouting-and-output { tcp dport 8080 counter dnat to 192.0.2.2:80 comment DNAT }文档解释这条“宿主机端口 → 容器端口”的 DNAT 规则没有限制为“来自发布端口所在网络的包”同样因为没有 proxy 去捕获它们。对照启用 proxy 场景同一链是chain nat-prerouting-and-output { iifname ! bridge1 tcp dport 8080 counter dnat to 192.0.2.2:80 comment DNAT }启用 proxy 时iifname ! bridge1把来自bridge1内部的流量排除在外——容器访问宿主机上自己发布的端口所谓 hairpin/回绕访问由 docker-proxy 负责禁用 proxy 后这条内核 DNAT 规则对任何来源包括bridge1内的容器自身生效。这个“有无iifname ! bridge前缀”的开关在源码中由 port.go 的setPerPortDNAT直接生成func (n *network) setPerPortDNAT(pbs []types.PortBinding, updater func(nftables.Obj), ipv nftables.Family) { var proxySkip string if !n.fw.config.Hairpin { proxySkip fmt.Sprintf(iifname ! %s , n.config.IfName) } ... }只有当Hairpin为false即用户态 proxy 在运行时才加上iifname ! bridge跳过条件。而 “Hairpin” 与 userland proxy 的对应关系在仓库中有两处明确注释firewaller.go// Hairpin means the userland proxy will not be running.Hairpin 为真 ⇔ 用户态 proxy 不运行bridge_linux.go 构建防火墙配置时Hairpin: !config.EnableProxy因此--userland-proxyfalse会直接把防火墙层推进 “hairpin 模式”这正是上述规则差异的来源。差异三nat-postrouting-in 链新增 masquerade 规则文档指出本场景的nat-postrouting-in链带有针对“来自本地地址的包”的 masquerade 规则且bridge1即拥有发布端口容器的网桥还额外有一条“容器访问其发布在宿主机上的自身端口”的 masquerade 规则chain nat-postrouting-in__docker0 { fib saddr type local counter masquerade comment MASQUERADE FROM HOST } chain nat-postrouting-in__bridge1 { fib saddr type local counter masquerade comment MASQUERADE FROM HOST ip saddr 192.0.2.2 ip daddr 192.0.2.2 tcp dport 80 counter masquerade comment MASQ TO OWN PORT }对照启用 proxy 场景这两条nat-postrouting-in__*链是空的。两类规则的作用可以从规则组合推断MASQ TO OWN PORThairpin masquerade容器c1192.0.2.2访问宿主机 8080 端口时包被差异二的 DNAT 规则改写到192.0.2.2:80——目的就是容器自己。若不处理源地址 192.0.2.2 与目的 192.0.2.2:80 的“自己发给自己”连接无法正常建立与回程故对该连接做 masquerade改写源地址为网桥网关使回绕访问在纯内核路径下可用。这条规则由 port.go 的setPerPortHairpinMasq生成其注释明确写着“allows containers to access their own published ports on the host when hairpin is enabled (no docker-proxy)”且仅在n.fw.config.Hairpin为真时添加——与文档场景严格对应。MASQUERADE FROM HOSTfib saddr type local匹配“源为宿主机本地地址”的入桥流量并 masquerade。从规则组合看其意义在于禁用 proxy 后宿主机发起的连接包括经 nat-OUTPUT DNAT 的 loopback 端口映射到达容器时可能携带宿主机本地源地址如 127.0.0.1容器无法直接路由回应给这类地址改写为网桥网关源地址后回程路径才成立。启用 proxy 时docker-proxy 以宿主机的桥接网关地址直接连接容器因此不需要这条规则。与启用 proxy 场景保持一致的部分除上述三处差异外规则与 启用 proxy 的端口映射文档 一致主要包括发布端口的转发放行filter-forward-in__bridge1中的ip daddr 192.0.2.2 tcp dport 80 counter accept由 port.go 的setPerPortForwarding为每个端口绑定逐条生成注释说明了多宿主端口映射到同一容器端口时的去重策略。外部直达拦截raw-PREROUTING中ip daddr 192.0.2.2 iifname ! bridge1 counter drop comment DROP DIRECT ACCESS——因为网络采用默认网关模式nat容器 IP 不应从宿主机外部直接可达。出站 masqueradenat-postrouting-out__bridge1中oifname ! bridge1 ip saddr 192.0.2.0/24 counter masquerade使容器访问外部时源地址改写为网桥网关。默认桥 docker0 的 ICC 与端口丢弃filter-forward-in__docker0仅放行 established/related 与桥内 ICC其余丢弃UNPUBLISHED PORT DROP。文档生成机制与验证方式该文档不是手写的理解其生成机制有助于判断可信边界nftablesdoc_linux_test.go 的TestBridgeNftablesDoc在每个独立网络命名空间中启动一个 dockerdnoproxy 小节附加--userland-proxyfalse按section定义创建bridge1网络192.0.2.0/24网关192.0.2.1并运行映射80/tcp - 8080的容器然后执行nft -s list table ip docker-bridges抓取带计数器的规则输出runNftables函数把输出按 map/chain 拆块存入模板可引用的键如{{index . chain nat-OUTPUT}}、{{index . Ruleset4}}用 templates/ 下对应的text/template渲染出完整 markdown与generated/下的 golden 文件做 diff不一致则测试失败。文件头部也标注了!-- This is a generated file; DO NOT EDIT. --测试注释说明更新流程规则变化时先检查 diff再更新对应模板描述最后以TESTFLAGS-update重新生成参考文档。此外纯 Go 单元层面还有一组 nftables 规则生成的测试数据daemon/libnetwork/drivers/bridge/internal/nftabler/testdata/TestNftabler/ 下的 golden 文件按hairpin、gwmnat / nat-unprotected / routed、icc、bindlh、wsl2mirrored等参数组合覆盖了hairpintrue即无 proxy与各网关模式的规则快照可用来交叉核对本文规则。小结--userland-proxyfalse会把网桥防火墙切入 “hairpin 模式”源码中Hairpin: !config.EnableProxy原本由 docker-proxy 处理的三类流量全部落到内核 nftablesloopback 端口映射nat-OUTPUT不排除 127.0.0.0/8、跨网络经 loopback 发布端口的访问、以及容器回绕访问自身发布端口DNAT 不再排除iifname bridge1并配套MASQ TO OWN PORTmasquerade。同时nat-postrouting-in__bridge链新增MASQUERADE FROM HOST规则保证宿主本地地址发起的连接在容器侧有可用的回程路径。其余 ICC、发布端口放行、外部直达拦截与出站 masquerade 规则与启用 proxy 场景一致。该文档集为开发用途、非稳定接口引用规则时应以当前仓库的 generated/usernet-portmap-noproxy.md 与nftabler包测试 golden 数据为准。【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表