默认情况下,容器中的进程以 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》(https://www.cnblogs.com/sparkdev/p/9462838.html),你需要注意的是到目前为止,docker 默认并没有启用 user namesapce,这也是本文讨论的情况。笔者会在接下来的文章中介绍如何配置 docker 启用 user namespace。

容器中默认使用 root 用户

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

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

注意:上面的命令中并没有使用 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 ubuntuRUN useradd -r -u 1000 -g appuserUSER appuserENTRYPOINT ["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(https://medium.com/@mccode/understanding-how-uid-and-gid-work-in-docker-containers-c37a01d01cf)

  • Introduction to User Namespaces in Docker Engine(https://success.docker.com/article/introduction-to-user-namespaces-in-docker-engine)

  • Isolate containers with a user namespace(https://docs.docker.com/engine/security/userns-remap/)

作者:sparkdev

出处:http://t.cn/Eflqt47

精彩推荐无监控不运维——使用 Python 写一个小小的项目监控止不住的裁员潮:看京东前员工吐槽——绩效打C还希望我好好干福利|现场为你讲解——基于 cfssl 自建 CA 系统

Reboot 课程信息

2019年3月1日

Python 零基础入门课程

Python 运维自动化进阶课程

Docker + K8s 课程

详情扫码添加小助手咨询

docker -v 覆盖了容器中的文件_10分钟让你理解 docker 容器中的 uid 和 gid相关推荐

  1. python random模块中的指令_10分钟让你掌握python编程中random模块功能使用,非常详细...

    原标题:10分钟让你掌握python编程中random模块功能使用,非常详细 python作为一门高级编程语言,它的定位是优雅.明确和简单.阅读Python编写的代码感觉像在阅读英语一样,这让使用者可 ...

  2. 文件管理搜不到Android 里的文件,Android:在原始文件夹中添加文件后窗口找不到内容容器视图...

    正在获取窗口找不到内容容器视图.这个错误询问应用程序何时将检查权限Android:在原始文件夹中添加文件后窗口找不到内容容器视图 我注意到,对于穿着应用程序,我正在将穿着apk复制到原始文件夹中.它有 ...

  3. python把模块装到文件夹中_把模块有关联的放在一个文件夹中 在python2中调用文件夹名会直接失败 在python3中调用会成功,但是调用不能成功的解决方案...

    把模块有关联的放在一个文件夹中 在python2中调用文件夹名会直接失败 在python3中调用会成功,但是调用不能成功 解决办法是: 在该文件夹下加入空文件__init__.py python2会把 ...

  4. 把模块有关联的放在一个文件夹中 在python2中调用文件夹名会直接失败 在python3中调用会成功,但是调用不能成功的解决方案...

    把模块有关联的放在一个文件夹中 在python2中调用文件夹名会直接失败 在python3中调用会成功,但是调用不能成功 解决办法是: 在该文件夹下加入空文件__init__.py python2会把 ...

  5. docker -v 覆盖了容器中的文件_Docker容器之安装Mysql

    Docker容器之安装Mysql 1.拉取mysql镜像 docker pull mysql 提示:此处拉取最新版本镜像. 2.通过镜像创建容器 根据需求创建Mysql容器(此处我们选择自定义映射端口 ...

  6. docker -v 覆盖了容器中的文件_浅谈docker中宿主机和容器之间互相copy文件的两种方式,欢迎补充...

    在dokcer的日常使用过程中,我们可能会遇到将宿主机内文件/目录copy到容器内,或者将容器的文件/目录copy到宿主机中,下面我们就来简单的谈一下关于这种情况的两种操作. 1.Docker cp命 ...

  7. docker -v 覆盖了容器中的文件_「安定坊」安全卫士-容器漏洞评估

    现阶段,我们正在从虚拟化过渡到容器化,一些我们所熟悉的容器化技术就包括了诸如docker或http://quay.io等.一般来说,我们可以通过配置程序依赖环境来为特定应用程序建立镜像,通常当开发人员 ...

  8. docker -v 覆盖了容器中的文件_springboot配合maven打成可执行jar,构建镜像部署到docker容器中...

    本篇文章将介绍springboot应用如何打成jar包,并将jar构建为docker镜像部署到docker中 应用打包 需要配合spring-boot-maven-plugin打包,将以下代码放到应用 ...

  9. Docker常用命令(启动、镜像相关、容器相关、文件拷贝、目录挂载、查看容器IP地址、Docker备份与恢复)

    0.设置Docker镜像加速器 详见文章:Docker设置ustc的镜像源(镜像加速器) 1.Docker的启动与停止 注: systemctl 命令是系统服务管理器指令. (1)启动docker(守 ...

最新文章

  1. Linux----函数中变量的作用域、local关键字。
  2. Fedora 26 将助力云、服务器、工作站系统
  3. 400全集python全套视频教程-千锋出品全套python视频教程,400大全集,你了解吗?...
  4. 让容器应用管理更快更安全,Dragonfly 发布 Nydus 容器镜像加速服务
  5. springcloud config服务端配置(二)
  6. 神秘买家6亿元拍走,乐视大厦究竟归谁?
  7. DataList分页访问FooterTemplate模板里的控件
  8. C++技术在哪些领域中最为适用?
  9. Atitit 关于微服务的思考与理解 attilax总结 1.1. 架构的历史 微服务发展历史 Web》soa》msa 1 1.2. 微服务最大特点 独立部署 1 2. 微服务的优点 1 2.1.
  10. Core Audio APIs 技术笔记二(麦克风音量和增强设置)
  11. 上万网友自发利用“蔚蓝地图”报污染源
  12. Apple 850 订单EDI X12报文格式解析
  13. 2015年可视化研究前沿动态
  14. .net mqtt 消息收发
  15. 苹果手机计算机报不了名,iPhone8无法连接电脑并且不弹出信任对话框怎么办?...
  16. 松果在线报名系统网站源码
  17. 14-HAL库之I2C通信
  18. 许家印的中秋行程单,恒大造车的“全球化”
  19. Centos7 防火墙配置
  20. unittest的基本使用

热门文章

  1. rabittmq java spring_消息队列 RabbitMQ 与 Spring 整合使用的实例代码
  2. app网站换服务器,app切换服务器
  3. php插入成功数据不显示,PHP插入数据不成功,什么原因呢?
  4. caffe编译报错 cudnn.hpp:127:41: error: too few arguments to function ‘cudnnStatus_t cudnnSetPooling2dDe
  5. caffe 在 windows 下的配置(scripts\build_win.cmd)
  6. faster-rcnn原理及相应概念解释
  7. 好程序员web前端技术之CSS3过渡
  8. Flutter 基础Widgets Text()之TextStyle详解
  9. tomcat中实现特定路径下的图片的url访问Tomcat配置图片保存路径,图片不保存在项目路径下...
  10. 浅谈 JSON.stringify 方法