USER Namespaces
主要是对用户和用户组进行隔离。它是从Linux 3.8内核才慢慢支持的,现在有些linux发行版还不支持user namespaces(主要是因为还不完全成熟,处于对安全的担心,在编译内核时并未开启USER Namespaces,Centos 7就不支持,后边的测试基于Ubuntu 14.04).
User namespaces允许每个命名空间中User ID和 group ID的映射。在容器上下文环境中,这就意味着User和User Group 在容器中的操作可以拥有特权,在容器外边没有。换句话说,一个进程在user namespace中操作的权限设置不同于在宿主系统中的权限设置。user namespace一个特定的目标就是允许一个进程在容器中的操作拥有root特权,与此同时,在托管容器的宿主机上是一个无特权的正常进程。
为了支持这种行为,每个进程的UID实际上有两种值:容器中的是一种,容器外是另一种。Group ID和进程UID相似。这种对偶性通过维护每个 User namespace用户ID的映射完成的:每个用户命令空间有一张表,这张表中记录着宿主机中用户ID对应namespace 的用户ID。这种映射是通过读写/proc/PID/uid_map(/proc/PID/gid_map gid映射文件)伪文件来设置,其中PID是user namespace这个进程的进程ID。例如,宿主机中UID为1000的用户被映射到namespace中可能会为0,用户ID为1000的进程在宿主机中是一个常态的用户(普通用户),但是它在namespace中将拥有root特权。如果在宿主机系统中没有为特定用户ID提供映射,在user namespace中,这个user ID会映射到/proc/sys/kernel/overflowuid(gid文件为overflowgid)文件提供的值(这个文件中默认的值是65534)。
测试使,只需要在clone函数中添加CLONE_NEWUSER标志即可:
clone(child_main, child_stack + STACK_SIZE, CLONE_NEWUSER | SIGCHLD, NULL);
编译后,普通用户(ty)就可以执行,先查看该用户ID:
ty@ubuntu:~$ id
uid=1000(ty) gid=1000(ty) groups=1000(ty)
使用clone函数创建进程,激活user namespace:
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];
int child_main(void* arg) {
char path[256];
printf("Child inside Namespace\n");
printf("euid=%d, egid=%d, pid=%d\n",geteuid<