Docker与iptables及实现bridge方式网络隔离与通信操作
作者:yaoshengting
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重新加载
补充知识:docker网络模式之 default bridge模式
上文提到,docker的网络模式一共有五种,birdge 、host 、overlay、nacvlan、none、Network plugin 六种模式,这里主要介绍网桥(bridge)默认桥接模式。
一、简介
在网络概念中,桥接网络是一种链路层设备,它在网络段之间转发通信。网桥是运行在主机内核上的一个硬件设备或者软件设备。在docker中,桥接网络是使用软件桥接,连接到同一桥接网络上的容器直接可相互通信,而且是全端口的,而与未连接到该桥接网络的容器直接隔离,如此,桥接网络管理同一主机上所有容器的连接与隔离。docker的桥接驱动程序自动在主机上安装规则,同一网桥上的网络可相互通信,不同网桥网络容器相互隔离。
桥接网络适用于同一主机docker daemon生成的容器,对于不同主机daocker daemon的容器间交互,要么使用操作系统级的路由操作,要么使用overlay网络驱动。
当我们启动docker时,systemctl start docker, 一个默认的桥接网络(birbr0)将自动被创建,连接docker daemon 与宿主机器,同时创建一个网络docker0,后续生产的容器将自动连接到该桥接网络(docker0)上。我们可查看本地网桥 , 每创建一个容器,将新建一个桥接,连接容器与默认的桥接网络。
[root@localhost hadoop]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.0242b47b550d no virbr0 8000.52540092a4f4 yes virbr0-nic [root@localhost hadoop]#
网桥 virbr0 连接docker0与宿主机器,在网桥上创建了接口virbr0-nic,该接口接收docker0网络的数据。每生成一个容器,在docker0上新建一个接口,并且docker0的地址被设置成容器的网关。
[root@localhost hadoop]# docker run --rm -tdi nvidia/cuda:9.0-base [root@localhost hadoop]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9f9c2b80062f nvidia/cuda:9.0-base "/bin/bash" 15 seconds ago Up 14 seconds quizzical_mcnulty [root@localhost hadoop]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.0242b47b550d no vethabef17b virbr0 8000.52540092a4f4 yes virbr0-nic [root@localhost hadoop]#
查看本地网络 信息 ifconfig -a
[root@localhost hadoop]# ifconfig -a docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.0.1 netmask 255.255.240.0 broadcast 192.168.15.255 inet6 fe80::42:b4ff:fe7b:550d prefixlen 64 scopeid 0x20<link> ether 02:42:b4:7b:55:0d txqueuelen 0 (Ethernet) RX packets 37018 bytes 2626776 (2.5 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 46634 bytes 89269512 (85.1 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.252.130 netmask 255.255.255.0 broadcast 192.168.252.255 ether 00:25:90:e5:7f:20 txqueuelen 1000 (Ethernet) RX packets 14326014 bytes 17040043512 (15.8 GiB) RX errors 0 dropped 34 overruns 0 frame 0 TX packets 10096394 bytes 3038002364 (2.8 GiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 device memory 0xfb120000-fb13ffff eth1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 ether 00:25:90:e5:7f:21 txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 device memory 0xfb100000-fb11ffff lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 0 (Local Loopback) RX packets 3304 bytes 6908445 (6.5 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 3304 bytes 6908445 (6.5 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 oray_vnc: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1200 inet 172.1.225.211 netmask 255.0.0.0 broadcast 172.255.255.255 ether 00:25:d2:e1:01:00 txqueuelen 500 (Ethernet) RX packets 1944668 bytes 227190815 (216.6 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 2092320 bytes 2232228527 (2.0 GiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 vethabef17b: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::e47d:4eff:fe87:39d3 prefixlen 64 scopeid 0x20<link> ether e6:7d:4e:87:39:d3 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 8 bytes 648 (648.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 virbr0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255 ether 52:54:00:92:a4:f4 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 virbr0-nic: flags=4098<BROADCAST,MULTICAST> mtu 1500 ether 52:54:00:92:a4:f4 txqueuelen 500 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
docker 结合网桥与路由规则,设置同一宿主机器内,各容器间交互,docker 容器桥接网络驱动下,网络接入方式如下图所示:
如果启动容器时,指定了端口映射,将内部端口 80 映射到主机端口8080,也可0.0.0.0:8080方式,指定网卡,如下
docker run --rm -ti -p 8080:80 nvidia/cuda:9.0-base
然后查看路由表,
iptables -t nat -vnL
可看到增加了路由转发规则:
[root@localhost hadoop]# iptables -t nat -vnL Chain PREROUTING (policy ACCEPT 55 packets, 2470 bytes) pkts bytes target prot opt in out source destination 161K 8056K PREROUTING_direct all -- * * 0.0.0.0/0 0.0.0.0/0 161K 8056K PREROUTING_ZONES_SOURCE all -- * * 0.0.0.0/0 0.0.0.0/0 161K 8056K PREROUTING_ZONES all -- * * 0.0.0.0/0 0.0.0.0/0 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 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 3442 258K OUTPUT_direct all -- * * 0.0.0.0/0 0.0.0.0/0 0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 MASQUERADE all -- * !docker0 192.168.0.0/20 0.0.0.0/0 0 0 RETURN all -- * * 192.168.122.0/24 224.0.0.0/24 0 0 RETURN all -- * * 192.168.122.0/24 255.255.255.255 0 0 MASQUERADE tcp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535 0 0 MASQUERADE udp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535 0 0 MASQUERADE all -- * * 192.168.122.0/24 !192.168.122.0/24 3442 258K POSTROUTING_direct all -- * * 0.0.0.0/0 0.0.0.0/0 3442 258K POSTROUTING_ZONES_SOURCE all -- * * 0.0.0.0/0 0.0.0.0/0 3442 258K POSTROUTING_ZONES all -- * * 0.0.0.0/0 0.0.0.0/0 0 0 MASQUERADE tcp -- * * 192.168.0.3 192.168.0.3 tcp dpt:80 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:8080 to:192.168.0.3:80
默认的端口类型为 TCP。
二、容器间访问配置
首先启动两个容器,然后进入到容器内,查看个容器IP信息,
[root@localhost hadoop]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 462751a70444 nvidia/cuda:9.0-base "/bin/bash" 17 minutes ago Up 17 minutes 0.0.0.0:8080->80/tcp sad_heyrovsky 9f9c2b80062f nvidia/cuda:9.0-base "/bin/bash" 41 minutes ago Up 41 minutes quizzical_mcnulty [root@localhost hadoop]#
我这里启动两个容器,然后调用 docker inspect 容器ID 查看容器IP
docker inspect -f {{.NetworkSettings.IPAddress}} 容器ID
我们这两个容器 为 192.168.0.2 192.168.0.3
进入其中的一个容器, ping 另外一台机器,会发现,仅仅只能采用地址模式才能ping的通 ping 192.168.0.3,
docker exec -ti 9f9c2b80062f /bin/bash
如果采用在/etc/hosts内追加 别名, 然后ping 名字,发现无法ping通。
192.168.0.3 node1
至于原因,下篇将继续讲解用户自定义桥接网络,解决该问题。
以上这篇Docker与iptables及实现bridge方式网络隔离与通信操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。
您可能感兴趣的文章:
- docker的iptables策略详解和用户自定义策略的添加方式
- Docker iptables的错误解决
- docker的WARNING:bridge-nf-call-iptables is disabled的解决方案
- iptables使用及docker的iptables规则
- 解决docker安装完成报:bridge-nf-call-iptables is disabled问题
- Docker中iptables规则在iptables重启后丢失的完整过程
- 详解Docker使用Linux iptables 和 Interfaces管理容器网络
- 在Docker容器中使用iptables时的最小权限的开启方法
- iptables如何限制宿主机跟Docker IP和端口访问(安全整改)