解决ufw与docker共存的矛盾

解决ufw与docker共存的矛盾

同时安装ufw和docker后,你或许遇到过ufw对docker不生效的问题。针对这个问题,网上大部分的方法是修改/etc/docker/daemon.json,然而这并不能彻底解决问题。

问题分析

docker在创建容器的时候,会修改iptables来打开端口和创建转发规则(bridge模式)。然而,ufw同样基于iptables,并且并不会处理docker创建的规则。因此,docker会把端口开放,并且不会显示在ufw中,就像这样:

docker-dig-ufw.webp

使用dall-e 3绘制

通过修改/etc/docker/daemon.json,可以让docker不再去碰iptables,然而由于bridge模式下需要通过iptables进行流量转发,如果不修改iptables,会导致容器内部无法联通外网。

在经过一番查找后,发现了这个仓库:https://github.com/chaifeng/ufw-docker

经过实测后,发现确实可以起到作用。

修复方法

编辑/etc/ufw/after.rules

sudo vim /etc/ufw/after.rules

然后在末尾加上这些:

# 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

-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

-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN

-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

-A DOCKER-USER -j RETURN

-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

这里虽然只配置了172.16.0.0/12段,但是经过测试,对于处在其他网段的容器同样有效。

接着,重启ufw

sudo systemctl restart ufw

貌似有个bug,重启一次不会生效,多重启几次就好了。如果重启正常,执行这个命令的时候会卡一下,而不是立即结束。按照那个仓库的说法,可能需要重启机子才能生效,不过我并没有碰到。

如果需要放行端口,怎么办?

直接使用ufw allow是没有用的,需要添加一条转发规则

sudo ufw route allow proto tcp from any to any port {your_port}

在ufw status中应该是ALLOW FWD

当然,如果你的云服务商提供安全组,那还是用安全组吧,这东西真够难用的。