个人服务器组网与防护实践(二):理解 Docker、iptables 与 UFW 的关系

原创 临窗旋墨 2026-11-05 阅读:12 分类: Linux运维 专题: Linux小主机实践 标签: 网络,安全,linux

# 个人服务器组网与防护实践(二):理解 Docker、iptables 与 UFW 的关系

上一篇打通了私有网络,随后遇到一个现象:Docker 发布的 Redis 端口,即使没有在 UFW 中放行,也可以访问。

原因在于数据包经过的路径:宿主机服务通常走 INPUT,Docker bridge 的发布端口经 DNAT 后走 FORWARD。只配置 UFW 入站规则,并不足以限制后一类流量。

本篇梳理判断规则所需的原理,下一篇给出实际防护脚本。

1. 几个组件各自做什么

  • Netfilter:Linux 内核中的报文处理框架,在收包、路由、转发等位置提供处理入口。
  • iptables:配置相关规则的命令行工具,负责匹配、过滤和地址转换等。
  • UFW:简化防火墙规则管理的工具,入站、出站、路由规则分别作用于不同路径。
  • Docker:为 bridge 网络自动配置网桥、虚拟网卡、路由和防火墙规则。

本文限定 Linux 上使用 iptables 后端的 Docker bridge 网络:

  1. iptables --version 中出现 nf_tables,可能只是 iptables 使用 nft 兼容层,不等于 Docker 已切换原生 nftables 后端。
  2. Docker 原生 nftables 后端不沿用本文的 DOCKER-USER 方案。
  3. host、macvlan、rootless 等网络模式不能直接套用本文的数据路径。

2. 表、链与规则顺序

2.1 表与链

本场景主要查看两张表:

  • filter:过滤报文,内置链包括 INPUT、FORWARD、OUTPUT。
  • nat:地址转换,内置链包括 PREROUTING、INPUT、OUTPUT、POSTROUTING。

其他常见表:mangle 用于修改报文属性,raw 可用于连接跟踪豁免等处理;本方案不需要修改它们。

常见路径位置:

  • PREROUTING:入站路由判断之前。
  • INPUT:交给宿主机本地进程。
  • FORWARD:由宿主机转发到其他目标。
  • OUTPUT:宿主机本地产生的报文。
  • POSTROUTING:完成路由判断、离开主机之前。

同名自定义链可出现在不同表中。例如 nat/DOCKER 处理端口转换,filter/DOCKER 参与访问放行;查看时要指定表。

2.2 规则顺序和动作

规则在链内按顺序检查,匹配后的行为取决于目标动作:

  • ACCEPT:在当前过滤处理处接受报文;后续其他处理环节仍可能影响它。
  • DROP:丢弃,不返回明确拒绝响应。
  • REJECT:拒绝并返回相应错误。
  • RETURN:退出当前自定义链,回到调用链继续处理。
  • 跳转到另一条链:进入该链,返回后再继续。

因此,DOCKER-USER 中的 RETURN 只是结束本链检查,之后仍要通过 Docker 的其他规则。

常用命令参数:

bash 复制代码
# -S 输出规则语法,便于确认条件和跳转顺序。
sudo iptables -S FORWARD
sudo iptables -S DOCKER-USER

# -n 不解析地址,-v 显示计数,--line-numbers 显示规则序号。
sudo iptables -L DOCKER-USER -n -v --line-numbers
  • -A:追加到链尾。
  • -I 链名 1:插入链首。
  • -C:检查相同规则是否存在。
  • -D:删除匹配规则或指定序号的规则。
  • -w:等待 xtables 锁释放,不是等待某条链创建。

3. 四类常见数据路径

以下是理解本场景的简化路径,实际还可能涉及其他表、连接跟踪和 Docker 版本差异。

3.1 外部访问宿主机服务

text 复制代码
网卡 → PREROUTING → 路由判断 → INPUT → 宿主机进程
  • 例如 apt 安装的 Nginx、宿主机 Redis、SSH。
  • UFW 的 allow in 规则用于此类本机入站访问。
  • 容器访问宿主机 Redis,同样需要考虑服务监听地址及宿主机入站规则;来源可能是自定义 Docker 网段。(即需要宿主机6379/tcp ALLOW IN 172.17.0.0/16)

3.2 外部访问 Docker 发布端口

以 40002:80 为例:

text 复制代码
网卡 → nat/PREROUTING → nat/DOCKER
     → DNAT:宿主机:40002 改为 容器IP:80
     → 路由判断 → filter/FORWARD → DOCKER-USER
     → Docker 后续转发规则 → 容器
  • DNAT 后目标变成容器,报文走 FORWARD,不会按宿主机服务的路径进入 INPUT。
  • Docker 的转发放行可能先于 UFW 的相关转发限制生效,单独添加 ufw deny 40002 不能可靠封住发布端口。
  • 这仍是内核按规则处理报文,并非 Docker 跳过了整个防火墙。
  • DOCKER-USER 是 Docker 为用户预留的转发过滤入口,位于其常规容器放行规则之前;它不处理全部宿主机流量。

3.3 容器访问外部服务

text 复制代码
容器 → 网桥 → FORWARD → POSTROUTING/MASQUERADE → 外网
回包 → 连接跟踪还原地址 → FORWARD → 容器
  • MASQUERADE 是一种源地址转换,将出站源地址改为出口地址。
  • conntrack 记录连接及 NAT 映射,后续报文据此处理;NAT 表通常在连接的首包建立转换关系。
  • ESTABLISHED,RELATED 用于匹配已有连接和相关流量。若将物理网卡入站转发一律丢弃而不保留回包,可能导致容器无法正常访问外部服务。

3.4 容器之间通信

  • 同一用户自定义 bridge 网络内,可使用服务名或网络别名访问容器端口,无需发布到宿主机。
  • 不同 bridge 网络默认有隔离;可让需要通信的容器加入共同网络,或明确设计其他访问路径。
  • 网桥、路由、Docker 内置 DNS 和过滤规则共同参与通信,不能将 Docker 所有网络能力都归结为 iptables。
  • 隔离链的名称和组织随版本变化,不应固定认定只有四条 Docker 链。

4. DNAT 后如何匹配宿主机端口

映射 40002:80 后,进入 DOCKER-USER 的报文目标端口已经是 80。此时直接匹配 --dport 40002 通常不会命中。

使用 conntrack 查询该连接原始方向的目标端口:

bash 复制代码
# 只查看具有该条件的规则是否存在,不添加规则。
sudo iptables -C DOCKER-USER -i enp1s0 -p tcp \
  -m conntrack --ctdir ORIGINAL --ctorigdstport 40002 -j RETURN
  • --ctdir ORIGINAL:匹配连接的原始请求方向。
  • --ctorigdstport 40002:匹配本机连接跟踪记录中的原始目标端口。
  • 如果前面还有家庭路由器做 NAT,这里看到的是路由器转发到小主机的端口,不一定是路由器公网端口。
  • conntrack 扩展匹配有额外开销,本方案用于个人小主机的少量端口规则。

5. WireGuard 为什么仍需要防火墙规则

WireGuard 流量分为两层:

  1. 外层加密报文访问云主机 51820/UDP。
  2. 解密后的内层报文,从 wg0 进入,源地址例如 10.10.0.3。

内层报文按目标继续分流:

  • 访问宿主机自身服务:走 INPUT,由本机入站规则控制。
  • 经云主机访问另一 Peer:走 FORWARD,需要 IP 转发和转发规则。
  • 访问 Docker 发布端口:经 DNAT 后走 Docker 转发路径。

因此,开放 UDP 51820 并不等于开放隧道内全部服务;而某个 Docker 端口无需 UFW 入站放行就能访问,也不能证明其他宿主机服务具有相同行为。

6. 排查与配置原则

bash 复制代码
# 发布地址和端口映射。
docker ps --format 'table {{.Names}}\t{{.Ports}}'

# 发布端口的 DNAT 规则。
sudo iptables -t nat -S DOCKER

# 转发链入口、自定义规则和命中计数。
sudo iptables -S FORWARD
sudo iptables -L DOCKER-USER -n -v --line-numbers

# 转发开关与 UFW 当前配置。
sysctl net.ipv4.ip_forward
sudo ufw status verbose
  1. 先区分宿主机服务、bridge 发布端口和 host 网络,再判断报文路径。
  2. 保留 Docker 自动管理 iptables,在 DOCKER-USER 添加本场景所需限制。
  3. 不清空 Docker 链,也不为解决 UFW 问题直接设置 "iptables": false;这会破坏 Docker 自动维护的 NAT 和过滤机制。
  4. 不需要跨主机访问的端口,可只发布到 127.0.0.1;需要访问的端口,再结合绑定地址、防火墙和认证配置。
  5. 127.0.0.1 属于当前网络命名空间,容器里的回环地址指向容器自己;不能简单归因于某条“禁止容器访问宿主机回环”的规则。
  6. IPv4 与 IPv6 分别检查,IPv4 规则不能保护 ::: 发布的 IPv6 入口。

理解这些路径后,防护分工就明确了:宿主机服务检查 UFW 入站规则,Docker 发布端口检查转发规则,公网入口再结合云安全组或家庭路由器控制。

评论区

avatar
未登录

请围绕文章内容交流,勿发布广告、违法信息或他人隐私。

暂无评论,来发表第一条评论吧~