背景

最近在配置安全策略时,发现centos的经典防火墙firewall对docker暴露出去的端口不生效,明明没有将docker暴露的端口放到public域,docker暴露的端口仍然能被外部访问。更加严重的问题是,不在firewall配置的白名单中的ip也能访问。

原因

导致这个问题的原因是,docker和firewall都依赖底层的iptables进行流量的控制,而且DOCKER配置的规则在firewall配置的规则前面,导致firewall的白名单不生效。

解决方案

对于该问题,docker官方给出了详细说明,链接如下:
https://docs.docker.com/network/packet-filtering-firewalls

在这边篇官方说明中,提到了三种解决办法:

  1. 在iptables的DOCKER-USER链中增加自定义规则
  2. 禁止docker使用iptables
  3. 和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暴露出去的端口,也是无效的。

有兴趣的可以自行尝试。我测试是没有效果的。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐