User namespace 是 Linux 3.8 新增的一种 namespace,用于隔离安全相关的资源,包括 user IDs and group IDs,keys, 和 capabilities。同样一个用户的 user ID 和 group ID 在不同的 user namespace 中可以不一样(与 PID nanespace 类似)。换句话说,一个用户可以在一个 user namespace 中是普通用户,但在另一个 user namespace 中是超级用户。
User namespace 可以嵌套(目前内核控制最多32层),除了系统默认的 user namespace 外,所有的 user namespace 都有一个父 user namespace,每个 user namespace 都可以有零到多个子 user namespace。 当在一个进程中调用 unshare 或者 clone 创建新的 user namespace 时,当前进程原来所在的 user namespace 为父 user namespace,新的 user namespace 为子 user namespace。
说明:本文的演示环境为 ubuntu 16.04。
创建 user namespace
我们可以通过 unshare 命令的 --user 选项来创建新的 user namespace:
| $ unshare -user -r /bin/bash |

通过 -r 参数,我们把新的 user namespace 中的 root 用户映射到了外面的 nick 用户(接下来会介绍映射相关的概念)。在新的 user namespace 中,root 用户是有权限创建其它的 namespace 的,比如 uts namespace。这是因为当前的 bash 进程拥有全部的 capabilities:

下面我们创建一个新的 uts namespace 试试:
| $ unshare --uts /bin/bash |

我们看到,新的 uts namespace 被顺利的创建了。这是因为除了 user namespace 外,创建其它类型的 namespace 都需要 CAP_SYS_ADMIN 的 capability。当新的 user namespace 创建并映射好 uid、gid 了之后, 这个 user namespace 的第一个进程将拥有完整的所有 capabilities,意味着它就可以创建新的其它类型 namespace。
其实没有必要把上面的操作(创建两个 namespace)分成两步,我们可以通 unshare 一次创建多个 namespace:

在 unshare 的实现中,其实就是传入了 CLONE_NEWUSER | CLONE_NEWUTS,大致如下:
| unshare(CLONE_NEWUSER | CLONE_NEWUTS); |
在上面这种情况下,内核会保证 CLONE_NEWUSER 先被执行,然后执行剩下的其他 CLONE_NEW*,这样就使得不用 root 用户而创建新的容器成为可能,这条规则对于clone 函数也同样适用。
理解 UID 和 GID 的映射
在前面的演示中我们提到了用户在 user namespace 之间的映射,下面我们同样通过演示来理解映射是什么。我们先查看下当前用户的 ID 和 user namespace 情况:








