本文发自 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已经存在了快三年了......
1F c 5 years, 5 months ago 回复
这不是docker绕过系统防火墙操作iptables,原因是docker网络是基于宿主机的,本身就只支持iptables。像firewalld之类的是后来在iptables上封装的,所以docker的网络规则会直接无视非iptables的防火墙,docker想做容器的网络操作也只能这么搞,他没法去动iptables这种系统防火墙,只能自己添加一个DOCKER规则。默认每次启动都会改掉防火墙,也无法灵活配置只允许cdn的ip访问容器的端口,因为你改了转发表,下次docker服务启动就会改回来。反正装了docker以后iptables表就被一通瞎b改。我一直在想docker技术真的有必要吗,好像对我来说只有一个不用去折腾镜像内的环境,但是维护网络的复杂抵消了这个优势,我觉得docker只有一个镜像环境不会污染宿主机的优点,其他没什么优点。快速大规模部署就是瞎扯淡,部完还得配网络环境
2F blacysy 5 years, 3 months ago 回复
确实很有点,想做控制估计要加入网络插件来实现了,那要上升到较为复杂层面, 看取舍了吧~
3F ff 5 years, 2 months ago 回复
这时docker说了,你不想暴露到外网可以在指定port的时候加127.0.0.1啊~
但问题是大家在使用时几乎不会手动指定为127.0.0.1,于是按照默认规则端口就被暴露到外网了,我觉得docker这种绕过系统防火墙直接操作iptables的做法非常不合理。
请教下,这里所谓的指定127.0.0.1 是不是在docker-compose.yml中
ports:
- 127.0.0.1:10000:1080/tcp
这样批定,貌似还是没有解决问题。
4F binss MOD 5 years, 2 months ago 回复
回复 [3F] ff:这里说的是 docker run 时 -p 127.0.0.1:8888:8888
5F ff 5 years, 2 months ago 回复
经过测试,在docker-compose.yml 中设置和run时-p 是一样的,用nmap扫描测试,确实可行。
6F mx 5 years, 2 months ago 回复
不想暴露,可以直接就不映射出来。容器间通信通过docker0桥。 但如果需要跨主机通信,必须要做nat规则
7F 你好 5 years, 2 months ago 回复
这样做有一个问题,容器是可以上网了,但是无法获取真正的的客户端ip,还需要做个iptables设置,我不会写
8F 一样的问题 4 years, 8 months ago 回复
容器里了不到真实ip,有知道的麻烦告知下.
9F plusls l 4 years, 6 months ago 回复
回复 [8F] 一样的问题:如果是http的话前面加个nginx,手动把源ip塞header里
或者自己用iptables配端口转发?
10F 11 3 years, 4 months ago 回复
centos的解决方法呢
11F 大拿 2 years, 5 months ago 回复
centos的解决方法呢??大佬
12F ck 2 years, 4 months ago 回复
centos的解决方法呢??大佬
13F 56d 1 year, 10 months ago 回复
回复 [7F] 你好:可以在容器外面加一个nginx或haproxy,容器里还塞一个nginx ,在nginx配置文件里启用 proxy_protocol 功能。你搜搜" proxy_protocol + nginx "