# 系統程式設計 - User Namespaces [TOC] ## References ### Containers unplugged (part 2): understanding user namespaces > 按:這個演講是接續前一個演講,著重在 user namespace 這個 namespace。同樣的內容有另外一場 [*Understanding user namespaces - Michael Kerrisk*](https://youtu.be/83NOk8pmHi8)。 {%youtube 73nB9-HYbAI %} ### Filesystem mounts in user namespaces - Christian Brauner {%youtube 2CuwuW7AYdE %} ### State of the User Namespace - Stephane Graber & Christian Brauner, Canonical {%youtube ef4WAT0ThY0 %} ## 簡介 在一個 user namespace 可以設定 UID 與 GID 的映射,使得同一個使用者的 UID 與 GID 在這個 user namespace 中對應到不同的編號。舉例來說,可以讓一個非特權的使用者建立一個 user namespace,並且在這個 user namepace 中讓它的 UID 變成 0。儘管在這個 user namespace 之外,這個使用者只是一個普通的使用者。 ### 例子 如果用打開兩個終端機視窗 (比如用 `tmux` 打開兩個分割區)。並且在其中一個視窗輸入 `unshare`,會發現使用者變成 `root`: ```shell ubuntu@ubuntu:~$ unshare -U -r bash root@ubuntu:~# ``` 如果使用 `id` 也會發現他是 root: ```shell root@ubuntu:~# id uid=0(root) gid=0(root) groups=0(root),65534(nogroup) ``` 但另外一方面,如果看看現在這個新的 user namespace 中的 shell 的 PID: ```shell root@ubuntu:~# echo $$ 3777 ``` 然後在另外一個終端機視窗看看這個 PID 對應的行程的使用者是誰: ```shell ubuntu@ubuntu:~$ ps aux | grep 3777 ubuntu 3777 0.0 0.0 10020 4828 pts/1 S+ 12:35 0:00 bash ``` 會發現這個行程的使用者(輸出的第一個欄位)並不是 `root`,而是原先的 `ubuntu`。 ## 基本設計 ### User Namespaces 間有階層 ![](https://i.imgur.com/JzZAisP.png) User Namespaces 間有階層關係。哪個 user namespace 中的行程建立了新的 user namespace,那麼這個 user namespace 就是這個新的 user namespace 的 parent user namespace。 > *User namespaces can be nested; that is, each user namespace - except the initial ("root") namespace—has a parent user namespace, and can have zero or more child user namespaces. The parent user namespace is the user namespace of the process that creates the user namespace via a call to `unshare(2)` or `clone(2)` with the `CLONE_NEWUSER` flag.* 除此之外,建立這個 user namespace 的行程,在這個新建立的 user namespace 中會具有所有 capabilities: > *The child process created by `clone(2)` with the `CLONE_NEWUSER` flag starts out with a complete set of capabilities in the new user namespace. Likewise, a process that creates a new user namespace using `unshare(2)` or joins an existing user namespace using `setns(2)` gains a full set of capabilities in that namespace.* 這個階層會影響到一個行程在哪個 user namespace 中會有哪些 capabilities。 ### 使用者可以有新的 UID 與 GID 如同最前面的例子,一個使用者的 UID 在 user namespace 中可以被重新設定。而這個設定方法是修改形成的 `/proc/PID/uid_map` 這個檔案。這個檔案中的每一行形成格式如下的三元組: ``` ID-inside-ns ID-outside-ns length ``` 裡面各自代表的意義是把「user namespace 中的 `ID-inside-ns` 後的連續 `length` 個 UID 編號」,逐一對應到「上一個 user namespace 中 `ID-outside-ns` 開始往後的 `length` 個 UID」。舉例來說: ``` 0 1000 1 ``` 代表的是「這個 namespace 中,編號 `0` 開始的 `1` 個 UID,其實代表創造他的 namespace 中的」 特別地,當 `ID-inside-ns` 是 `0`,且 `length` 是 `1`,且 `ID-outside-ns` 恰好就是建立這個 user namespace 的行程時。這時候這個 namespace 中就只會有一個 root,這樣的對應稱為 root mapping。這可以在使用 `unshare -U` 命令建立 user namespace 時,加上 `-r` 選項來自動設定。 ### User NS 以外的 NS 都有一個 User Namespace 當它的主人 > *When a nonuser namespace is created, it is owned by the user namespace in which the creating process was a member at the time of the creation of the namespace.* ![](https://i.imgur.com/yQ2CSoc.png) 在 `unshare` 的時候,如果裡面其中一個是創造 user namespace (`-U` 選項),那麼會保證那個 user namespace 先被建立,並且其他種類的 namespace 會被那個 user namespace 擁有: > *If `CLONE_NEWUSER` is specified along with other `CLONE_NEW*` flags in a single `clone(2)` or `unshare(2)` call, the user namespace is guaranteed to be created first, giving the child (`clone(2)`) or caller (`unshare(2)`) privileges over the remaining namespaces created by the call. Thus, it is possible for an unprivileged caller to specify this combination of flags.* 舉例來說,建立上面這張圖的 namespace 可以用以下命令: ```shell $ unshare -U -u -r ``` 在這個命令中,建立了新的 user namespace (`-U`) 與新的 UTS namespace (`-u`)。這時候 user namespace 會先被建立,接著再建立 UTS namespace。 ### 行程只能更動所屬的 User NS 擁有的 Namespaces 中的資源 一個 user namespace 中的 root 不代表它可以對它看見的資源做出任意的更動,即使它有所有的 capabilities。必須要「管轄該資源的 namespace」恰好被「該行程所處的 user namespace」擁有,才能夠使用: > *When a new namespace (other than a user namespace) is created via `clone(2)` or `unshare(2)`, the kernel records the user namespace of the creating process as the owner of the new namespace. (This association can't be changed.) When a process in the new namespace subsequently performs privileged operations that operate on global resources isolated by the namespace, the permission checks are performed according to the process's capabilities in the user namespace that the kernel associated with the new namespace. For example, suppose that a process attempts to change the hostname (`sethostname(2)`), a resource governed by the UTS namespace. In this case, the kernel will determine which user namespace owns the process's UTS namespace, and check whether the process has the required capability (`CAP_SYS_ADMIN` in that user namespace.)* 舉例來說,建立一個 user namespace 之後,並且指定 root mapping。這時雖然在這個 user namespace 中看起來是 root: ```shell $ unshare -U -r # id uid=0(root) gid=0(root) groups=0(root),65534(nogroup) ``` 但如果想要改變 host name,卻會被說「要是 root 才可以更動」: ```shell # hostname newname hostname: you must be root to change the host name ``` 另外一方面,除了 user namespace 之外,另外建立一個屬於這個 user namespace 的 UTS namespace。這時就可以更改這個 UTS namesapce 中的 host name: ```shell $ unshare -U -r -u # id uid=0(root) gid=0(root) groups=0(root),65534(nogroup) # hostname ubuntu # hostname newname # hostname newname ``` 並不是所有的資源都可以被一個 user namespace 中的 root 存取: > *On the other hand, there are many privileged operations that affect resources that are not associated with any namespace type, for example, changing the system (i.e., calendar) time (governed by `CAP_SYS_TIME`), loading a kernel module (governed by `CAP_SYS_MODULE`), and creating a device (governed by `CAP_MKNOD`). Only a process with privileges in the initial user namespace can perform such operations.* ## 更多權限管理的細節 ### 不是 root 但具有 capabilities 的行程 > *A process has a capability inside a user namespace if it is a member of that namespace and it has the capability in its effective capability set.* ### 在一個 User NS 有特權,則在他的後代子孫都有 > *If a process has a capability in a user namespace, then it has that capability in all child (and further removed descendant) namespaces as well.* ### 上一輩的 User NS 中,EUID 跟「建立這個時 User NS 用的 EUID」相同的行程 > *When a user namespace is created, the kernel records the effective user ID of the creating process as being the "owner" of the namespace. A process that resides in the parent of the user namespace and whose effective user ID matches the owner of the namespace has all capabilities in the namespace.*