Docker容器:网络以及容器安全
通过Namespace和Cgroups技术实现了容器进程的隔离,通过AUFS让容器拥有自己的“文件系统”;
网络相关背景知识
协议、接口和端口
网络、NAT和端口转发
网络虚拟化技术
Linux虚拟网络设备
Linux支持创建出虚拟化的设备,可以通过虚拟化设备的组合实现多种多样的功能和网络拓扑;
常见的虚拟化设备有Veth、Bridge、802.1q VLAN device、TAP;
Linux Veth
Veth是成对出现的虚拟网络设备,发送到Veth一端虚拟设备的请求会从另一端的虚拟设备中发出; 在容器的虚拟化场景中,经常会使用Veth连接不同的网络Namespace;
Linux Bridge
Bridge虚拟设备是用来桥接的网络设备,相当于现实世界中的交换机,可以连接不同的网络设备, 当请求到达Bridge时,可以通过报文中的Mac地址进行广播或转发;
sudo ip netns add ns1
sudo ip link add vth0 type veth peer name veth1
sudo ip link veth1 setns ns1
# 创建网桥
sudo brctl addbr br0
# 挂载网络设备
sudo brctl addif br0 eth0
sudo brctl addif br0 veth0
Linux路由表
路由表是Linux内核的一个模块,通过定义路由表来决定在某个网络Namespace中包的流向,从而定义请求会到那个网络设备上;
通过设置路由,对IP地址的请求就能正确的路由到对应的网络设备上,从而实现通信;
Linux iptables
iptables是对Linux内核的netfilter模块进行操作和展示的工具,用来管理包的流动和转送;
iptables定义了一套链式处理的结构,在网络包传输的各个阶段可以使用不同的策略对包进行加工、传送或者丢弃;
在容器虚拟化的技术中,经常会用到两种策略MASQUERADE和DNAT,用于容器和宿主机外部的网络通信;
MASQUERADE
iptables中的MASQUERADE策略可以将请求包中的源地址转换成一个网络设备的地址;
# 打开IP转发
sudo sysctl -w net.ipv4.conf.all.forwarding=1
# 对namespace中发出的包添加网络地址转换
sudo iptables -t nat -A POSTROUTING -s 172.18.0.0./24 -o eth0 -j MASQUERADE
在namespace中请求宿主机外部地址时,将namespace中的源地址转换成宿主机的地址作为源地址,就可以在namespace中访问宿主机外的网络了;
DNAT
DNAT策略也是做网络地址的转换,不过它是更换目标地址,经常用于将内部网络地址的端口映射到外部去;
sudo iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:80
这样就可以把宿主机上80端口的TCP请求转发到namespace中的地址172.18.0.2:80,从而实现外部的应用调用;
Docker网络基础
Docker本身的技术依赖于近年Linux内核虚拟化技术的发展,Docker对Linux内核的特性有很强的依赖;
Docker使用到的与Linux网络有关的主要技术包括:
-
Network Namespace
-
Veth设备对
-
Iptables/Netfilter
-
网桥
-
路由
network namespace网络命名空间
原理
为了支持网络协议栈的多个实例,Linux在网络栈中引入了network namespace, 这些独立的协议栈被隔离到不同的命名空间。
在Linux的network namespace内可以有自己独立的路由表及独立的Iptables/Netfilter设置来提供包转发 以及NAT、IP包过滤等功能;
Linux支持7种namespace:
-
cgroup用于隔离cgroup根目录;
-
IPC用于隔离系统消息队列;
-
Network隔离网络;
-
Mount隔离挂载点;
-
PID隔离进程;
-
User隔离用户和用户组;
-
UTS隔离主机名nis域名。
实践
创建一个network namespace:
ip netns add <name>
在命名空间执行命令:
ip netns exec <name> <command>
ip netns exec <name> bash
Veth设备对
网络命名空间代表的是一个独立的协议栈,相互隔离无法通信;通过Veth设备对可以打通;
veth设备都是成对出现,很像一对以太网卡
操作命令:
给两个network namespace配置“网线”:
创建对:
ip link add veth0 type veth peer name veth1
ip link show
把另外一个给到其他network namespace:
ip link set veth1 netns netns1
加上IP:
ip netns exec netns1 ip addr add 10.1.1.1/24 dev veth1
ip addr add 10.1.1.2/24 dev veth0
启动:
ip netns exec netns1 ip link set dev veth1 up
ip link set dev veth0 up
网桥
brctl addbr xxxx
brctl addif xxxx ethx
ifconfig ethx 0.0.0.0
ifconfig brxxxx xx.xx.xx.xx
iptables/netfilter
iptables-save
iptables-vnL
路由
ip route show table local type local
ip route list
netstat -rn
Docker网络的局限
没有考虑多主机互联的网络解决方案
容器跨主机网络
Linux Bridge没有办法路由到宿主机外部,所以不同宿主机Bridge上的容器还是得通过映射到端口的方式来实现互相访问;
跨主机容器网络通信的常见方式
封包
可以把容器之间的请求外面包装上宿主机的地址发送,这样跨主机的容器之间的通信就转换成了宿主机之间的通信,到达另外一个容器所在 的宿主机后再解开外面的包装,拿到真正的容器请求,就能实现跨主机的容器间通信了;
常见的封包技术有VxLan、Ipip-tunnel、GRE,或者自己的封包格式等,通过这些技术就可以将容器间的请求封装成宿主机间的请求;
对基础设施要求低,只需要宿主机之间能联通即可;但是性能损耗较大(带宽、资源);
路由
让宿主机的网络“知道”容器的地址要怎么路由、路由到哪台机器,这种方式一般要网络设备的支持,比如修改路由器的路由表, 将容器IP的地址的下一跳修改到这个容器所在的宿主机上;
常见的路由技术有路由器路由表、Vlan、各大云服务厂商的VPC路由表等;
无封包、性能好;但是对基础网络设施有要求,需要支持一些路由的配置或者特定的网络协议;