基于IPTABLES的DOCKER实现BRIDGE方式网络隔离与通信
发表于:2022-10-08 |
Docker提供了bridge, host, overlay等多种网络。同一个Docker宿主机上同时存在多个不同类型的网络。位于不同网络中的容器,彼此之间是无法通信的。

Docker容器的跨网络隔离与通信,是借助了iptables的机制。

我们知道,iptables的filter表中默认划分为INPUT, FORWARD和OUTPUT共3个链,详情请参考 iptables及其过滤规则。

Docker在FORWARD链中,还额外提供了自己的链,以实现bridge网络之间的隔离与通信。



1. Docker在iptables的filter表中的链

在2015.12之前,Docker只额外提供了DOCKER链。

DOCKER
在此之后,直到Docker 17.06.0(2017.6)之前的版本中,Docker提供了如下2个链:

DOCKER
DOCKER-ISOLATION
在Docker 17.06.0(2017.6)及之后,Docker 18.03.1(2018.4)及之前的版本中,Docker提供了如下3个链:

DOCKER
DOCKER-ISOLATION
DOCKER-USER
查看Docker的iptables如下:

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
DOCKER-USER  all  --  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           
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0           
在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
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [4:272]
:POSTROUTING ACCEPT [4:272]
:DOCKER - [0:0]
##-m表示使用扩展模块进行数据包匹配,到达本机的数据包,如果目标地址类型是本地局域网,则指向到DOCKER规则链
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER

-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER

##这条规则是为了使容器和外部网络通信
#将源地址为192.168.0.0/20的包(也就是从Docker容器产生的包),并且不是从docker0网卡发出的
#进行源地址转换,转换成主机网卡的地址。
-A POSTROUTING -s 192.168.0.0/20 ! -o docker0 -j MASQUERADE

-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]

##需要宿主机转发的数据包全部指向到DOCKER-USER规则链
-A FORWARD -j DOCKER-USER
##需要宿主机转发的数据包全部指向到DOCKER-USER规则链
-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
##处理所有由docker0输入接口过来的数据包,排除掉从docker0输出接口的数据包,指定到DOCKER-ISOLATION-STAGE-2规则链
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
##数据包直接返回
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
##处理所有由docker0输出接口过来的数据包,丢弃掉这些数据包
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
##数据包直接返回
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
##直接返回
-A DOCKER-USER -j RETURN
2. Docker的DOCKER链

仅处理从宿主机到docker0的IP数据包。

3. Docker的DOCKER-ISOLATION链

可以看到,为了隔离在不同的bridge网络之间的容器,Docker提供了两个DOCKER-ISOLATION阶段实现。

DOCKER-ISOLATION-STAGE-1链过滤源地址是bridge网络(默认docker0)的IP数据包,匹配的IP数据包再进入DOCKER-ISOLATION-STAGE-2链处理;

不匹配就返回到父链FORWARD。

在DOCKER-ISOLATION-STAGE-2链中,进一步处理目的地址是bridge网络的IP数据包,匹配的IP数据包表示该IP数据包是从一个bridge网络的网桥发出,到另一个bridge网络的网桥,这样的IP数据包来自其他bridge网络,将被直接DROP;

不匹配的IP数据包就返回到父链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重新加载
上一篇:
docker-compose搭建的Mysql主从复制
下一篇:
hexo博客插入图片与视频方法