docker容器下的防火墙

3,681 阅读3分钟

笔者在旷视科技从事私有云平台开发,在工作中遇到了在服务器上防火墙无法阻止docker容器bridge模式下服务的问题。

背景

常见的linux防火墙方式为在iptablesINPUT链,设置白名单,端口或者ip sudo iptables -L INPUT -nv --line 如图所示, 防火墙对于10.199.1.26, 10.122.104.147, 10.199.1.25 三个为可通行ip, 以及docker0网卡的请求时可通行的。

docker-firewall.png

问题

服务器上如果docker container是以bridge模式启动,则防火墙规则失效。比如docker 启动bridge模式启动nginx,则防火墙无法阻拦。

原因

解释原因之前,先抛出一个问题,docker 运行如下命令 docker run -d -p 27111:80 nginx 是如何把服务器27111端口映射到容器内80端口的呢? 这一切还要从linux网络,以及docker 如何使用Linux网络说起。下图是linux网络经典图,linux的iptables对外暴露5链4表。 当请求发送到机器上,会先经过 PREROUTING链路,经路由判断后选择 INPUTFORWAR ,进入INPUT链路会走INPUT的处理规则,然后进入到上层协议栈,从OUTPUT链路返回,经POSTROUTING发出,进入FORWARD的链路也会经POSTROUTING发出。

linux-ipatbles.png Docker利用Linux iptables特性,会在PREROUTING链路创建DOCKER子链,运行 docker run -d -p 27111:80 nginx时,会在DOCKER链 nat表上做转发。如图所示:

docker-net.png 会把27111端口的请求转发到 172.17.0.11:80, 而172.17.0.11:80就是nginx container 的网络。 补充: docker是通过创建docker0虚拟网卡然后通过veth pair绑定到宿主机网卡:

一个完整的访问docker bridge container 服务网络请求为:

  1. 请求打到PREROUTING链,PREROUTING nat表做转发到docker container的网络里
  2. 请求经过FORWARD链,进入container服务
  3. 请求从container出来后还会走FORWARD链
  4. 走POSTROUTING链离开

所以INPUT链路的过滤规则无法约束到docker bridge模式的容器服务,就是因为docker 桥模式是在PREROUTING链路做转发。 查阅docker官方文档,发现docker提供DOCKER-USER链路来做辅助防火墙,DOCKER-USE链是挂载在FORWARD链上的子链,看起来可以在DOCKER-USE做过滤规则。但是新的问题来了,docker bridge每启动一个服务, 内部ip会有变化。比如启动一个Nginx 内部Ip为172.17.0.11,启动第二个Nginx ip为172.17.0.12。由于防火墙白名单通常为安全ip + 暴露端口等模式,那么依托DOCKER-USE的防火墙设计思路为:docker bridge 启动container,监听docker event事件,如果container监听的本机端口是暴露端口,那么DOCKER-USE添加端口,accept规则,同时添加安全ip的accept规则。非常的麻烦。

本人的设计思路如下:

由于我们的服务器不做路由转发,所以在PREROUTING链路做防火墙。 创建FIREWALL子链挂载到PREROUTING上DOCKER子链之前,设置安全ip, 端口ACCEPT规则

  1. 当请求到达机器时,如果是安全ip或者访问暴露端口,那么ACCPET或者RETURN
  2. 其他请求到达时,正常的操作是DROP掉,但是由于PREROUTING链路没有DROP操作,所以需要把这些请求DNAT到一个特殊ip比如129.129.129.129。
  3. 把FIREWALL挂载到FORWARD上,DOCKER子链之前,(iptables链路挂载准确的说是链路的某些表挂载到某些链路的某些表下),当访问特殊ip,如129.129.129.129 drop请求。

firewall-design.png

优缺点

优点: 简单有效

  1. 有效实现面对容器挑战下linux的防火墙实现
  2. 不需要动态更新Iptables

缺点

  1. 特殊ip不能和docker默认网段以及服务器网段重合

结束语

爱上写作