Solving the docker and ufw problem
背景与问题 (Docker 与 UFW 的冲突)
UFW 是在 Ubuntu 等系统中经常使用的防火墙管理工具。然而当在此类系统上安装并运行 Docker 时,会出现一个严重的安全隐患:Docker 会直接修改系统的 iptables 规则来路由它的网络流量,从而绕过 UFW 的限制。
具体表现为:你在 UFW 中配置了拒绝所有未授权的外部访问,但如果你启动
Docker 并使用 -p 选项映射端口(例如
docker run -p 8080:80),这个 8080
端口将直接暴露在公网上,任何人都能够访问。即便你运行
ufw deny 8080
也无法阻止外部访问。这意味着本来只想暴露在内网的服务可能会裸露在公网中,存在严重的安全性问题。
解决方案
网上常见的早期方案是禁用 Docker 的 iptables 支持(即设置
--iptables=false),但这样做的副作用很大,相当于放弃了
Docker 的网络管理功能,导致容器无法访问外网或难以与新建的 Docker
网络互通。
根据 chaifeng/ufw-docker 项目的推荐,我们可以在不改变 Docker 默认配置、保留 Docker 的网络管理能力的前提下解决这个问题。
核心思路是修改 UFW
的规则文件(/etc/ufw/after.rules),利用 iptables 的
DOCKER-USER 链来拦截并处理公网请求: 1.
阻断外部访问:默认阻止所有公网主动发起的流量访问 Docker
映射的端口。 2.
信任本地与内部网络:允许容器之间、私有局域网 IP
之间互访。 3.
精准开放端口:后续想要将特定服务暴露给公网时,不使用会导致主机端口一起开放的
ufw allow,而是使用 ufw route allow (例如
ufw route allow proto tcp from any to any port 80)来单独控制对容器端口的放行。
更详细的操作步骤与 after.rules 配置脚本可以直接参考 该项目文档。
允许 Tailscale 访问被拦截的 Docker 容器
虽然上述方案拦截了公网流量,但有时我们也需要从自己组建的虚拟局域网(例如
Tailscale)中访问这台机器上的 Docker 容器。由于 UFW 限制了非本地流量对
Docker 的路由访问,Tailscale 节点的请求进入 tailscale0
虚拟网卡时同样会被拦截。
此时,我们需要让 UFW 知道 tailscale0
是一个受信任的入站接口。可以使用以下命令来配置路由转发允许:
1 | |
执行后,属于你的 Tailscale 虚拟网络内的设备,就可以正常访问该服务器上暴露的 Docker 端口,兼顾了安全性与异地访问的便利性。