• docker 原理之 user namespace(下)



    1. user namespace

    user namespace 主要隔离了安全相关的标识符和属性,包括用户 ID,用户组 ID,key 和 capabilities 等。同样一个用户 id 在不同 user namespace 中会有不同的权限。比如,进程属于一个普通用户,但是它创建的 user namespace 确属于拥有所有权限的超级用户。使用 unshare 创建 user namespace:

    chunqiu@chunqiu:/root/chunqiu/docker/mount/disk1$ unshare --user -r --mount /bin/bash
    root@chunqiu:/root/chunqiu/docker/mount/disk1# id
    uid=0(root) gid=0(root) groups=0(root)
    root@chunqiu:/root/chunqiu/docker/mount/disk1# echo $$
    13905
    

    打开另一个 shell 窗口,查看进程 13905 所属用户:

    root@chunqiu:~/chunqiu/docker/mount/disk1# ps -ef | grep 13905 | grep -v grep
    chunqiu     13905 13880  0 08:26 pts/0    00:00:00 /bin/bash
    

    从上例可以看出,进程 13905 在容器(user namespace)外属于一个普通用户,但是在 user namespace 里却属于 root 用户。

    继续对上例进行深挖,unshare 的 -r 选项指明了 user namespace 用户和容器外用户的映射,查看 uid_map 和 gid_map:

    root@chunqiu:/root/chunqiu/docker/mount/disk1# cat /proc/13905/uid_map
             0       1002          1
    root@chunqiu:/root/chunqiu/docker/mount/disk1# cat /proc/13905/gid_map
             0       1002          1
    

    可以看到 user namespace 内的 root(0) 用户/组和 user namespace 外的 chunqiu(1002) 用户/组建立映射。因此,在 user namespace 内的特权用户只是 user namespace 的普通用户,无法访问“权限不够”的文件/文件夹。如:

    // user namespace 外
    root@chunqiu:~/chunqiu/docker/mount/disk1# ls -l
    total 1
    -rw-r----- 1 root root 21 May  3 08:20 rootfile
    
    // user namespace 内
    root@chunqiu:/root/chunqiu/docker/mount/disk1# ls -l
    total 1
    -rw-r----- 1 nobody nogroup 21 May  3 08:20 rootfile
    root@chunqiu:/root/chunqiu/docker/mount/disk1# cat rootfile
    cat: rootfile: Permission denied
    

    -r 选项建立 user namespace 内外的用户映射。如果不用 -r 选项 则需手动填写 uid_map 和 gid_map,实现用户的映射。创建 user namespace 如下:

    chunqiu@chunqiu:~$ unshare --user --mount /bin/bash
    nobody@chunqiu:~$ id
    uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
    nobody@chunqiu:~$ echo $$
    14641
    

    这里 user namespace 的用户是 nobody ,是因为未建立用户映射。修改 uid_map 和 gid_map 文件,注意写这两个文件的进程必须是该 user namespace 的父 namespace 或者子 namespace:

    在父 user namespace 中写文件 uid_map,gid_map:

    chunqiu@chunqiu:~$ echo '0 1002 1' > /proc/14641/uid_map
    -su: echo: write error: Operation not permitted
    chunqiu@chunqiu:~$ echo '0 1002 1' > /proc/14641/gid_map
    -su: echo: write error: Operation not permitted
    chunqiu@chunqiu:~$ ls -l /proc/14641/uid_map
    -rw-r--r-- 1 chunqiu chunqiu 0 May  3 08:57 /proc/14641/uid_map
    chunqiu@chunqiu:~$ ls -l /proc/14641/gid_map
    -rw-r--r-- 1 chunqiu chunqiu 0 May  3 08:57 /proc/14641/gid_map
    

    尝试写入 uid_map 和 gid_map 显示没有权限,但是这两个文件确实是属于用户 chunqiu。查看当前进程的 capability:

    chunqiu@chunqiu:~$ cat /proc/$$/status | egrep 'Cap(Inh|Prm|Eff)'
    CapInh: 0000000000000000
    CapPrm: 0000000000000000
    CapEff: 0000000000000000
    

    查看 capability 知道当前进程没 CAP_SETUID 和 CAP_SETGID 权限,为其加上权限重新写:

    chunqiu@chunqiu:~$ sudo setcap cap_setgid,cap_setuid+ep /bin/bash
    chunqiu@chunqiu:~$ echo '0 1002 1' > /proc/14641/uid_map
    chunqiu@chunqiu:~$ echo '0 1002 1' > /proc/14641/gid_map
    

    在子 user namespace 中执行 bash 查看用户:

    nobody@chunqiu:~$ exec bash
    root@chunqiu:~# id
    uid=0(root) gid=0(root) groups=0(root)
    

    可以看到 nobody 改成了用户 root,实现了user namespace 内外用户的映射。

    有一点需要注意的是当 user namespace 和其它 namespace 混合使用时,依旧需要 root 权限。解决方案是先以普通用户身份运行 user namespace,然后在 user namespace 中以 root 身份运行其它 namespace。内核会保证 user namespace 先执行。

    2. docker 容器中的 uid 和 gid

    docker 默认并没有使用 user namespace,它创建的容器和宿主机是同一 user namespace。意味着,docker 并未隔离宿主机和容器的用户。

    在 docker 中指定用户身份有两种方式:

    1. Dockerfile 中指定用户身份
    2. 命令行参数指定用户身份

    这里介绍第二种命令行参数指定用户身份:

    # docker run -d --user 9999:9999 --name chunqiu 32c400c35bc2 sleep infinity
    c588d1c1487a802aad016d5b82080f675bebc3111c33b103852408c56ff9b2e9
    [root@chunqiu ~ (Master)]# docker ps | grep chunqiu
    c588d1c1487a    32c400c35bc2   "sleep infinity"    11 seconds ago   Up 8 seconds    chunqiu
    [root@chunqiu ~ (Master)]# ps -ef | grep sleep | grep -v grep
    9999     3212381 3212189  2 13:53 ?        00:00:00  /usr/bin/sleep infinity
    [root@chunqiu ~ (Master)]# readlink /proc/$$/ns/user
    user:[4026531837]
    

    命令行参数中使用 --user 指定用户 id 和用户组 id。在容器外查看进程所属的用户 id 是命令行参数指定的用户 9999,进入 container 中查看用户信息:

    [root@chunqiu ~ (Master)]# docker exec -it chunqiu /bin/bash
    bash-5.0$ ps -ef
    UID          PID    PPID  C STIME TTY          TIME CMD
    9999           1       0  0 05:53 ?        00:00:00  /usr/bin/sleep infinity
    9999           7       0 10 05:54 pts/0    00:00:00 /bin/bash
    9999          13       7  0 05:54 pts/0    00:00:00 ps -ef
    bash-5.0$ id
    uid=9999 gid=9999 groups=9999
    bash-5.0$ readlink /proc/$$/ns/user
    user:[4026531837]
    

    进入容器中发现 user namespace 和宿主机上 user namespace 是一样的。同时,容器使用了 PID namespace,容器外的 3212381 进程在容器是容器内 PID 为 1 的 init 进程,并且进程的所属用户是命令行参数指定的用户。

    容器和宿主机共用内核,内核使用的是 uid 和 gid,而不是用户名和组名, 因此这里不指定用户名也是可以工作的。内核会将用户 9999 当作普通用户对待,建立文件查看 9999 的访问权限:

    [root@chunqiu ~ (Master)]# docker exec -it chunqiu /bin/bash
    bash-5.0$ ls
    commonfile  chunqiufile
    bash-5.0$ ls -l
    total 0
    -rw-rw----. 1 7779 7779 0 May  3 10:18 commonfile
    -rw-rw-r--. 1 7779 7779 0 Apr 30 05:31 chunqiufile
    bash-5.0$ cat chunqiufile
    bash-5.0$ cat commonfile
    cat: commonfile: Permission denied
    bash-5.0$
    

    将文件 commonfile 和 chunqiufile mount 到容器内,文件的所属用户和用户组改成了 7779,它是宿主机上的 chunqiu 普通用户,在这里以 id 的形式显示。发现用户只能读取 chunqiufile,因为它开放了读权限给不属于其用户组的其它用户。

    2.1 kubernetes 指定 uid 和 gid 方式

    在 kubernetes 中通过配置 security Context 来配置 Pod 或容器 container 的 uid 和 gid,kubernetes 默认也是不使用 user namespace 的。
    如下创建 container 所属 uid 和 gid:

    spec:
        securityContext:
            runAsUser: 9999
            runAsGroup: 9999
            ...
    

    详细信息看 这里

    3. docker 和 user namespace

    上节说了 docker 默认不开启 user namespace,实际上 docker 已经实现了相关功能,参看 这里 进行配置使用,本文就不赘述啦~

    芝兰生于空谷,不以无人而不芳。
  • 相关阅读:
    Longest Common Prefix
    Roman to Integer
    Intger to Roman
    Container With Most Water
    Regular Expression Matching
    atoi
    Rotate List
    54. Search a 2D Matrix && Climbing Stairs (Easy)
    53. Minimum Window Substring
    52. Sort Colors && Combinations
  • 原文地址:https://www.cnblogs.com/xingzheanan/p/14729462.html
Copyright © 2020-2023  润新知