解决docker和ufw网络冲突问题
问题说明
UFW 无法管理 Docker 发布的端口。
具体现象是:
- 在一个对外提供服务的服务器上启用了 UFW,并且默认阻止所有未被允许的传入连接。
- 运行了一个 Docker 容器,并且使用 -p 选项来把该容器的某个端口发布到服务器的所有 IP 地址上。比如:docker run -d –name httpd -p 0.0.0.0:8080:80 httpd:alpine 将会运行一个 httpd 服务,并且将容器的 80 端口发布到服务器的 8080 端口上。
- UFW 将不会阻止所有对 8080 端口访问的请求,用命令 ufw deny 8080 也无法阻止外部访问这个端口。
这个问题其实挺严重的,这意味着本来只是为了在内部提供服务的一个端口被暴露在公共网络上。
期望的目标
- 不要禁用 Docker 的 iptables,像往常一样由 Docker 来管理自己的网络。这样有任何新增的 Docker 网络时都无需手工维护 iptables 规则,也避免了在 Docker 中禁用 iptables 之后可能带来的副作用。
- 公共网络不可以访问 Docker 发布出来的端口,即使是使用类似 -p 0.0.0.0:8080:80 的选项把端口发布在所有的 IP 地址上。容器之间、内部网络之间都可以正常互相访问,只有公共网络不可以访问。 虽然可以让 Docker 把容器的某一个端口映射到服务器的私有 IP 地址上,这样公共网络上将不会访问到这个端口。但是这个服务器可能有多个私有 IP 地址,这些私有 IP 地址可能也会发生变化。
- 可以很方便的允许公共网络直接访问某个容器的端口,而无需额外的软件和配置。就像是用 ufw allow 8080 这样允许外部访问 8080 端口,然后用 ufw delete allow 8080 就不再允许外部访问。
手动解决
参考原始文章:解决 UFW 和 Docker 的问题
使用 ufw-docker 工具解决
1. 撤销原来的修改
如果已经按照目前网络上搜索到解决方案修改过了,请先修改回来,包括:
- 启用 Docker 的 iptables 功能,删除所有类似 –iptables=false 的修改,包括 /etc/docker/daemon.json 配置文件。
- UFW 的默认 FORWARD 规则改回默认的 DROP,而非 ACCEPT。
- 删除 UFW 配置文件 /etc/ufw/after.rules 中与 Docker 网络相关的规则。
- 如果修改了 Docker 相关的配置文件,重启 Docker。稍后还要修改 UFW 的配置,可以一并重启。
2. 安装 ufw-docker 工具:
sudo wget -O /usr/local/bin/ufw-docker \
https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
chmod +x /usr/local/bin/ufw-docker
ufw-docker install
安装完成后可能需要重启docker,ufw.
3. 开放特定端口
如果希望允许外部网络访问 Docker 容器提供的服务,比如有一个容器的服务端口是 80。那就可以用以下命令来允许外部网络访问这个服务:
ufw route allow proto tcp from any to any port 80
这个命令会允许外部网络访问所有用 Docker 发布出来的并且内部服务端口为 80 的所有服务。
请注意,这个端口 80 是容器的端口,而非使用 -p 0.0.0.0:8080:80 选项发布在服务器上的 8080 端口。
如果有多个容器的服务端口为 80,但只希望外部网络访问某个特定的容器。比如该容器的私有地址为 172.17.0.2,就用类似下面的命令:
ufw route allow proto tcp from any to 172.17.0.2 port 80
如果一个容器的服务是 UDP 协议,假如是 DNS 服务,可以用下面的命令来允许外部网络访问所有发布出来的 DNS 服务:
ufw route allow proto udp from any to any port 53
同样的,如果只针对一个特定的容器,比如 IP 地址为 172.17.0.2:
ufw route allow proto udp from any to 172.17.0.2 port 53