Linux命名空间

背景

从内核2.6.24开始,Linux支持6种不同类型的命名空间。 命名空间在创建与系统其他部分更为隔离的过程中非常有用,而无需使用完整的低级虚拟化技术。

  • CLONE_NEWIPC :IPC命名空间:SystemV IPC和POSIX消息队列可以隔离。
  • CLONE_NEWPID :PID命名空间:PID是隔离的,这意味着命名空间内的虚拟PID可能与命名空间之外的PID冲突。 命名空间内的PID将映射到命名空间之外的其他PID。 命名空间中的第一个PID将为“1”,在命名空间之外将其分配给init
  • CLONE_NEWNET :网络命名空间 :网络( / proc / net ,IP,接口和路由)是隔离的。 服务可以在命名空间中的相同端口上运行,并且可以创建“重复”虚拟接口。
  • CLONE_NEWNS :装载命名空间。 我们有能力在进程中看到安装点。 使用挂载命名空间,我们可以实现与chroot()类似的功能,但具有改进的安全性。
  • CLONE_NEWUTS :UTS命名空间。 这个命名空间的主要目的是隔离主机名和NIS名称。
  • CLONE_NEWUSER :用户名空间。 这里,用户名和组ID在命名空间内外是不同的,可以重复。

我们先来看一下C程序的结构,这个程序需要演示进程名称空间。 以下已经在Debian 6和7上进行了测试。首先,我们需要在上分配一页内存,并设置一个指向该内存页面末尾的指针。 我们使用alloca来分配内存而不是分配内存在堆上的malloc

void *mem = alloca(sysconf(_SC_PAGESIZE)) + sysconf(_SC_PAGESIZE);

接下来,我们使用clone创建一个子进程,传递我们的子栈“mem”的位置以及指定新命名空间的必需标志。 我们将'callee'指定为在子空间内执行的功能:

mypid = clone(callee, mem, SIGCHLD | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | CLONE_FILES, NULL);

在调用clone之后,我们等待子进程完成,然后终止父进程。 如果没有,父执行流程将继续并立即终止,清除孩子:

while (waitpid(mypid, &r, 0) < 0 && errno == EINTR)
{
	continue;
}

最后,我们将返回到shell的退出代码:

if (WIFEXITED(r))
{
	return WEXITSTATUS(r);
}
return EXIT_FAILURE;

现在,我们来看看被调用函数:

static int callee()
{
	int ret;
	mount("proc", "/proc", "proc", 0, "");
	setgid(u);
	setgroups(0, NULL);
	setuid(u);
	ret = execl("/bin/bash", "/bin/bash", NULL);
	return ret;
}

在这里,我们挂载一个/ proc文件系统,然后在生成/ bin / bash shell之前将uid(User ID)和gid(Group ID)设置为'u'。 LXC是一种操作系统级虚拟化工具,利用cgroups和命名空间进行资源隔离。 让我们把它放在一起,将“u”设置为65534,这是用户“nobody”,而在Debian上是“nogroup”组。

#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <grp.h>
#include <alloca.h>
#include <errno.h>
#include <sched.h>
static int callee();
const int u = 65534;
int main(int argc, char *argv[])
{
	int r;
	pid_t mypid;
	void *mem = alloca(sysconf(_SC_PAGESIZE)) + sysconf(_SC_PAGESIZE);
	mypid = clone(callee, mem, SIGCHLD | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | CLONE_FILES, NULL);
	while (waitpid(mypid, &r, 0) < 0 && errno == EINTR)
	{
		continue;
	}
	if (WIFEXITED(r))
	{
		return WEXITSTATUS(r);
	}
	return EXIT_FAILURE;
}
static int callee()
{
	int ret;
	mount("proc", "/proc", "proc", 0, "");
	setgid(u);
	setgroups(0, NULL);
	setuid(u);
	ret = execl("/bin/bash", "/bin/bash", NULL);
	return ret;
}

执行代码产生以下内容:

root@w:~/pen/tmp# gcc -O -o ns.c -Wall -Werror -ansi -c89 ns.c
root@w:~/pen/tmp# ./ns
nobody@w:~/pen/tmp$ id
uid=65534(nobody) gid=65534(nogroup)
nobody@w:~/pen/tmp$ ps auxw
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
nobody       1  0.0  0.0   4620  1816 pts/1    S    21:21   0:00 /bin/bash
nobody       5  0.0  0.0   2784  1064 pts/1    R+   21:21   0:00 ps auxw
nobody@w:~/pen/tmp$ 

请注意,UID和GID设置为nobody和nogroup。 特别注意,完整的ps输出仅显示两个正在运行的进程,它们的PID分别为1和5。 现在,我们继续使用ip netns来处理网络命名空间。 首先,我们确认目前没有命名空间:

root@w:~# ip netns list
Object "netns" is unknown, try "ip help".

在这种情况下,任一ip需要一个升级,或内核。 假设你的内核比2.6.24更新,最有可能是ip 。 升级后, ip netns列表默认情况下不返回任何内容。 我们添加一个名为'ns1'的新命名空间:

root@w:~# ip netns add ns1
root@w:~# ip netns list
ns1

首先,列出当前界面:

root@w:~# ip link list
1: lo: 
  
    mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: 
   
     mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT qlen 1000
    link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff

   
  

现在要创建一个新的虚拟接口,并将其添加到我们的新命名空间。 虚拟接口成对创建,并且彼此链接 - 想象一个虚拟交叉电缆:

root@w:~# ip link add veth0 type veth peer name veth1
root@w:~# ip link list
1: lo: 
  
    mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: 
   
     mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT qlen 1000
    link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff
3: veth1: 
    
      mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether d2:e9:52:18:19:ab brd ff:ff:ff:ff:ff:ff
4: veth0: 
     
       mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether f2:f7:5e:e2:22:ac brd ff:ff:ff:ff:ff:ff

     
    
   
  
ifconfig -a还将显示添加veth0和veth1。

很好,现在要将我们的新界面分配给命名空间。 请注意, ip netns exec用于在命名空间中执行命令:

root@w:~# ip link set veth1 netns ns1
root@w:~# ip netns exec ns1 ip link list
1: lo: 
  
    mtu 65536 qdisc noop state DOWN mode DEFAULT 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: veth1: 
   
     mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether d2:e9:52:18:19:ab brd ff:ff:ff:ff:ff:ff

   
  
ifconfig -a现在只显示veth0,因为veth1在ns1命名空间中。

我们是否要删除veth0 / veth1:

ip netns exec ns1 ip link del veth1

我们现在可以在我们的主机上将IP地址为192.168.5.5/24分配给veth0:

ifconfig veth0 192.168.5.5/24

并在ns1中分配veth1 192.168.5.10/24:

ip netns exec ns1 ifconfig veth1 192.168.5.10/24 up

在我们的主机和我们的命名空间执行ip addr列表

root@w:~# ip addr list
1: lo: 
  
    mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: 
   
     mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
    link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff
    inet 192.168.3.122/24 brd 192.168.3.255 scope global eth0
    inet6 fe80::20c:29ff:fe65:259e/64 scope link 
       valid_lft forever preferred_lft forever
6: veth0: 
    
      mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 86:b2:c7:bd:c9:11 brd ff:ff:ff:ff:ff:ff
    inet 192.168.5.5/24 brd 192.168.5.255 scope global veth0
    inet6 fe80::84b2:c7ff:febd:c911/64 scope link 
       valid_lft forever preferred_lft forever
root@w:~# ip netns exec ns1 ip addr list
1: lo: 
     
       mtu 65536 qdisc noop state DOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: veth1: 
      
        mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 12:bd:b6:76:a6:eb brd ff:ff:ff:ff:ff:ff
    inet 192.168.5.10/24 brd 192.168.5.255 scope global veth1
    inet6 fe80::10bd:b6ff:fe76:a6eb/64 scope link 
       valid_lft forever preferred_lft forever

      
     
    
   
  

查看命名空间内外的路由表:

root@w:~# ip route list
default via 192.168.3.1 dev eth0  proto static 
192.168.3.0/24 dev eth0  proto kernel  scope link  src 192.168.3.122 
192.168.5.0/24 dev veth0  proto kernel  scope link  src 192.168.5.5 
root@w:~# ip netns exec ns1 ip route list
192.168.5.0/24 dev veth1  proto kernel  scope link  src 192.168.5.10 

最后,为了连接我们的物理和虚拟接口,我们需要一个桥梁。 让我们在主机上桥接eth0和veth0,然后使用DHCP在ns1命名空间中获取一个IP:

root@w:~# brctl addbr br0
root@w:~# brctl addif br0 eth0
root@w:~# brctl addif br0 veth0
root@w:~# ifconfig eth0 0.0.0.0
root@w:~# ifconfig veth0 0.0.0.0
root@w:~# dhclient br0
root@w:~# ip addr list br0
7: br0: 
  
    mtu 1500 qdisc noqueue state UP 
    link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff
    inet 192.168.3.122/24 brd 192.168.3.255 scope global br0
    inet6 fe80::20c:29ff:fe65:259e/64 scope link 
       valid_lft forever preferred_lft forever

  

br0已分配IP为192.168.3.122/24。 现在为命名空间:

root@w:~# ip netns exec ns1 dhclient veth1
root@w:~# ip netns exec ns1 ip addr list
1: lo: 
  
    mtu 65536 qdisc noop state DOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: veth1: 
   
     mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 12:bd:b6:76:a6:eb brd ff:ff:ff:ff:ff:ff
    inet 192.168.3.248/24 brd 192.168.3.255 scope global veth1
    inet6 fe80::10bd:b6ff:fe76:a6eb/64 scope link 
       valid_lft forever preferred_lft forever

   
  

优秀! veth1已分配192.168.3.248/24

链接

IO数字版
Linux顾问

赞(52) 打赏
未经允许不得转载:优客志 » 系统运维
分享到:

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏