首页 > 其他分享 >理解 Docker 容器中 UID 和 GID 的工作原理

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

时间:2024-03-08 21:44:41浏览次数:24  
标签:容器 UID marc 用户 server GID sleep Docker uid

理解用户名、组名、用户ID(UID)和组ID(GID)在容器内运行的进程与主机系统之间的映射是构建安全系统的重要一环。如果没有提供其他选项,容器中的进程将以root用户身份执行(除非在Dockerfile中提供了不同的UID)。本文将解释这一工作原理,如何正确授予权限,并提供示例加以说明。

逐步分析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 和组名)的映射,指定容器化进程运行的用户可以使该进程在容器内部和外部看起来像是由不同的用户拥有。

标签:容器,UID,marc,用户,server,GID,sleep,Docker,uid
From: https://www.cnblogs.com/cheyunhua/p/18061926

相关文章

  • 在Docker中,怎么快速查看本地的镜像和容器?
    在Docker中,查看本地的镜像和容器分别可以通过以下两条命令来快速实现:1.查看本地镜像要查看本地计算机上存储的所有Docker镜像,可以使用dockerimages命令。这个命令会列出所有可用的镜像,包括镜像的存储库名称、标签、镜像ID、创建时间和所占用的空间。dockerimages输出示例:......
  • 在Docker中,如何实现退出容器时候自动删除?
    在Docker中,要实现容器在退出时自动删除,可以在运行容器时使用--rm标志。--rm参数会指示Docker在容器退出后立即删除容器。以下是具体的命令示例:dockerrun--rm<image-name><command>例如,如果要运行一个基于Ubuntu镜像的容器,并在容器运行完cat/etc/hosts命令后自动删除容......
  • 在Docker中,构建镜像应该遵循哪些原则?
    构建Docker镜像时,应当遵循以下一系列最佳实践和原则,以确保镜像的高效、安全和易于维护:镜像最小化:选择尽可能小的基础镜像,如AlpineLinux,或者针对特定场景选择轻量级的基础镜像。只安装应用程序运行所必需的软件包和服务,避免无关组件和文件。在构建过程中清理临时文件和构建......
  • 在Docker中,如何批量清理临时镜像文件?
    在Docker中,所谓的“临时镜像”或“虚悬镜像”(danglingimages)是指那些没有被任何容器引用的层,通常是在构建新镜像时遗留下来的中间层。要批量清理这类镜像,你可以使用dockerimages命令结合一些过滤条件来找到它们,然后用dockerrmi命令删除。以下是批量清理临时镜像文件的步骤......
  • 在Docker中,容器退出后,通过docker ps命令查看不到,数据会丢失么?
    在Docker中,当一个容器退出(stoppedorexited)后,它将不再处于运行状态,因此,通过dockerps命令默认情况下将无法看到已退出的容器。然而,这并不意味着容器内的数据会丢失。容器退出时,其存储层中的数据通常是保留的,除非你明确地删除了容器。这意味着即便容器停止运行,只要你没有使用d......
  • Window10使用Docker-Desktop自带k8s和dashboard配置本地k8s开发环境
    安装Docker-DesktopInstallDockerDesktoponWindows下载地址:https://docs.docker.com/desktop/install/windows-install/打开Docker-Desktop,启用Kubernetes打开Docker-Desktop,启用Kubernetes点击左上角:设置,选择Kubernetes选项卡,启用Kubernetes,点击Apply&restart注意:最......
  • Docker Compose一键搭建安全测试靶场
    1.Docker快速安装1.1.ubuntu系统步骤一:安装必要的一些系统工具sudoapt-getupdatesudoapt-get-yinstallapt-transport-httpsca-certificatescurlsoftware-properties-common步骤二:安装GPG证书curl-fsSLhttps://mirrors.aliyun.com/docker-ce/linux/ubuntu/gp......
  • docker 常用命令
    docker常用命令1.dockersearch[OPTIONS]NAME#搜索DockerHub上的镜像。-q:只显示仓库名。--filter:设置过滤条件,比如搜索官方镜像可以使用--filter="is-official=true"。limit:设置搜索结果的最大条数。--no-trunc:显示完整的镜像描述,而不是截断的描述。例子......
  • docker 部署 django + mysql + vue 项目
    ​项目目录结构,在Vue和Django项目根目录下创建Dockerfile文件,在父级目录下创建docker-compose.yml文件Project#父级目录├─client#vue3项目目录│├─public│└─src│├─*files│├─*files│├─Dockerfile└─server#Django项......
  • 如何通过docker容器查看run命令
    1.当你需要迁移docker的时候,忘记的run的命令可以使用一下方法blossom-backend是你的容器名字或者容器IDdockerinspect--format'dockerrun\--name{{printf"%q".Name}}\{{-with.HostConfig}}{{-if.Privileged}}--privileged\{{-en......