容器的访问控制主要是通过iptables防火墙软件实现的
- 容器访问外部网络
容器的默认网关为docker0网桥的docker0接口,docker0接口也是宿主机的本地网络接口,如果容器需要访问到外部网络则需要宿主机进行辅助转发。
在宿主机上面需要打开以下设置:
Docker服务启动时会默认开启--ip-forward=true,自动配置宿主机系统的转发规则。sysctl -w net.ipv4.ip_forward=1 # 开启转发功能
假如容器内部的ip地址是172.17.0.2,本地网络地址是10.0.2.2.容器要能够访问外部网络,源地址不能使172.17.0.2,需要进行源地址映射(SNAT),修改为本地系统的IP地址10.0.2.2.
映射是通过iptables的源地址伪装操作实现的,查看主机nat表上POSTROUTING链的规则,该链负责数据包离开主机前,改写起源地址:➜ ~ iptables -t nat -nvL POSTROUTING Chain POSTROUTING (policy ACCEPT 260 packets, 22174 bytes) pkts bytes target prot opt in out source destination 48 3648 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0 0 0 MASQUERADE tcp -- * * 172.17.0.2 172.17.0.2 tcp dpt:6379 0 0 MASQUERADE tcp -- * * 172.17.0.3 172.17.0.3 tcp dpt:80
上述规则将所有源地址是172.17.0.0/16网络,且不是从docker0接口发出的流量(即从容器中出来的流量),动态伪装为从系统网卡发出。MASQUERADE行动与传统SNAT行动相比,好处是能动态地从网卡获取地址。
- 容器之间的访问
容器之间访问需要两方面的支持:- 网络拓扑是否已经连通,默认情况下,所有容器都会连接到docker0网桥上,这表示网络拓扑是互通的;
- 本地防火墙iptables规则是否允许访问通过
当启动Docker服务时,默认会添加一条允许转发策略到iptables的FORWARD链上,通过配置--icc=true|false实现,为了安全考虑,可以在Docker配置文件中指定DOCKER_OPTS=--icc=false来默认禁止容器间的访问,同时可以指定--iptables=false参数,不会修改宿主机上面iptables的规则。
iptables -nL
如果--iptables=true,表示Docker可以自主添加iptables规则,启动容器时使用--link=CONTAINER_NAME:ALIAS选项,Docker会在iptables中为两个互连容器分别添加一条ACCEPT规则,运行相互访问开放的端口。
- 外部网络访问容器
容器运行外部网络访问,可以在docker run的时候通过-p或-P参数来启用。其实是在本地的iptables的nat表中添加规则,将访问外部ip的包进行目的地址DNAT,将目标地址修改为容器的ip地址。
以一个开放的6379端口的redis容器为例,使用-p,本地的6379端口映射到容器的6379端口:
➜ ~ iptables -t nat -nvL
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 5 packets, 332 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT 5 packets, 332 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
0 0 MASQUERADE tcp -- * * 172.17.0.2 172.17.0.2 tcp dpt:6379
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:6379 to:172.17.0.2:6379
可以看到,nat表中设计两条链:PREROUTING链负责包到达网络接口时,改写目的地址,将所有流量转发到DOCKER链,而DOCKER链将所有不是从docker0进来的包(意外着不是本地主机产生),同时目标端口为6379的修改为目标地址是172.17.0.2,目标端口修改为6379