privileged权限中我们了解到了capabilities的概念,在不具备capabilities的情况下,即使使用root用户也仍然不具备某些操作权限。

但是root用户在容器当中仍然还是具有相比普通用户更高的操作权限的,可以修改一些重要的文件,获得在宿主机上的执行权限。因此,以root用户来运行容器也是存在安全风险的。

那么解决方案有哪些呢?

1.给容器指定普通用户

  • docker run的时候使用-u指定uid/gid
# docker run -ti --name root_example -u 6667:6667 -v /etc:/mnt  centos bash
bash-4.4$ id
uid=6667 gid=6667 groups=6667
bash-4.4$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
6667         1     0  1 01:27 pts/0    00:00:00 bash
6667         8     1  0 01:27 pts/0    00:00:00 ps -ef
  • 使用Dockerfile构建镜像的时候创建用户,这样容器默认启动的时候就是使用nonroot用户了
# cat Dockerfile
FROM centos 
RUN adduser -u 6667 nonroot
USER nonroot

但是这样是存在一个问题的,那就是uid是节点级别的,也就是说是为容器分配的uid实际上就是宿主机上的uid。而linux下对uid下的资源是有限制的,比如打开的文件数、最大进程数等,因此容器共用uid的话可能会导致资源耗尽,从而所有的容器都不可用。这在云平台上是一个问题。

2.User Namespace

user namespace可以隔离一个linux节点上的uid和gid。这样每个namespace看到的uid和gid都是相互独立的。从实现上来说,user namespace只是对宿主机上的uid和gid做了一下映射

云平台现在是不支持user namespace。用podman的--uidmap选项来启动一个带user namespace的容器

# podman run -ti  -v /etc:/mnt --uidmap 0:2000:1000 centos bash

这里的--uidmap 0:2000:1000表示容器当中的uid从0开始,映射到host namespace里的2000开始,连续映射1000个。

这样容器中的用户虽然依然是root用户,但是无法对挂载的宿主机的/etc目录下的文件做修改。

使用user namespace有如下优点:

  • 将容器中的root用户映射成宿主机上的普通用户
  • 分配好每个容器映射的范围,就不会出现宿主机上uid冲突的问题。

3.rootless container

rootless并不是在容器当中不仅仅指以root用户来运行,还指以非root用户来创建容器,管理容器。这样就不用担心容器运行时代码中的漏洞导致容器获得宿主机上的权限。

docker和podman都支持rootless container, k8s当中的rootless container支持在rootless mode