设为首页 收藏本站
查看: 750|回复: 0

[经验分享] 理解 docker 容器中的 uid 和 gid-Python & Golang 学习

[复制链接]
YunVN网友  发表于 2019-2-20 07:30:32 |阅读模式
  默认情况下,容器中的进程以 root 用户权限运行,并且这个 root 用户和宿主机中的 root 是同一个用户。听起来是不是很可怕,因为这就意味着一旦容器中的进程有了适当的机会,它就可以控制宿主机上的一切!本文我们将尝试了解用户名、组名、用户>


先来了解下 uid 和 gid
  uid 和 gid 由 Linux 内核负责管理,并通过内核级别的系统调用来决定是否应该为某个请求授予特权。比如当进程试图写入文件时,内核会检查创建进程的 uid 和 gid,以确定它是否有足够的权限修改文件。注意,内核使用的是 uid 和 gid,而不是用户名和组名。
  简单起见,本文中剩下的部分只拿 uid 进行举例,系统对待 gid 的方式和 uid 基本相同。
  很多同学简单地把 docker 容器理解为轻量的虚拟机,虽然这简化了理解容器技术的难度但是也容易带来很多的误解。事实上,与虚拟机技术不同:同一主机上运行的所有容器共享同一个内核(主机的内核)。容器化带来的巨大价值在于所有这些独立的容器(其实是进程)可以共享一个内核。这意味着即使由成百上千的容器运行在 docker 宿主机上,但内核控制的 uid 和 gid 则仍然只有一套。所以同一个 uid 在宿主机和容器中代表的是同一个用户(即便在不同的地方显示了不同的用户名)。

  注意,由于普通的用来显示用户名的 Linux 工具并不属于内核(比如>  如果你已经了解了 Linux 的 user namespace 技术,参考《Linux Namespace : User》,你需要注意的是到目前为止,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 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
  转载出处:https://www.cnblogs.com/sparkdev/p/9614164.html


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

Python 自动化运维进阶课程
  此课程为面授班和网络班,一共 15 个课时,每周上一个全天,历时4个月。附加:录播视频+笔记+答疑2019-4月份开课
  现在报名即可享受早鸟价
  您可以添加我们的小助手WeChat:17812796384 咨询。



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-674581-1-1.html 上篇帖子: 修改docker默认网桥 下篇帖子: 『高级篇』docker之服务发现、部署更新和扩容(七)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表