背景
最近在配置安全策略时,发现centos的经典防火墙firewall对docker暴露出去的端口不生效,明明没有将docker暴露的端口放到public域,docker暴露的端口仍然能被外部访问。更加严重的问题是,不在firewall配置的白名单中的ip也能访问。
原因
导致这个问题的原因是,docker和firewall都依赖底层的iptables进行流量的控制,而且DOCKER配置的规则在firewall配置的规则前面,导致firewall的白名单不生效。
解决方案
对于该问题,docker官方给出了详细说明,链接如下:
https://docs.docker.com/network/packet-filtering-firewalls
在这边篇官方说明中,提到了三种解决办法:
- 在iptables的DOCKER-USER链中增加自定义规则
- 禁止docker使用iptables
- 和firewall集成时,在firewall叫
docker
的zone中增加规则
经过实战,第二个和第三个不生效,只有第一个生效了,下面是具体情况。
在iptables的DOCKER-USER链中增加自定义规则
官方提到,在docker启动时,会自动在iptables中创建DOCKER链和DOCKER-USER链,其中DOCKER链的规则会被docker动态更新,我们不能在这里面增加规则;DOCKER-USER链可以增加自定义规则,docker保证所有的请求都会先过DOCKER-USER链的过滤,再由docker转发,因此,我们可以在DOCKER-USER链增加自定义规则。
可以在机器上执行 iptables -nvL
来查看规则,类似下面:
[root@localhost ~]# iptables -nvL ... Chain FORWARD (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 DOCKER-USER all -- * * 0.0.0.0/0 0.0.0.0/0 0 0 DOCKER-ISOLATION-STAGE-1 all -- * * 0.0.0.0/0 0.0.0.0/0 ...
可以看到在iptables的FORWARD链的最前面,被DOCKER增加了DOCKER-USER链。
DOCKER-USER链默认内容如下:
Chain DOCKER-USER (1 references) pkts bytes target prot opt in out source destination 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
在DOCKER-USER链增加的规则都要用iptables -I xxx
的方式插入到这条默认规则的前面,否则流量就出去了。
特别注意: 由于DOCKER-USER链的位置特别靠前,所有流量均会经过,在增加DROP或者DENY规则时,一定要控制范围(端口范围和源IP范围),否则可能导致整个机器的网络无法访问!
实战
我们的需求是增加针对暴露出去的80端口,增加访问白名单,即白名单以外的IP不能访问。白名单为192.168.88.0/24
。
我们的规则增加如下:
iptables -I DOCKER-USER -p tcp --dport 80 -j DROP iptables -I DOCKER-USER -s 192.168.88.0/24 -p tcp --dport 80 -j ACCEPT
使用 iptables -nvL
查看规则:
Chain DOCKER-USER (1 references) pkts bytes target prot opt in out source destination 0 0 ACCEPT tcp -- * * 192.168.88.0/24 0.0.0.0/0 tcp dpt:80 0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
由于-I是插入到最前面,所以最前面把DROP规则加上,后面再把白名单IP加上,可以保证仅有白名单的ip访问得到。
重启问题
机器重启时,iptables的所有规则会丢失,经实战测试,上面编写的规则也会丢失。通常做法是使用iptables-save工具保证开机自动增加规则,但是,在刚开机且docker服务未启动时,DOCKER-USER链还未创建,直接向里面增加规则会报错,因此,需要等待DOCKER服务启动后再增加,下面是一个简单的脚本示例:
PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin echo "startup docker iptables rules..." # 开机时直接sleep,等docker服务启动。这里实现比较简单,最好使用for循环一直查询docker服务状态。 sleep 120 # docker ps 在服务器启动中会卡住,直到服务启动成功。 docker ps # 增加规则 iptables -I DOCKER-USER -p tcp --dport 80 -j DROP iptables -I DOCKER-USER -s 192.168.88.0/24 -p tcp --dport 80 -j ACCEPT
保存脚本后,使用crontab设置开机自动执行即可。
禁止docker使用iptables
网络上的其他博客有的选择配置启动参数,让docker不使用iptables,经实战,该方案无效;而且,禁用掉iptables会导致docker的容器间网络受到影响,对于这个配置,官方的说明如下:
It is possible to set the iptables key to false in the Docker engine’s configuration file at /etc/docker/daemon.json, but this option is not appropriate for most users. It is not possible to completely prevent Docker from creating iptables rules, and creating them after-the-fact is extremely involved and beyond the scope of these instructions. Setting iptables to false will more than likely break container networking for the Docker engine.
其实就是不建议配置,而且不能完全保证docker创建iptables规则,但是一定会导致容器间网络受损。
和firewall集成时,在firewall叫docker
的zone中增加规则
在这边篇官方说明中,提到docker和firewall集成时,会在firewall创建一个叫docker
的zone,但是实战在这里面添加规则也无法阻断docker暴露出去的端口,也是无效的。
有兴趣的可以自行尝试。我测试是没有效果的。