Linux网络虚拟化基石 network namespace

1 网络虚拟化基石 network namespace

Linux的namespace的作用就是"隔离内核资源".

在Linux的世界里,文件系统挂载点、主机名、POSIX进程间通信消息队列、进程PID数字空间、IP地址、userID数字空间等全局系统资源被namespace分割,、装到一个个抽象的独立空间里。

而隔离上述系统资源的namespace分别是Mount namespce、UTS namespace、IPC namespace、PID namespace、network namespace 和user namespace。

对于进程来说,要想使用namespace里面的资源,首先要“进入”到这个namespace,而且还无法跨namespace访问资源。

Linux的namespace给里面的进程造成了两个错觉:

(1) 它是系统里的唯一进程。

(2)它独享系统的所有资源。

默认情况下,Linux进程处在和宿主机相同的namespace,即初始的根namespace里,默认享有全局系统资源

network namespace,在Linux内核2.6版本引入,作用是隔离Linux系统的设备,以及IP地址、端口、路由表、防火墙规则等网络资源。

因此,每个网络namespace里都有自己的网络设备(如IP地址、路由表、端口范围、/proc/net目录等)。

也因此,容器里的进程可以放心地绑定在端口上而不必担心冲突,使得一个主机上同时运行多个监听80端口的web服务器变成可能。

1.1 初始network namespace

network namespace 的增删改查功能已经集成到Linux的ip工具netns命令中

# 创建network namepspace
[root@boc119 ~]# ip netns add netns1
# 在/var/run/netns 路径下生成一个挂载点,
# 挂载点作用
# (1)方便namespace管理
# (2)使namespace即使没有进程运行也能继续存在
[root@boc119 ~]# cd /var/run/netns/
[root@boc119 netns]# ls
netns1
# ip netns exec 命令进入,做一些网络查询配置工作
# 如下,查询网卡信息的命令,(只有一块系统默认的本地回环设备lo)
[root@boc119 netns]# ip netns exec netns1 ip link list
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@boc119 netns]# s
# 查询及删除
[root@boc119 netns]# ip netns list
netns1
[root@boc119 netns]# ip netns delete netns
Cannot remove namespace file "/var/run/netns/netns": No such file or directory
[root@boc119 netns]# ip netns delete netns1
[root@boc119 netns]# 
# 注意以上delete并不一定删除netns1只是移除了对应的挂载点,只要里面还有进程运行着,network namespace便一直存在

1.2 配置 network namespace

# 全新 network namespace 会附带创建一个本地回环地址,这个自带的lo设备状态还是DOWN的
# 因此,尝试访问本地回环地址时,网络也是不通的。
[root@boc119 netns]# ip netns exec netns1 ip link list
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@boc119 netns]# 
[root@boc119 netns]# ip netns exec netns1 ping  127.0.0.1
connect: Network is unreachable
[root@boc119 netns]# 

# 将设备状态设置UP,即可访问本地回环地址
[root@boc119 netns]# ip netns exec netns1 ip link set dev lo up
[root@boc119 netns]# ip netns exec netns1 ip link list
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@boc119 netns]# ip netns exec netns1 ping  127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.077 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.062 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.060 ms
^C
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.060/0.066/0.077/0.010 ms
[root@boc119 netns]# 

仅有一个本地回环设备是没法与外界通信的。如果我们想与外界(比如主机网卡)进行通信,

就需要在namespace里再创建一对虚拟的以太网卡,即所谓的veth pair。

veth pair 总是成对出现且互相连接,他就像Linux的双向管道(pipe),报文从veth pair 一端进去就会从另一端收到。

下面创建一对虚拟以太网卡veth0和veth1,然后把veth pair 的一端放到netns1 network namespace。

[root@boc119 netns]# ip link add veth0 type veth peer name veth1
[root@boc119 netns]# ip link set veth1 netns netns1
[root@boc119 netns]# 

默认,他们都在主机根 network namespace中,将其中一块虚拟网卡veth1通过ip link set 命令移动到netns network namespace中

那么,veth0和veth1之间能通信吗?

还不能,因为这两块网卡刚创建出来还都是DOWN的状态,需手动设UP。

与对lo网卡设置类似,只是多一步绑定IP地址。

# veth1 绑定ip
[root@boc119 netns]# ip netns exec netns1 ifconfig veth1 10.1.1.1/24 up
[root@boc119 netns]# ifconfig vet0 10.1.1.2/24 up
SIOCSIFADDR: No such device
vet0: ERROR while getting interface flags: No such device
vet0: ERROR while getting interface flags: No such device
SIOCSIFNETMASK: No such device
# veth0 绑定ip
[root@boc119 netns]# ifconfig veth0 10.1.1.2/24 up
[root@boc119 netns]# 

由此即可ping通veth pair的任意一头了

root@boc119 netns]# ifconfig veth0 10.1.1.2/24 up
[root@boc119 netns]# ping 10.1.1.1
PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data.
64 bytes from 10.1.1.1: icmp_seq=1 ttl=64 time=0.114 ms
64 bytes from 10.1.1.1: icmp_seq=2 ttl=64 time=0.048 ms
^C
--- 10.1.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.048/0.081/0.114/0.033 ms

[root@boc119 netns]# ip netns exec netns1 ping 10.1.1.2
PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.080 ms
64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.073 ms
^C
--- 10.1.1.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.073/0.076/0.080/0.009 ms
[root@boc119 netns]# 
[root@boc119 netns]# 

另外,不同namespace之间路由表和防火墙规则等也是隔离的。

因此 netns1 无法和主机共享路由表和防火墙。

测试:

[root@boc119 netns]# ip netns exec netns1 route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.1.1.0        0.0.0.0         255.255.255.0   U     0      0        0 veth1
[root@boc119 netns]# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         gateway         0.0.0.0         UG    100    0        0 eth0
10.1.1.0        0.0.0.0         255.255.255.0   U     0      0        0 veth0
link-local      0.0.0.0         255.255.0.0     U     1003   0        0 eth1
172.32.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
192.168.1.0     0.0.0.0         255.255.255.0   U     100    0        0 eth0
[root@boc119 netns]# 
[root@boc119 netns]# ip netns exec netns1 iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination      

netns1 空的路由表及iptables规则,意味着从netns1法包到因特网是徒劳的。

[root@boc119 netns]# ip netns exec netns1 ping www.baidu.com
ping: www.baidu.com: Name or service not known
[root@boc119 netns]# ping www.baidu.com
PING www.wshifen.com (103.235.46.39) 56(84) bytes of data.
64 bytes from 103.235.46.39 (103.235.46.39): icmp_seq=1 ttl=46 time=205 ms
^C
--- www.wshifen.com ping statistics ---
2 packets transmitted, 1 received, 50% packet loss, time 1001ms
rtt min/avg/max/mdev = 205.304/205.304/205.304/0.000 ms

netns1想连接因特网,有若干办法。

可以在主机根network namespace创建一个Linux网桥并绑定veth pair的一端到网桥上;

也可以通过适当的NAT(网络地址转换)规则并辅以Linux的IP转发功能(配置net.ipv4.ip_forward=1)

非root进程被分配到network namespace后只能访问和配置已经存在于该ns的设备。

root进程可以在ns里创建新的网络设备。

ns里的root进程还能把本ns的虚拟网络设备分配到其他ns。

# ip netns exec netns1 ip link set veth1 netns 1

理解:

(1) ip netns exec netns1 进入 netns1 namespace

(2)ip link set veth1 netns 1 把 netns1 network namespace 下的veth1网卡移到PID为1的进程(即init进程)所在的network namespace。

通常,init进程都在主机的根network naemspace下运行,因此上面命令就是把veth1从netns1 network namespace移动到系统根network namespace。

有两种途径索引network namespace: 名字(例如netns1)或者属于该namespace的进程PID,上文中用的就是后者。

对于 namespace的rooot用户而言,以上移动虚拟网络设备到其他network namespace,甚至主机根network namespace!这就带来潜在的安全风险。

如果用户希望屏蔽这一行为,则需要结合PID namespace和Mount namespace的隔离特性做到network namespace之间完全不可达。

1.3 network namespace API的使用

  1. 创建namespace的黑科技: clone系统调用

  2. 维持namespace存在:/proc/PID/ns 目录的奥秘

    每一个Linux进程都拥有属于自己的/proc/PID/ns,这个目录下的每个文件都代表一个类型的namespace。

[root@boc119 ns]# cd /proc/27236/ns
[root@boc119 ns]# ls
ipc  mnt  net  pid  user  uts
[root@boc119 ns]# 
# /proc/PID/ns 这个目录下的文件在3.8 Linux内核3.8之前都是硬链接(hard link),而且只有ipc、net和uts这三个文件
# 3.8 之后,每个文件都是一个特殊的符合链接文件
# 这些文件提供了操作进程关联namespace的一种方式
[root@boc119 ns]# ls -l
total 0
lrwxrwxrwx 1 root root 0 Jul  4 16:31 ipc -> ipc:[4026532928]
lrwxrwxrwx 1 root root 0 Jul  4 16:31 mnt -> mnt:[4026532959]
lrwxrwxrwx 1 root root 0 Jul  4 16:31 net -> net:[4026532244]
lrwxrwxrwx 1 root root 0 Jul  4 16:31 pid -> pid:[4026532960]
lrwxrwxrwx 1 root root 0 Jul  4 16:31 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Jul  4 16:31 uts -> uts:[4026531838]
[root@boc119 ns]# 
#符号链接

	#用途一: 确定某两个进程是否属于同一namespace。

 	#如果属于同一namespace下,那么这个两个进程/proc/PID/ns 目录下对应符号链接文件的inode数字([]内的数字)是一样的。
	 #也可以通过stat()系统调用返回结构体的st_info字段判断。

	 #用途二:只要这些文件描述符保持open状态,对应namespace就会一直存在,哪怕这个namespace里的所有进程都终止运行了。
 	#之前版本Linux内核想要保持namespace存在,需要在其中放一个进程(不一定是运行中的)
 	#现在Linux内核提供黑科技允许: 只要打开文件描述符,不需要进程存在也能保持namespace存在!

 怎么操作?
 
 touch /my/net 新建一个文件
 mount --bind /proc/$$/ns/net /my/net
 把/proc/PID/ns目录下的文件挂载起来就能起到打开文件描述符的作用,而且这个network namespace 会一直存在,直到/proc/self/ns/net被卸载。
  1. 往namespace里添加进程:setns系统调用
  2. 帮助进程逃离namespace:unshare系统调用

小结

通过Linux的network namespace 技术可以自定义一个独立的网络栈,

简单到只有loopback设备,复杂到具备系统完整的网络能力,这就使得network namespace成为Linux网络虚拟化技术的基石。

network namespace的另一个隔离功能在于,系统管理员一旦禁用namespace中的网络设备,即使里面进程拿到了一些系统权限,也无法和外界通信。

即使network namespace 能够提供网络资源隔离的机制,用户还是会结合其他类型的namespace一起使用,以提供更好的安全隔离能力。


总结自 <<kubernetes网络权威指南>> 杜军

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值