我的编程空间,编程开发者的网络收藏夹
学习永远不晚

理解 Docker 容器中 UID 和 GID 的工作原理

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

理解 Docker 容器中 UID 和 GID 的工作原理

逐步分析uid/gid安全性

首先,让我们回顾一下uid和gid是如何实现的。Linux内核负责管理uid和gid空间,使用内核级系统调用来确定是否应该授予请求的特权。例如,当一个进程尝试写入文件时,内核会检查创建该进程的uid和gid,以确定它是否具有足够的特权来修改文件。这里不使用用户名,而是使用uid。

在服务器上运行 Docker 容器时,仍然只有一个内核。容器化带来的巨大价值之一是所有这些独立的进程可以继续共享一个内核。这意味着即使在运行 Docker 容器的服务器上,整个 uid 和 gid 的世界仍由一个单一内核控制。

因此,在不同的容器中不能使用相同的 uid 分配给不同的用户。这是因为在常见的 Linux 工具中显示的用户名(和组名)并不是内核的一部分,而是由外部工具(如 /etc/passwd、LDAP、Kerberos 等)管理。因此,你可能会看到不同的用户名,但是即使在不同的容器中,对于相同的 uid/gid,你也不能拥有不同的权限。这一点一开始可能会让人感到相当困惑,所以让我们通过几个例子来说明一下:

简单的Docker运行

我将首先以普通用户(marc)的身份登录到一个属于docker组的服务器上。这样我就可以在不使用sudo命令的情况下启动docker容器。然后,从容器外部,让我们来看看这个过程是如何呈现的。

marc@server:~$ docker run -d ubuntu:latest sleep infinity
92c57a8a4eda60678f049b906f99053cbe3bf68a7261f118e411dee173484d10
marc@server:~$ ps aux | grep sleep
root 15638 0.1 0.0 4380 808 ? Ss 19:49 0:00 sleep infinity

尽管我从未输入过sudo,也不是root用户,但我执行的sleep命令以root用户身份启动并具有root权限。我如何知道它具有root权限?容器内的root是否等同于容器外的root?是的,因为正如我提到的,有一个单一的内核和一个共享的uid和gid池。由于容器外显示的用户名是“root”,我可以确定容器内的进程是以具有uid = 0的用户启动的。

带有定义用户的Dockerfile

当我在 Dockerfile 中创建一个不同的用户并以该用户身份启动命令时会发生什么?为了简化这个例子,我这里没有指定 gid,但相同的概念也适用于组 id。

首先,我正在以用户名为“marc”的用户身份运行这些命令,该用户的用户ID为1001。

marc@server:~$ echo $UID
1001

Dockerfile文件:

FROM ubuntu:latest
RUN useradd -r -u 1001 -g appuser appuser
USER appuser
ENTRYPOINT [“sleep”, “infinity”]

构建:

marc@server:~$ docker build -t test .
Sending build context to Docker daemon 14.34 kB
Step 1/4 : FROM ubuntu:latest
 — -> f49eec89601e
Step 2/4 : RUN useradd -r -u 1001 appuser
 — -> Running in 8c4c0a442ace
 — -> 6a81547f335e
Removing intermediate container 8c4c0a442ace
Step 3/4 : USER appuser
 — -> Running in acd9e30b4aba
 — -> fc1b765e227f
Removing intermediate container acd9e30b4aba
Step 4/4 : ENTRYPOINT sleep infinity
 — -> Running in a5710a32a8ed
 — -> fd1e2ab0fb75
Removing intermediate container a5710a32a8ed
Successfully built fd1e2ab0fb75
marc@server:~$ docker run -d test
8ad0cd43592e6c4314775392fb3149015adc25deb22e5e5ea07203ff53038073
marc@server:~$ ps aux | grep sleep
marc 16507 0.3 0.0 4380 668 ? Ss 20:02 0:00 sleep infinity
marc@server:~$ docker exec -it 8ad0 /bin/bash
appuser@8ad0cd43592e:/$ ps aux | grep sleep
appuser 1 0.0 0.0 4380 668 ? Ss 20:02 0:00 sleep infinity

这里到底发生了什么,这意味着什么?我构建了一个 Docker 镜像,其中有一个名为“appuser”的用户,该用户的 uid 为 1001。在我的测试服务器上,我使用的帐户名为“marc”,uid 也是 1001。当我启动容器时,sleep 命令以 appuser 的身份执行,因为 Dockerfile 包含了“USER appuser”这一行。但实际上这并不是以 appuser 的身份运行,而是以 Docker 镜像中被识别为 appuser 的用户的 uid 运行。

当我检查容器外运行的进程时,我发现它映射到用户“marc”,但在容器内部,它映射到用户“appuser”。这两个用户名只是显示它们的执行上下文所知道的映射到1001的用户名。

这并不是非常重要。但重要的是要知道,在容器内部,用户“appuser”获得了来自容器外部用户“marc”的权限和特权。在Linux主机上授予用户marc或uid 1001的权限也将授予容器内的appuser这些权限。

如何控制容器的访问权限

另一种选择是在运行 Docker 容器时指定用户名或用户ID,以及组名或组ID。

再次使用上面的初始示例。

marc@server:~$ docker run -d --user 1001 ubuntu:latest sleep infinity
84f436065c90ac5f59a2256e8a27237cf8d7849d18e39e5370c36f9554254e2b
marc@server$ ps aux | grep sleep
marc     17058 0.1 0.0 4380 664 ? Ss 21:23 0:00 sleep infinity

我在这里做了什么?我创建了容器以1001用户身份启动。因此,当我执行诸如ps或top(或大多数监控工具)之类的命令时,进程映射到“marc”用户。

有趣的是,当我进入该容器时,你会发现1001用户在/etc/passwd文件中没有条目,并在容器的bash提示符中显示为“I have no name!”。

marc@server:~$ docker exec -it 84f43 /bin/bash
I have no name!@84f436065c90:/$

重要的是要注意,在创建容器时指定用户标志也会覆盖 Dockerfile 中的值。还记得第二个例子吗?那时我使用了一个 Dockerfile,其中的 uid 映射到本地主机上的不同用户名。当我们在命令行上使用用户标志来启动一个执行“sleep infinity”进程的容器时,会发生什么呢?

marc@server:$ docker run -d test
489a236261a0620e287e434ed1b15503c844648544694e538264e69d534d0d65
marc@server:~$ ps aux | grep sleep
marc     17689 0.2 0.0 4380 680 ? Ss 21:28 0:00 sleep infinity
marc@server:~$ docker run --user 0 -d test
ac27849fcbce066bad37190b5bf6a46cf526f56d889af61e7a02c3726438fa7a
marc@server:~$ ps aux | grep sleep
marc     17689 0.0 0.0 4380 680 ? Ss 21:28 0:00 sleep infinity
root     17783 0.3 0.0 4380 668 ? Ss 21:28 0:00 sleep infinity

在上面的最后一个示例中,您可以看到我最终得到了两个运行睡眠进程的容器,一个是“marc”,另一个是“root”。这是因为第二个命令通过在命令行上传递--user标志来更改了用户ID。

总结

现在我们已经探讨了这一点,可以理解以有限权限运行容器的方式都利用了主机的用户系统:

  • 如果容器内部的进程正在执行的已知 uid,那么简单地限制对主机系统的访问,使容器中的 uid 仅具有有限访问权限就可以了。
  • 更好的解决方案是使用--user以已知 uid 启动容器(也可以使用用户名,但请记住这只是提供主机用户名系统中的 uid 的一种更友好的方式),然后限制主机上您决定容器将以其运行的 uid 的访问权限。
  • 由于容器到主机的 uid 和用户名(以及 gid 和组名)的映射,指定容器化进程运行的用户可以使该进程在容器内部和外部看起来像是由不同的用户拥有。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

理解 Docker 容器中 UID 和 GID 的工作原理

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

理解 Docker 容器中 UID 和 GID 的工作原理

如果容器内部的进程正在执行的已知 uid,那么简单地限制对主机系统的访问,使容器中的 uid 仅具有有限访问权限就可以了。
DockerUIDGID2024-11-30

Docker镜像与容器的工作原理是什么

本篇内容介绍了“Docker镜像与容器的工作原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一. bootfs和rootfs通常而言
2023-06-30

Docker中Cgroup的原理和作用

本篇内容主要讲解“Docker中Cgroup的原理和作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Docker中Cgroup的原理和作用”吧!内核中强大的工具cgroup,不仅可以限制被Na
2023-06-20

docker笔记31_1-容器之间通讯方式及Flannel工作原理

转自:https://blog.csdn.net/weixin_29115985/article/details/78963125 一. 容器之间通讯方式k8s里面容器是存在于pod里面的,所以容器之间通讯,一般分为三种类型:(1) pod
2023-06-04

如何理解操作系统中的Hosts文件工作原理和作用

本篇内容介绍了“如何理解操作系统中的Hosts文件工作原理和作用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、什么是Hosts文件?Ho
2023-06-13

电容式传感器的工作原理是什么

电容式传感器的工作原理是基于物体与传感器之间形成的电场。当一个物体靠近传感器时,物体会改变传感器的电场分布。这种改变会导致传感器上的电容发生变化。通过测量电容的变化,可以确定物体与传感器之间的距离或其他相关物理量。电容式传感器具有高精度、快
2023-08-10

MySQL中的查询优化器工作原理解析

MySQL是一种常用的关系型数据库管理系统,广泛应用于各种Web应用程序和大型企业级系统中。在MySQL的日常使用中,查询语句是最常见和重要的操作之一。为了提高查询效率和性能,MySQL引入了查询优化器。查询优化器是MySQL内部的一个组件
2023-10-22

理解Golang中线程与协程的工作原理

Golang中线程与协程的工作原理在Go语言(Golang)中,线程和协程是非常重要的概念,它们是并发编程的基本组成部分。理解它们的工作原理对于开发高效的并发程序非常重要。本文将深入探讨Golang中线程与协程的工作原理,并通过具体的代码
理解Golang中线程与协程的工作原理
2024-02-29

谈谈你对Netty中,Pipeline工作原理的理解?

每个Context中又会包含一个ChannelHandler,我们通过addLast()方法往Pipeline中添加的对象,并且Handler的添加顺序会影响代码的执行顺序。而这些Handler本质上都是实现编码和解码的功能,不管是编码器还

Vue3中key的作用和工作原理是什么

这篇文章主要介绍“Vue3中key的作用和工作原理是什么”,在日常操作中,相信很多人在Vue3中key的作用和工作原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue3中key的作用和工作原理是什么
2023-06-20

深入理解z-index的工作原理和应用技巧

z-index是CSS中控制元素层级的属性,但其工作原理并不简单。本文将深入探讨z-index的工作原理,包括堆叠上下文、层叠顺序和层叠上下文等概念,并介绍z-index的应用技巧,如如何避免z-index的陷阱、如何使用z-index实现复杂布局等。
2023-05-19

Golang中接口的工作原理和特性深入解析

探索Golang中接口的实现原理与特性引言:Golang是一种现代化的编程语言,凭借其简洁性、高效性和强大的并发支持而受到广泛关注。其中,接口是Golang中一个重要的特性,使得代码能够更加灵活、可扩展和易于维护。本文旨在深入探索Gola
Golang中接口的工作原理和特性深入解析
2024-01-24

深入解析Golang中锁的工作原理

Golang中锁的工作原理深度剖析引言:在并发编程中,避免竞态条件(race condition)是至关重要的。为了实现线程安全,Golang提供了丰富的锁机制。本文将深入剖析Golang中锁的工作原理,并提供具体的代码示例。一、互斥锁(M
深入解析Golang中锁的工作原理
2023-12-28

热门标签

编程热搜

编程资源站

目录