本文发自 http://www.binss.me/blog/docker-pass-through-system-firewall/,转载请注明出处。

祸起

一直以来,我都对server暴露的端口进行了严格的限制,只开放少数对外服务的端口,像什么mysql这种端口肯定是不能开放的。

而各个平台都提供了方便的工具来设置防火墙,如Ubuntu的ufw,CentOS的firewalld。

我也很放心地用工具来配置防火墙,直到有一天发现:系统防火墙对docker根本不起作用!

可以看到docker直接在iptables开了几个大洞:

$ sudo iptables -L DOCKER
Chain DOCKER (2 references)
target     prot opt source               destination
ACCEPT     tcp  --  anywhere             172.18.0.2           tcp dpt:mysql
ACCEPT     tcp  --  anywhere             172.18.0.100         tcp dpt:8888
ACCEPT     tcp  --  anywhere             172.18.0.3           tcp dpt:https

瞬间冷汗就流了下来,是谁给你的胆子直接操作iptables的?愤怒的我取消了docker修改iptables的权限。

BAN

Ubuntu (upstart)

$ sudo vim /etc/default/docker

加入行

DOCKER_OPTS="--iptables=false"

重启

$ sudo restart docker

CentOS 7 (systemd)

$ sudo mkdir /etc/systemd/system/docker.service.d
$ sudo vim /etc/systemd/system/docker.service.d/docker.conf

加入行

ExecStart=
ExecStart=/usr/bin/dockerd --iptables=false

重启

sudo systemctl daemon-reload
sudo systemctl restart docker

修改完后,检验一下:

$ ps aux | grep docker | grep -v grep
root      2150  1.2  2.8 504416 28608 ?        Ssl  23:33   0:00 /usr/bin/dockerd --iptables=false

看到生效后我就愉快地洗洗睡了。

在处理了这个问题后,我逐渐忘了这件事。直至几天前对binsite进行升级时,发现为何有一些请求特别慢?

查看ufw的log

Jan 22 16:36:05 ubuntu kernel: [5528848.005381] [UFW BLOCK] IN=web OUT=eth0 PHYSIN=vethbfec0a1 MAC=02:42:70:f6:b9:bf:02:42:ac:12:00:02:08:00 SRC=172.18.0.2 DST=106.187.95.5 LEN=59 TOS=0x00 PREC=0x00 TTL=63 ID=3966 DF PROTO=UDP SPT=54112 DPT=53 LEN=39
...
Jan 22 17:13:44 ubuntu kernel: [5531106.548557] [UFW BLOCK] IN=web OUT=web PHYSIN=vetha2fb979 PHYSOUT=veth1c712b5 MAC=33:33:00:00:00:02:02:42:ac:12:00:03:86:dd SRC=fe80:0000:0000:0000:0042:acff:fe12:0003 DST=ff02:0000:0000:0000:0000:0000:0000:0002 LEN=56 TC=0 HOPLIMIT=255 FLOWLBL=0 PROTO=ICMPv6 TYPE=133 CODE=0

发现容器无法访问外网了,也无法进行容器间通信了,根据https://github.com/docker/docker/issues/4737,解决办法如下(Ubuntu):

启用封包转发

ufw default allow routed

添加转发规则:

sudo vim /etc/ufw/before.rules

在最后加上以下内容:

*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING ! -o net -s 172.18.0.0/16 -j MASQUERADE
COMMIT

net为当前网桥的名称,可通过ifconfig查看,172.18.0.0/16为网桥的子网段,可用过

docker network inspect net | grep Subnet

查看。

然后重启ufw:

sudo ufw disable
sudo ufw enable

吐槽

就像https://github.com/docker/docker/issues/4737里说到的那样,这已经不是什么小问题了,而是事关server安全的大问题。

这时docker说了,你不想暴露到外网可以在指定port的时候加127.0.0.1啊~

但问题是大家在使用时几乎不会手动指定为127.0.0.1,于是按照默认规则端口就被暴露到外网了,我觉得docker这种绕过系统防火墙直接操作iptables的做法非常不合理。

截止至目前版本,docker官方都没有要fix的意思,而这个issue已经存在了快三年了......