Docker的网络模式与端口屏蔽

6次阅读
没有评论

前言

因为最近要把一些容器的端口屏蔽掉

但是发现如果单纯去屏蔽 filter 的 INPUT 链的话

并不起作用

因此对 Docker 的网络模式进行一番探索

bridge 模式

就是桥接模式

主要是介绍一下这个模式

因为这个网络模式是 docker 默认的

也就是如果我想启动一个容器,例如

docker run -itd -p 6379:6379 redis

上面命令默认就使用的是 bridge 的网络模式

而桥模式是如何将端口的流量映射出来的呢?

这时候就轮到 iptables 上场了

首先看一下 nat 表的 iptables

[root@vbox ~]# iptables -vnL -t nat
Chain PREROUTING (policy ACCEPT 55 packets, 3331 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 87 packets, 6603 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 87 packets, 6603 bytes)
 pkts bytes target     prot opt in     out     source               destination
   55  3331 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:30010
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:30009
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:30008
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:30007
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:30006
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:30005
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:30004
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:30003
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:30002
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:30001
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:30000
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:7070
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:6060
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:443
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           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:30010 to:172.17.0.2:30010
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:30009 to:172.17.0.2:30009
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:30008 to:172.17.0.2:30008
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:30007 to:172.17.0.2:30007
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:30006 to:172.17.0.2:30006
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:30005 to:172.17.0.2:30005
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:30004 to:172.17.0.2:30004
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:30003 to:172.17.0.2:30003
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:30002 to:172.17.0.2:30002
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:30001 to:172.17.0.2:30001
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:30000 to:172.17.0.2:30000
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:7070 to:172.17.0.2:7070
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:6060 to:172.17.0.2:6060
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:443 to:172.17.0.2:443
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80 to:172.17.0.2:80

首先看 PREROUTING 链,所有入口的流量都被指向了 DOCKER 的链

端口映射,比如我的端口有 80:80 的映射需求

它对应的就是 Docker 链中的 0 0 DNAT tcp — !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80 这一行

直接将 80 使用 DNAT 指向了网桥中的容器 ip

而这个 ip 的路由是

[root@vbox ~]# ip route
default via 10.0.2.2 dev enp0s3
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1

也就是 172.17.0.0 的路由指向了 docker0 这个网桥

此时流量的目的 ip 已经不是主机本身的 ip

因此不会走 Filter 表中的 INPUT 和 OUTPUT 链

Docker 的网络模式与端口屏蔽

如上图,流量走的是最下面的一条路线

而不是从上面走过

而每创建一个 bridge 模式的容器

宿主机上都会出现一个名叫 veth 开头的网卡

如果他们之间想相互通信则通过链接 docker0 的网桥实现网络互通 t

不过即使 veth 网卡和容器内的 eth0 网卡是对应关系,但是他们的 MAC 并不相同(采用了 veth pair 技术,逻辑上相当于同一条线上的两个网卡)

原理如图

Docker 的网络模式与端口屏蔽

而根据 iptables 表

流量会被分发到 Filter 表的 forward 链中

让我们简单看一下 FORWARD 链里到底有什么

[root@vbox ~]# iptables -vn -L FORWARD
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
  337 30773 DOCKER-USER  all  --  *      *       0.0.0.0/0            0.0.0.0/0
  337 30773 DOCKER-ISOLATION-STAGE-1  all  --  *      *       0.0.0.0/0            0.0.0.0/0
   81 14642 ACCEPT     all  --  *      docker0  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
    0     0 DOCKER     all  --  *      docker0  0.0.0.0/0            0.0.0.0/0
  256 16131 ACCEPT     all  --  docker0 !docker0  0.0.0.0/0            0.0.0.0/0
    0     0 ACCEPT     all  --  docker0 docker0  0.0.0.0/0            0.0.0.0/0
    0     0 REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            reject-with icmp-host-prohibited

FORWARD 链的最上面会先将所有流量指向到 DOCKER-USER,再到 DOCKER-ISOLATION-STAGE-1

DOCKER-USER 这个链就是 docker 提供给用户,用来自定义操作的,这个链有特殊的配置,即使重启 docker 该链也不会被清理(放其他链里有被清理的风险)

DOCKER-ISOLATION-STAGE- 1 这个链则是负责去做容器之间的网络隔离

那么很明显,如我们想做端口屏蔽,在流量进来的这几个链中都可以做,比如 DOCKER、DOCKER-USER、DOCKER-ISOLATION-STAGE- 1 甚至 Filter 中的 FORWARD,但是 DOCKER 留给我们使用的是 DOCKER-USER

屏蔽端口

比如屏蔽 3306 端口,注意要放在 return 之前,要不然流量就返回了,无法达到效果

iptables -I DOCKER-USER -p tcp --dport 3306 -j DROP

参考链接

DOCKER-USER 和 DOCKER 链的那些事

一文讲清楚 docker 使用 iptables 实现容器网络的原理

理解 Docker 单机容器网络

正文完
 
评论(没有评论)
验证码