默认情况下,容器中的进程以 root 用户权限运行,并且这个 root 用户和宿主机中的 root 是同一个用户。听起来是不是很可怕,因为这就意味着一旦容器中的进程有了适当的机会,它就可以控制宿主机上的一切!本文我们将尝试了解用户名、组名、用户 id(uid)和组 id(gid)如何在容器内的进程和主机系统之间映射,这对于系统的安全来说是非常重要的。说明:本文的演示环境为 ubuntu 16.04(下图来自互联网)。

先来了解下 uid 和 gid

uid 和 gid 由 Linux 内核负责管理,并通过内核级别的系统调用来决定是否应该为某个请求授予特权。比如当进程试图写入文件时,内核会检查创建进程的 uid 和 gid,以确定它是否有足够的权限修改文件。注意,内核使用的是 uid 和 gid,而不是用户名和组名
简单起见,本文中剩下的部分只拿 uid 进行举例,系统对待 gid 的方式和 uid 基本相同。

很多同学简单地把 docker 容器理解为轻量的虚拟机,虽然这简化了理解容器技术的难度但是也容易带来很多的误解。事实上,与虚拟机技术不同:同一主机上运行的所有容器共享同一个内核(主机的内核)。容器化带来的巨大价值在于所有这些独立的容器(其实是进程)可以共享一个内核。这意味着即使由成百上千的容器运行在 docker 宿主机上,但内核控制的 uid 和 gid 则仍然只有一套。所以同一个 uid 在宿主机和容器中代表的是同一个用户(即便在不同的地方显示了不同的用户名)。

注意,由于普通的用来显示用户名的 Linux 工具并不属于内核(比如 id 等命令),所以我们可能会看到同一个 uid 在不同的容器中显示为不同的用户名。但是对于相同的 uid 不能有不同的特权,即使在不同的容器中也是如此。

如果你已经了解了 Linux 的 user namespace 技术,参考《Linux Namespace : User》,你需要注意的是到目前为止,docker 默认并没有启用 user namesapce,这也是本文讨论的情况。笔者会在接下来的文章中介绍如何配置 docker 启用 user namespace。

容器中默认使用 root 用户

如果不做相关的设置,容器中的进程默认以 root 用户权限启动,下面的 demo 使用 ubuntu 镜像运行 sleep 程序:

$ docker run -d  --name sleepme ubuntu sleep infinity0

注意上面的命令中并没有使用 sudo。笔者在宿主机中的登录用户是 nick,uid 为 1000:

在宿主机中查看 sleep 进程的信息:

$ ps aux | grep sleep

sleep 进程的有效用户名称是 root,也就是说 sleep 进程具有 root 权限。
然后进入容器内部看看,看到的情况和刚才一样,sleep 进程也具有 root 权限:

那么,容器内的 root 用户和宿主机上的 root 用户是同一个吗?
答案是:是的,它们对应的是同一个 uid。原因我们在前面已经解释过了:整个系统共享同一个内核,而内核只管理一套 uid 和 gid。

其实我们可以通过数据卷来简单的验证上面的结论。在宿主机上创建一个只有 root 用户可以读写的文件

然后挂载到容器中:

$ docker run --rm -it -w=/testv -v $(pwd)/testv:/testv ubuntu

在容器中可以读写该文件

我们可以通过 Dockerfile 中的 USER 命令或者是 docker run 命令的 --user 参数指定容器中进程的用户身份。下面我们分别来探究这两种情况。

在 Dockerfile 中指定用户身份

我们可以在 Dockerfile 中添加一个用户 appuser,并使用 USER 命令指定以该用户的身份运行程序,Dockerfile 的内容如下:

FROM ubuntu
RUN useradd -r -u 1000 -g appuser
USER appuser
ENTRYPOINT ["sleep", "infinity"]

编译成名称为 test 的镜像:

$ docker build -t test

用 test 镜像启动一个容器:

$ docker run -d --name sleepme test.

在宿主机中查看 sleep 进程的信息:

这次显示的有效用户是 nick,这是因为在宿主机中,uid 为 1000 的用户的名称为 nick。再进入到容器中看看:

$ docker exec -it sleepme bash

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

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

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

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

在容器中 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 run --user 指定的用户身份会覆盖掉 Dockerfile 中指定的值。
我们重新通过 test 镜像来运行两个容器:

$ docker run -d test

查看 sleep 进程信息:

$ docker run --user 0 -d test

再次查看 sleep 进程信息:

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

总结

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

参考:
Understanding how uid and gid work in Docker containers
Introduction to User Namespaces in Docker Engine
Isolate containers with a user namespace

作者:sparkdev
出处:http://t.cn/Eflqt47

51Reboot 2019 最新课程招生信息

Python 零基础入门课程此课程为面授班和网络班,一共 15 个课时,每周上一个全天,历时4个月。附加:录播视频+笔记+答疑2019-6月份开课

Python 自动化运维进阶课程此课程为面授班和网络班,一共 15 个课时,每周上一个全天,历时4个月。附加:录播视频+笔记+答疑2019-4月份开课

Golang 课程

现在报名即可享受早鸟价

对此感兴趣的朋友可以留言或私信我

进入docker容器中查看文件夹_理解 docker 容器中的 uid 和 gid相关推荐

  1. linux中查看隐藏文件夹_如何在Linux中隐藏图像中的文件或文件夹

    linux中查看隐藏文件夹 If other people use your Linux PC from time to time, you can hide files and folders fr ...

  2. 进入docker容器中查看文件夹_如何在docker容器文件夹中提供文件夹权限

    我正在我的Dockerfile中创建一个文件夹,我想给它一个写权限.但是当我尝试这样做时,我得到了许可被拒绝的错误 FROM python:2.7 RUN pip install Flask==0.1 ...

  3. outlook 脱机文件夹_在Outlook 2007中使用搜索文件夹节省时间

    outlook 脱机文件夹 This article was written by MysticGeek, a tech blogger at the How-To Geek Blogs. 本文由Ho ...

  4. java的sdk在哪个文件夹_我的计算机中的Java SDK文件夹在哪里? Ubuntu 12.04

    我知道它的安装,因为当我键入: $java -version 我得到: OpenJDK Runtime Environment (IcedTea6 1.12.5) (6b27-1.12.5-0ubun ...

  5. 苹果笔记本怎么找文件夹_如何在苹果笔记本中找出 “~/Library/Preferences/” 文件夹?...

    一.iPhone访问限制密码找回教程(未越狱) 1.打开iTools,在左侧菜单栏找到"文件系统",依次打开/private/var/keychains/,在keychains文件 ...

  6. python读取多个文件夹_如何从python中的文件夹中读取多个NetCDF文件

    我试图绘制多年(1979-2014)的平均温度图,我唯一的问题是尝试从文件夹中读取多个NetCDF(.nc)文件.目前我的程序将绘制单个文件,但我不明白如何使其读取文件夹中的所有文件(每年一个).我想 ...

  7. Android设置默认文件管理器,使用默认资源管理器在Android中查看文件夹内容

    我想以编程方式启动默认文件资源管理器以显示文件夹的内容. 我正在使用此代码,但它崩溃了: Uri startDir = Uri.fromFile(new File("/sdcard/DCIM ...

  8. docker镜像默认下载文件夹迁移,docker默认保存位置及迁移

    linux系统盘空间太小了,如果将docker装在系统盘下,很容易被占满,这个时候就需要将docker默认的下载及镜像保存的路径改到空间大的磁盘中. 步骤一: 首先在空间大的磁盘下新建一个目录,我这里 ...

  9. Linux中把文件夹打成war包,SpringBoot中maven项目打成war包部署在liunx服务器上的方法...

    说明:Spring Boot由于内嵌了如Tomcat,Jetty和Undertow这样的容器,也就是说可以直接跑起来,用不着再像Spring项目还需要外置的Tomcat等容器来进行部署工作了,通过启动 ...

  10. java中打开文件显示_从java程序中打开任何文件

    在 java中打开文件似乎有点棘手 – 对于.txt文件,必须将File对象与Scanner或BufferedReader对象结合使用 – 对于图像IO,必须使用 ImageIcon类 – 如果要打开 ...

最新文章

  1. 多线程EXecutor 柜架示例
  2. php从mysql 表中提取图片数据并显示
  3. android 判断http编码格式,安卓入门笔记之HttpURLConnection的使用
  4. AbstractListView源码分析4
  5. kubernetes mysql pxc_K8S使用operator部署和管理Percona - PXC集群
  6. Python18行代码做出来这样有点浪漫,又有点极客的“内涵”图
  7. java 反射 设置属性_Java通过反射机制动态设置对象属性值的方法
  8. 剑指offer面试题17. 打印从1到最大的n位数
  9. activemq linux 100M,Linux下安装 activemq 并指定jdk 1.8(示例代码)
  10. 新型计算机离我们还有多远
  11. 用C语言实现传递闭包(warshall算法)
  12. python怎么添加多行注释_python如何注释多行
  13. 富爸爸穷爸爸读书感言
  14. 计算机网络与Internet发展历史
  15. linux权限英文,Linux常见英文报错中文翻译(菜鸟必知)
  16. 【python小技巧】 批量将.png格式图片转换为.jpg格式图片
  17. webSpider----request
  18. vue+element自动计算天数
  19. 翻译程序和解释程序的区别
  20. android紫禁城一日游的代码,故宫旅游app下载-故宫旅游 安卓版v3.3.6-PC6安卓网

热门文章

  1. 浅谈微服务下异常处理
  2. 怎样提高团队管理能力7
  3. 【linux】gcc命令
  4. web FG interview all
  5. 基于Velocity的Web开发指南
  6. WPF中的TreeView入门
  7. linux新手记录;可执行文件直接运行
  8. java基础 题和知识点总结, 关于String s是否默认初始化为null......,new一个对象和类静态域,是不是在内存中不是一个地方...
  9. groovy学习(四)io
  10. ESRI.ArcGIS.Controls.AxMapControl