docker 以进程为核心, 对系统资源进行隔离使用的管理工具. 隔离是通过 cgroups (control groups 进程控制组) 这个操作系统内核特性来实现的. 包括用户的参数限制、 帐户管理、 资源(cpu,内存,磁盘i/o,网络)使用的隔离等. docker 在运行时可以为容器内进程指定用户和组. 没有指定时默认是 root .但因为隔离的原因, 并不会因此丧失安全性. 传统上, 特定的应用都以特定的用户来运行, 在容器内进程指定运行程序的所属用户或组并不需要在 host 中事先创建.
Docker容器的权限管理
默认使用的root权限
不管是以root用户还是以普通用户(有启动docker容器的权限)启动docker容器,容器进程和容器内的用户权限都是root!
新建了sleep用户,以sleep用户权限启动容器并在有root权限的磁盘进行权限测试。1
docker run -v /data/sleep:/sleep -d --name sleep-1 ubuntu sleep infinity
在宿主机中/data/sleep路径新建了leo_zhou文件并在文件写入“docker”,然后进入sleep-1容器1
docker exec -it sleep-1 bash
依然可以正常操作拥有root权限的文件。
限制Docker容器启动的用户
新增–user参数,使容器启动用户变成指定的sleep用户,发现并不能操作拥有root权限的文件了。会发现容器中的uid号和实际主机中的uid号一样,也验证了docker容器使用宿主机的内核。可以一定程度进行权限管理。
使用namespace隔离技术
namespace是一种隔离技术,docker就是使用隔离技术开启特定的namespace创建出一些特殊的进程,不过使用namespace是有条件的。系统会创建dockremap,通过/etc/subuid和/etc/subuid对应的id值,映射到容器中去;实际情况还是使用的是dockremap普通权限,达到自动隔离的效果。
①开启Centos内核中关闭的user namespace的功能。1
2grubby --args="namespace.unpriv_enable=1 user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"
echo "user.max_user_namespaces=15076" >> /etc/sysctl.conf
②修改/etc/docker/daemon.json配置,新增”userns-remap”: “default”选项,default默认就是docker自动创建的用户dockremap,然后重启docker。
修改此项配置需要慎重,如果是已经部署了一套docker环境,启用此选项后,会切换到隔离环境,以前的docker容器将无法使用!
③Centos需要手动输入id值映射范围
最后systemctl restart docker后再次测试效果,发现文件权限已经变成nobody,但docker容器内部依然是以”root”的权限管理,但实际只有普通用户的权限,从而达到权限隔离的效果。
进程控制组cgroups主要可能做以下几件事:
- 资源限制 组可以设置为不超过配置的内存限制, 其中还包括文件系统缓存
- 优先级 某些组可能会获得更大的 cpu 利用率份额或磁盘 i/o 吞吐量
- 帐号会计 度量组的资源使用情况, 例如, 用于计费的目的
- 控制 冻结组进程, 设置进程的检查点和重新启动
与 cgroups(控制进程组) 相关联的概念是 namespaces (命令空间)。
命名空间主要有六种名称隔离类型:
- pid 命名空间为进程标识符 (pids) 的分配、进程列表及其详细信息提供了隔离。
虽然新命名空间与其他同级对象隔离, 但其 “父 “ 命名空间中的进程仍会看到子命名空间中的所有进程 (尽管具有不同的 pid 编号)。 - 网络命名空间隔离网络接口控制器 (物理或虚拟)、iptables 防火墙规则、路由表等。网络命名空间可以使用 “veth “ 虚拟以太网设备彼此连接。
- uts 命名空间允许更改主机名。
- mount(装载)命名空间允许创建不同的文件系统布局, 或使某些装入点为只读。
- ipc 命名空间将 system v 的进程间通信通过命名空间隔离开来。
- 用户命名空间将用户 id 通过命名空间隔离开来。
普通用户 docker run 容器内 root
如 busybox, 可以在 docker 容器中以 root 身份运行软件. 但 docker 容器本身仍以普通用户执行.
考虑这样的情况1
echo test | docker run -i busybox cat
前面的是当前用户当前系统进程,后面的转入容器内用户和容器内进程运行.
当在容器内 pid 以1运行时, linux 会忽略信号系统的默认行为, 进程收到 sigint 或 sigterm 信号时不会退出, 除非你的进程为此编码. 可以通过 dockerfile stopsignal signal指定停止信号.
普通用户 docker run 容器内指定不同用户 demo_user
1 | docker run --user=demo_user:group1 --group-add group2 <image_name> <command> |
这里的 demo_user 和 group1(主组), group2(副组) 不是主机的用户和组, 而是创建容器镜像时创建的.
当dockerfile里没有通过user指令指定运行用户时, 容器会以 root 用户运行进程.
docker 指定用户的方式
dockerfile 中指定用户运行特定的命令
1 | user <user>[:<group>] #或 |
docker run -u(–user)[user:group] 或 –group-add 参数方式1
2
3
4
5
6
7
8$ docker run busybox cat /etc/passwd
root:x:0:0:root:/root:/bin/sh
...
www-data:x:33:33:www-data:/var/www:/bin/false
nobody:x:65534:65534:nobody:/home:/bin/false
$ docker run --user www-data busybox id
uid=33(www-data) gid=33(www-data)
docker 容器内用户的权限
对比以下情况, host 中普通用户创建的文件, 到 docker 容器下映射成了 root 用户属主:1
2
3$ mkdir test && touch test/a.txt && cd test
$ docker run --rm -it -v `pwd`:/mnt -w /mnt busybox /bin/sh -c 'ls -al /mnt/*'
-rw-r--r-- 1 root root 0 oct 22 15:36 /mnt/a.txt
而在容器内卷目录中创建的文件, 则对应 host 当前执行 docker 的用户:1
2
3
4$ docker run --rm -it -v `pwd`:/mnt -w /mnt busybox /bin/sh -c 'touch b.txt'
$ ls -al
-rw-r--r-- 1 xwx staff 0 10 22 23:36 a.txt
-rw-r--r-- 1 xwx staff 0 10 22 23:54 b.txt
docker volume 文件访问权限
创建和使用卷, docker 不支持相对路径的挂载点, 多个容器可以同时使用同一个卷.1
2
3
4
5
6
7
8
9
10
11
12
13
14$ docker volume create hello #创建卷
hello
$ docker run -it --rm -v hello:/world -w /world busybox /bin/sh -c 'touch /world/a.txt && ls -al' #容器内建个文件
total 8
drwxr-xr-x 2 root root 4096 oct 22 16:38 .
drwxr-xr-x 1 root root 4096 oct 22 16:38 ..
-rw-r--r-- 1 root root 0 oct 22 16:38 a.txt
$ docker run -it --rm -v hello:/world -w /world busybox /bin/sh -c 'rm /world/a.txt && ls -al' #从容器内删除
total 8
drwxr-xr-x 2 root root 4096 oct 22 16:38 .
drwxr-xr-x 1 root root 4096 oct 22 16:38 ..
外部创建文件, 容器内指定用户去删除1
2$ touch c.txt && sudo chmod root:wheel c.txt
$ docker run -u 100 -it --rm -v `pwd`:/world -w /world busybox /bin/sh -c 'rm /world/c.txt && ls -al'
实际是可以删除的1
2
3
4
5
6rm: remove '/world/c.txt'? y
total 4
drwxr-xr-x 4 100 root 128 oct 23 16:09 .
drwxr-xr-x 1 root root 4096 oct 23 16:09 ..
-rw-r--r-- 1 100 root 0 oct 22 15:36 a.txt
-rw-r--r-- 1 100 root 0 oct 22 15:54 b.txt
docker 普通用户的1024以下端口权限1
2
3
4
5
6 $ docker run -u 100 -it --rm -p 70:80 busybox /bin/sh -c 'nc -l -p 80'
nc: bind: permission denied #用户id 100 时, 不能打开80端口
$ docker run -u 100 -it --rm -p 70:8800 busybox /bin/sh -c 'nc -l -p 8800' #容器端口大于1024时则可以
...
$ docker run -it --rm -p 70:80 busybox /bin/sh -c 'nc -l -p 80' #容器内是 root 也可以
...