Lazy loaded image
Lazy loaded image解决 Docker 与 UFW 防火墙的端口管理冲突
Words 2456Read Time 7 min
2025-4-17
2025-4-19
type
status
date
slug
summary
tags
category
icon
password
转载自Escape 原文链接:Docker防火墙管理 UFW(Uncomplicated Firewall)是一种用户友好的 Linux 防火墙管理工具,可以轻松地配置和管理系统的网络安全。对于 Docker 容器,使用 UFW 可以方便地管理容器的网络访问权限。 但在使用 Docker 的环境中,UFW(Uncomplicated Firewall)防火墙管理工具存在一个显著的问题:Docker  自动修改 iptables 规则,导致 UFW 无法有效管理Docker  容器发布的端口。这意味着即使 UFW 设置了阻止规则,Docker 仍可以暴露容器端口到公共网络,造成安全隐患。

1. 问题描述

现在的 Docker 防火墙存在什么问题?
UFW 是 Ubuntu 上很流行的一个 iptables 前端(毕竟 ufw 其实也是在操作 iptables 的),可以非常方便的管理防火墙的规则。但是当安装了 Docker 之后,UFW 就无法管理 Docker 发布出来的端口了。具体现象,如下所示:
  1. 在一个对外提供服务的服务器上启用了 UFW 进行防火墙管理,并且默认阻止所有未被允许的传入连接。
  1. 运行了一个 Docker 容器,并且使用 p 选项来把该容器的某个端口发布到服务器的所有 IP 地址上。
  1. UFW 将不会阻止所有对 8080 端口访问的请求,用命令 ufw deny 8080 也无法阻止外部访问这个端口。
这个问题其实挺严重的,这意味着本来只是为了在 内部提供服务的一个端口被暴露在公共网络上。在网络上搜索 ufw docker 可以发现很多的讨论,都是在说这个问题的。基本上可以找到的解决办法就是首先禁用 docker 的 iptables 功能,但这也意味着放弃了 docker 的网络管理功能,很典型的现象就是 容器将无法访问外部网络。在有的文章中也提到了可以在 UFW 的配置文件中手工添加一条规则,比如 -A POSTROUTING ! -o docker0 -s 172.17.0.0/16 -j MASQUERADE。但这也只是允许了 172.17.0.0/16 这个网络。如果有了新增的网络,我们也必须 手工再为新增的网络添加这样类似的 iptables 规则
因为 docker 的话,会新建一个 DOCKER 的链,如果容器使用 -p 绑定了端口,那么 docker 会自动去修改对应的 iptables 规则,所以从现象上来看就是 docker 在你的防火墙上打了一个洞。

2. 期望目标

现在有什么解决方式呢?
目前网络上的解决方案都非常类似,而且也不优雅,我希望一个新的解决方案可以:
  1. 不要禁用 Docker 的 iptables 功能,像往常一样由 Docker 来管理自己的网络。这样有任何新增的 Docker 网络时都无需手工维护 iptables 规则,也避免了在 Docker 中禁用 iptables 之后可能带来的副作用。
  1. 公共网络不可以访问 Docker 发布出来的端口,即使是使用类似 p 0.0.0.0:8080:80 的选项把端口发布在所有的 IP 地址上。容器之间、内部网络之间都可以正常互相访问,只有公共网络不可以访问。 虽然可以让 Docker 把容器的某一个端口映射到服务器的私有 IP 地址上,这样公共网络上将不会访问到这个端口。但是这个服务器可能有多个私有 IP 地址,这些私有 IP 地址可能也会发生变化。
  1. 可以很方便的允许公共网络直接访问某个容器的端口,而无需额外的软件和配置。就像是用 ufw allow 8080 这样允许外部访问 8080 端口,然后用 ufw delete allow 8080 就不再允许外部访问。

3. 解决方式

现在这个脚本也支持 Docker Swarm 集群了
  • 撤销原先的修改
如果已经按照目前网络上搜索到解决方案修改过了,请先修改回来。
  • 解决方法
目前新的解决方案只需要修改一个 UFW 配置文件即可,Docker 的所有配置和选项都保持默认。然后重启 UFW 服务(sudo systemctl restart ufw)。现在外部就已经无法访问 Docker 发布出来的任何端口了,但是容器内部以及私有网络地址上可以正常互相访问,而且容器也可以正常访问外部的网络。可能由于某些未知原因,重启 UFW 之后规则也无法生效,请重启服务器。
DEEPSEEK默认配置+以上配置
如果希望允许外部网络访问 Docker 容器提供的服务,比如有一个容器的服务端口是 80。这个命令会允许外部网络访问所有用 Docker 发布出来的并且内部服务端口为 80 的所有服务。请注意,这个端口 80 是容器的端口,而非使用 -p 0.0.0.0:8080:80 选项发布在服务器上的 8080 端口。
bash
如果有多个容器的服务端口为 80,但只希望外部网络访问某个特定的容器。比如该容器的私有地址为 172.17.0.2,就用类似下面的命令。
如果一个容器的服务是 UDP 协议,假如是 DNS 服务,可以用下面的命令来允许外部网络访问所有发布出来的 DNS 服务。
同样的,如果只针对一个特定的容器,比如 IP 地址为 172.17.0.2

4. 参考链接

送人玫瑰,手有余香!
上一篇
模板说明
下一篇
探针机部署哪吒监控 V1并套用Cloudflare CDN