linux namespace
本文最后更新于:1 年前
Namespace常用类型
pid namespace
Linux内核维护着一个进程树,Linux中的进程关系通过此树来反映。而在pid namespace引入后,可以认为Linux内依旧是维护着一个进程树,但是其节点既可以是进程也可以是进程树,即此时进程树允许树的嵌套关系。而不同的进程树则使得其内部的进程组互相隔离,它们并不清楚彼此的存在。见下图
在不考虑新的pid namespace的情况下,我们所有进程都属于同一个命名空间init_pid_ns,整个进程树都是由作为根的1号进程派生而来的。而当我们创建新的pid namespace时,则会创立出一个新的独立的进程空间。它是我们在创建子进程时可以设置的一个选项,使得新创建出来的子进程在新的pid namespace中,并且成为该命名空间的1号进程。
显然,利用pid namespace做隔离,子命名空间里的进程认为自己是一棵独立的进程树,完全不知到是父命名空间的存在。但是父命名空间(即init_pid_ns)却拥有着整个进程树的真正完整的视图,因为在引入pid namespace的概念后,pid的含义也变得复杂了,加入了层级level的概念,高层级的进程可以被低层级看见,即init_pid_ns这个进程空间的层级是最低的,为0,而新的进程命名空间随着嵌套情况逐次增加层级。就像图一所示,6号进程的子进程是8号进程(需要被level0的看见),但是由于其派生时设置标志位CLONE_NEWPID,使得8号进程与原先的进程树分离,自己作为新进程命名空间里的1号进程,此时该进程便拥有两个pid,一个是level1的1号进程,而另一个则为level0的8号进程。因此此时的pid结构也发生了相应的变化,见下图
通过引入pid namespace,一个进程现在可以有多个与之关联的pid,每个pid通过level即层级来进行划分,然后利用变长数组numbers可以检索到对应的upid,而upid里的nr即为此命名空间里的pid的值。
上文中说过,要创建一个新的pid namespace,除了需要利用特殊的标志位注明外,对于pid namespace而言,只能通过clone()这一系统调用进行新的进程命名空间的创建。示例程序如create-new-pidns
所示,其输出结果如pid-of-new-pidns
所示:
通过图3图4的示例程序及相应的输出结果,不难看出在clone()后的子进程在新的进程命名空间里,它认为自己是1号进程,打印出其父进程,则为0,即意味着该进程本身是这个进程树的init进程。
在此时,我们的进程只是隔离了进程空间,即在不同的进程空间下我们所打印出来的进程表都是不同的,但实质上如端口号这一类的资源还是会产生冲突。
network namespace
网络命名空间的作用是隔离网络协议栈,即解决了上述所说的端口号资源冲突问题。除了对端口资源进行隔离外,也对网络设备、ARP表、路由表、iptables以及套接字等资源进行隔离,即构建出一套独立的网络协议栈供新的网络命名空间所用。
在这一部分就简单的对网络设备的隔离进行讲解,后续一部分会专门对network namespace进行详细的分析。
若是我们在clone()时增添标志位CLONE_NEWNET,则会使得我们的子进程处于一个新的网络命名空间中,与原命名空间相比,二者的网络设备完全不同,即原来主机上的以太网卡和环回设备在新的命名空间中消失了,取而代之的是一个与原来不同的新的环回设备。也即说明网络命名空间对网络设备一级也做了隔离。示例程序见create-new-netns
,其输出结果见difference-of-netns-device
:
可以看出此主机上的的以太网卡eth0和其环回设备与新的network namespace的并不相同(环回设备状态不同)。因此在网络设备级别做了隔离。
此时,不同的网络命名空间就相互隔离开来,若是想让多个不同的网络命名空间进行通信,则可以通过虚拟网卡、虚拟网桥来实现,甚至还可以通过主机的以太网卡接受外界的数据包而后通过路由进程路由到对应的子网络命名空间(这里的子对应的是初始的网络命名空间)。见下图:
uts namespace
uts namespace隔离的是utsname这个结构体的domainname和nodename,前者指待的是域名,后者在单服务器上等价于主机名。之所以取名为uts(UNIX Time-Sharing System),是与早期的UNIX分时系统有关,但在现在的namespace中,它只负责隔离主机名和域名。通过标识主机名,有利于我们在一些IP地址动态变化的情况下的操作,即只需要知道IP和这个主机名关联关系即可,无需关心IP的变化。因此uts namespace对于大型容器化的环境非常有帮助。以下是关于uts namespace更改主机名的实验,示例代码如下图modify-new-utsns-hostname
所示,结果输出如图hostname-of-diffrent-utsns
所示:
通过上述结果,可以看出uts namespace对主机名的隔离作用,在新的uts namespace中对主机名的修改并不影响另一uts命名空间的主机名。
mount namespace
mount namespace用于隔离挂载点,以使得不同的命名空间中的进程不能查看到彼此的文件。
创建单独的mount namespace效果类似于chroot(改变程序执行时的参考的根目录),但chroot并不能提供完全的隔离。chroot变更文件的根挂载点示例如下图所示
在一个新的mount namespace中,子进程会看到和原来的父进程完全相同的挂载点,但此时我们可以在新的挂载命名空间中利用mount或umount来进行挂载或卸下对应的挂载点,这并不会对原来的父进程的挂载点有任何改变,只是对这个命名空间里的挂载点有变化。
ipc namespace
ipc namespace可以对命名空间内的进程间通信资源进行隔离,这里隔离的是每个命名空间所属的消息队列、信号量以及共享内存,并没有对所有的通信方式都进行隔离。
以共享内存进行通信为例,我们的同一ipc namespace里的进程组可以通过共享内存来快速知道一些共享的公共参数的变化,而此时若不进行隔离,则会产生安全性问题。
user namespace
user namespace主要是用来隔离用户权限的。它是namespace中最核心也最复杂的,Linux内核对其进行了最长时间的开发。其涉及到权限和安全问题。其包括uid/gid以及capabilities的两大部分的内容。user namespace通过对权限以及uid/gid的控制,使得即使在新的user namespace里是root的用户,当它试图去篡改原来的user namespace里的如主机名一类的别的namespace,它是否具备权限是看它原来在该user namespace是否有相应的修改权限,这也正是为什么每一个非user namespace的命名空间,在它们结构体定义里,都需要指向一个用户命名空间的意义所在。通过user namespace这个命名空间的利用,可以很好的避免了一些越权的行为发生。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!