理解 Docker 容器中的 uid 和 gid(2)

容器中的当前用户就是我们设置的 appuser,如果查看容器中的 /etc/passwd 文件,你会发现 appuser 的 uid 就是 1000,这和宿主机中用户 nick 的 uid 是一样的。

让我们再创建一个只有用户 nick 可以读写的文件:

同样以数据卷的方式把它挂载到容器中:

$ docker run -d --name sleepme -w=/testv -v $(pwd)/testv:/testv test

理解 Docker 容器中的 uid 和 gid

在容器中 testfile 的所有者居然变成了 appuser,当然 appuser 也就有权限读写该文件。

这里到底发生了什么?而这些又这说明了什么?
首先,宿主机系统中存在一个 uid 为 1000 的用户 nick。其次容器中的程序是以 appuser 的身份运行的,这是由我们通过 USER appuser 命令在 Dockerfile 程序中指定的。
事实上,系统内核管理的 uid 1000 只有一个,在宿主机中它被认为是用户 nick,而在容器中,它则被认为是用户 appuser。
所以有一点我们需要清楚:在容器内部,用户 appuser 能够获取容器外部用户 nick 的权利和特权。在宿主机上授予用户 nick 或 uid 1000 的特权也将授予容器内的 appuser。

从命令行参数中自定用户身份

我们还可以通过 docker run 命令的 --user 参数指定容器中进程的用户身份。比如执行下面的命令:

$ docker run -d --user 1000 --name sleepme ubuntu sleep infinity

因为我们在命令行上指令了参数 --user 1000,所以这里 sleep 进程的有效用户显示为 nick。进入到容器内部看一下:

$ docker exec -it sleepme bash

这是个什么情况?用户名称居然显示为 "I have no name!"!去查看 /etc/passwd 文件,里面果然没有 uid 为 1000 的用户。即便没有用户名称,也丝毫不影响该用户身份的权限,它依然可以读写只有 nick 用户才能读写的文件,并且用户信息也由 uid 代替了用户名:

理解 Docker 容器中的 uid 和 gid

需要注意的是,在创建容器时通过 docker run --user 指定的用户身份会覆盖掉 Dockerfile 中指定的值。
我们重新通过 test 镜像来运行两个容器:

$ docker run -d test

查看 sleep 进程信息:

理解 Docker 容器中的 uid 和 gid

$ docker run --user 0 -d test

再次查看 sleep 进程信息:

指定了 --urser 0 参数的进程显示有效用户为 root,说明命令行参数 --user 0 覆盖掉了 Dockerfile 中 USER 命令的设置。

总结

从本文中的示例我们可以了解到,容器中运行的进程同样具有访问主机资源的权限(docker 默认并没有对用户进行隔离),当然一般情况下容器技术会把容器中进程的可见资源封锁在容器中。但是通过我们演示的对数据卷中文件的操作可以看出,一旦容器中的进程有机会访问到宿主机的资源,它的权限和宿主机上用户的权限是一样的。所以比较安全的做法是为容器中的进程指定一个具有合适权限的用户,而不要使用默认的 root 用户。当然还有更好的方案,就是应用 Linux 的 user namespace 技术隔离用户,笔者会在接下来的文章中介绍如何配置 docker 开启 user namespace 的支持。

Linux公社的RSS地址https://www.linuxidc.com/rssFeed.aspx

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/f88ab32ffea81072d5b06e74ea2dde3b.html