Ubuntu 防火墙 UFW 对 Docker 容器端口的策略问题处理
原因
UFW(Uncomplicated Firewall)是 Debian 和 Ubuntu 系统自带的一个前端工具,便于用户管理防火墙规则。然而,由于 Docker 和 UFW 在操作 iptables 时采取不同的方式,二者之间存在不兼容的问题。
当 Docker 公开容器端口时,容器的流入和流出流量会在经过 UFW 防火墙规则之前被重定向。具体而言,Docker 使用 nat
表对容器流量进行路由,使得数据包在到达 UFW 监控的 INPUT
链之前就被处理。同样,流出的 OUTPUT
数据包也在 UFW 应用规则之前完成了路由操作,导致 UFW 的防火墙配置被绕过。
现在的目标是让 UFW 生效,同时尽可能少的修改 Docker 网络。
方案一
https://github.com/chaifeng/ufw-docker?tab=readme-ov-file#solving-ufw-and-docker-issues
该方案的优点是不需要修改 Docker 的网络配置。缺点是无论内网还是外网,想要访问容器暴露的端口,都需要进行一些额外的操作。
-
在
/etc/ufw/after.rules
末尾添加以下内容:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward
# [1]允许同一网络的容器可以互相访问,但也导致内网可以访问容器暴露的端口,如需更高安全性可注释
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16
# [2]允许容器进行 DNS 查询
-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
# [3]记录并拒绝所有发往内网 IP 的新 TCP 连接(SYN 包)和 UDP 流量
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
# [4]默认返回规则
-A DOCKER-USER -j RETURN
# [5]记录并丢弃非法连接尝试
-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP
COMMIT
# END UFW AND DOCKER -
重载配置:
1
2
3sudo systemctl daemon-reload && sudo ufw reload
# 如果不生效,执行以下命令
sudo /usr/lib/ufw/ufw-init flush-all && sudo service ufw restart && sudo service docker restart
配置后的效果
- 宿主机可以访问容器内端口。(与新增配置无关)
- 容器内无法访问宿主机的端口。(与新增配置无关)
- 同一 Docker 网络内的容器可以互相访问端口(如果注释了
[1]
部分的规则,则此功能将失效,解决方法请参考问题1)。 - 内网可以访问容器暴露的端口(如果注释了
[1]
部分的规则,则此功能将失效,解决方法请参考问题2)。 - 外网无法访问容器暴露的端口。(解决方法请参考问题2)
问题及解决方案
1. 容器无法访问同一 Docker 网络中的其他容器端口
1 | sudo ufw route allow from 172.18.0.0/16 to 172.18.0.0/16 |
或者直接在 /etc/ufw/after.rules
的 -A DOCKER-USER -j ufw-user-forward
后面添加一行:
1 | -A DOCKER-USER -j ufw-user-forward |
2. 无法访问容器映射的端口
当使用 -p 8080:80
映射端口时,应允许路由到容器端口 80
,而不是主机端口 8080
:
1 | sudo ufw route allow proto tcp from any to <容器ip> port <容器端口> |
同时需要确保主机的 8080
端口能够被外部访问:
1 | sudo ufw allow proto tcp from any to <主机ip> port <主机端口> |
方案二
虽然该方案需要修改 Docker 的网络配置,但不会有方案一中出现的问题,推荐使用。
-
禁用 Docker 的 iptables 管理:
1
sudo vim /etc/docker/daemon.json
添加以下配置:
1
{ "iptables": false }
-
修改 Docker 启动配置:
1
sudo vim /etc/default/docker
添加
-iptables=false
:1
DOCKER_OPTS="-iptables=false"
-
修改 UFW 配置:
1
sudo vim /etc/default/ufw
把
DEFAULT_FORWARD_POLICY
改为ACCEPT
:1
DEFAULT_FORWARD_POLICY="ACCEPT"
-
修改 UFW 规则:
1
sudo vim /etc/ufw/before.rules
在
*filter
前面添加下面内容:1
2
3
4
5
6
7*nat
:POSTROUTING ACCEPT [0:0]
# 访问外部网络时,将源地址改为主机的 IP 地址,只影响发往 docker0 网卡之外的流量
-A POSTROUTING ! -o docker0 -s 172.17.0.0/16 -j MASQUERADE5COMMIT
-A POSTROUTING ! -o [网卡名] -s [网卡所在网段] -j MASQUERADE5COMMIT
...
COMMIT -
重启 UFW 和 Docker:
1
sudo /usr/lib/ufw/ufw-init flush-all && sudo service ufw restart && sudo service docker restart