Docker提供了bridge, host, overlay等多种网络。同一个Docker宿主机上同时存在多个不同类型的网络,位于不同网络中的容器,彼此之间是无法通信的。
Docker容器的跨网络隔离与通信,是借助了iptables的机制。
iptables的filter表中默认分为INPUT, FORWARD和OUTPUT共3个链。
Docker在FORWARD链中(forward到自定义的链),还额外提供了自己的链,以实现bridge网络之间的隔离与通信。
Docker的基本网络配置
当 Docker 启动时,会自动在主机上创建一个 docker0
虚拟网桥,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机。它会在挂载到它的网口之间进行转发。
同时,Docker 随机分配一个本地未占用的私有网段中的一个地址给 docker0
接口。比如典型的 172.17.0.1
,掩码为 255.255.0.0
。此后启动的容器内的网口也会自动分配一个同一网段(172.17.0.0/16
)的地址。
当创建一个 Docker 容器的时候,同时会创建了一对 veth pair
接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即 eth0
;另一端在本地并被挂载到 docker0
网桥,名称以 veth
开头(例如 veth1
)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络。
1. Docker在iptables的filter表中的链
在Docker 18.05.0(2018.5)及之后的版本中,提供如下4个chain:
- DOCKER
- DOCKER-ISOLATION-STAGE-1
- DOCKER-ISOLATION-STAGE-2
- DOCKER-USER
目前,Docker默认对宿主机的iptables设置规则完整一览,在/etc/sysconfig/iptables文件中
##地址转发表nat中的规则链及默认
*nat
#PREROUTING规则链默认策略是ACCEPT
:PREROUTING ACCEPT [0:0]
#INPUT规则链默认策略是ACCEPT
:INPUT ACCEPT [0:0]
#OUTPUT规则链默认策略是ACCEPT
:OUTPUT ACCEPT [4:272]
#POSTROUTING规则链默认策略是ACCEPT
:POSTROUTING ACCEPT [4:272]
#DOCKER规则链默认策略是ACCEPT
:DOCKER - [0:0]
#######################在PREROUTING规则链中添加的规则###########################
##-m表示使用扩展模块进行数据包匹配,到达本机的数据包,如果目标地址类型是本地局域网,则指定到DOCKER链
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
#######################在OUTPUT规则链中添加的规则###########################
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
#######################在POSTROUTING规则链中添加的规则###########################
##这条规则是为了使容器和外部网络通信
#将源地址为192.168.0.0/20的包(也就是从Docker容器产生的包),并且不是从docker0网卡发出的
#进行源地址转换,转换成主机网卡的地址。
-A POSTROUTING -s 192.168.0.0/20 ! -o docker0 -j MASQUERADE
############################在DOCKER规则链中添加的规则###########################
#由docker0接口输入的数据包,返回到调用链;-i指定了要处理来自哪个接口的数据包
-A DOCKER -i docker0 -j RETURN
###############################################################################
##规则表中的链及默认策略
*filter
:INPUT DROP [4:160]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [59:48132]
:DOCKER - [0:0]
:DOCKER-ISOLATION-STAGE-1 - [0:0]
:DOCKER-ISOLATION-STAGE-2 - [0:0]
:DOCKER-USER - [0:0]
############################在FORWARD规则链中添加的规则###########################
##数据包全部指定到DOCKER-USER链
-A FORWARD -j DOCKER-USER
##数据包全部指定到DOCKER-ISOLATION-STAGE-1链
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
##由docker0接口输出的数据包,指定到DOCKER链
-A FORWARD -o docker0 -j DOCKER
##由docker0接口输入的数据包,且不是由docker0接口输出的数据包,允许通过
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
##由docker0接口输入的数据包,且由docker0接口输出的数据包,允许通过
-A FORWARD -i docker0 -o docker0 -j ACCEPT
####################在DOCKER-ISOLATION-STAGE-1规则链中添加的规则#################
##由docker0接口输入的数据包,且不是由docker0接口输出的数据包,指定到DOCKER-ISOLATION-STAGE-2链
##也就是要处理来自docker0的数据包,但是不是由docker0输出的数据包
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
##数据包直接返回到调用链
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
####################在DOCKER-ISOLATION-STAGE-2规则链中添加的规则#################
##由docker0接口输出的数据包,丢弃掉
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
##数据包直接返回到调用链
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
############################在DOCKER-USER规则链中添加的规则###########################
##直接返回到调用链
-A DOCKER-USER -j RETURN
2. Docker的DOCKER链
仅处理从宿主机到docker0的IP数据包。
3. Docker的DOCKER-ISOLATION链(隔离在不同的bridge网络之间的通信)
可以看到,为了隔离在不同的bridge网络之间的通信,Docker提供了两个DOCKER-ISOLATION阶段实现。
DOCKER-ISOLATION-STAGE-1链过滤源地址是bridge网络(默认docker0)的数据包,匹配的数据包再进入DOCKER-ISOLATION-STAGE-2链处理;
不匹配就返回到父链FORWARD。
在DOCKER-ISOLATION-STAGE-2链中,进一步处理目的地址是bridge网络(默认是docker0)的数据包,匹配的数据包表示该数据包是从一个bridge网络的网桥发出,到另一个bridge网络的网桥,这样的数据包来自其他bridge网络,将被直接DROP;
不匹配的数据包就返回到父链FORWARD继续进行后续处理。
4. Docker的DOCKER-USER链
Docker启动时,会加载DOCKER链和DOCKER-ISOLATION(现在是DOCKER-ISOLATION-STAGE-1)链中的过滤规则,并使之生效。绝对禁止修改这里的过滤规则。
如果用户要补充Docker的过滤规则,强烈建议追加到DOCKER-USER链。
DOCKER-USER链中的过滤规则,将先于Docker默认创建的规则被加载(在上面的规则一览中,DOCKER_USER链被最早APPEND到规则链中),从而能够覆盖Docker在DOCKER链和DOCKER-ISOLATION链中的默认过滤规则。
例如,Docker启动后,默认任何外部source IP都被允许转发,从而能够从该source IP连接到宿主机上的任何Docker容器实例。如果只允许一个指定的IP访问容器实例,可以插入路由规则到DOCKER-USER链中,从而能够在DOCKER链之前被加载。
示例如下:
#只允许192.168.1.1访问容器
iptables -A DOCKER-USER -i docker0 ! -s 192.168.1.1 -j DROP
#只允许192.168.1.0/24网段中的IP访问容器
iptables -A DOCKER-USER -i docker0 ! -s 192.168.1.0/24 -j DROP
#只允许192.168.1.1-192.168.1.3网段中的IP访问容器(需要借助于iprange模块)
iptables -A DOCKER-USER -m iprange -i docker0 ! --src-range 192.168.1.1-192.168.1.3 -j DROP
5. Docker在iptables的nat表中的规则
为了能够从容器中访问其他Docker宿主机,Docker需要在iptables的nat表中的POSTROUTING链中插入转发规则,示例如下:
iptables -t nat -A POSTROUTING -s 172.18.0.0/16 -j MASQUERADE
上述配置,还进一步限制了容器实例的IP范围,这是为了区分Docker宿主机上有多个bridge网络的情况。
6. Docker中禁止修改iptables过滤表
dockerd启动时,参数--iptables默认为true,表示允许修改iptables路由表。
要禁用该功能,可以有两个选择:
设置启动参数--iptables=false
修改配置文件/etc/docker/daemon.json,设置"iptables": "false";然后执行systemctl reload docker重新加载