在 Docker 中以非 root 用户身份运行应用程序

Posted

技术标签:

【中文标题】在 Docker 中以非 root 用户身份运行应用程序【英文标题】:Running app inside Docker as non-root user 【发布时间】:2014-08-10 02:51:24 【问题描述】:

在yesterday's news of Shocker 之后,Docker 容器中的应用似乎不应该以 root 身份运行。我尝试更新我的Dockerfile 以创建应用程序用户,但是更改应用程序文件的权限(虽然仍然是 root)似乎不起作用。我猜这是因为某些 LXC 权限可能没有授予 root 用户?

这是我的 Dockerfile:

# Node.js app Docker file

FROM dockerfile/nodejs
MAINTAINER Thom Nichols "thom@thomnichols.org"

RUN useradd -ms /bin/bash node

ADD . /data
# This next line doesn't seem to have any effect:
RUN chown -R node /data 

ENV HOME /home/node
USER node

RUN cd /data && npm install

EXPOSE 8888

WORKDIR /data

CMD ["npm", "start"]

很简单,但是当我ls -l 时,一切仍然归根用户所有:

[ node@ed7ae33e76e1:/data docker-nonroot-user ]$ ls -l /data
total 64K
-rw-r--r--  1 root root  383 Jun 18 20:32 Dockerfile
-rw-r--r--  1 root root  862 Jun 18 16:23 Gruntfile.js
-rw-r--r--  1 root root 1.2K Jun 18 15:48 README.md
drwxr-xr-x  4 root root 4.0K May 30 14:24 assets/
-rw-r--r--  1 root root  416 Jun  3 14:22 bower.json
-rw-r--r--  1 root root  930 May 30 01:50 config.js
drwxr-xr-x  4 root root 4.0K Jun 18 16:08 lib/
drwxr-xr-x 42 root root 4.0K Jun 18 16:04 node_modules/
-rw-r--r--  1 root root 2.0K Jun 18 16:04 package.json
-rw-r--r--  1 root root  118 May 30 18:35 server.js
drwxr-xr-x  3 root root 4.0K May 30 02:17 static/
drwxr-xr-x  3 root root 4.0K Jun 18 20:13 test/
drwxr-xr-x  3 root root 4.0K Jun  3 17:38 views/

My updated dockerfile works great 感谢@creak 对卷如何工作的澄清。一旦初始文件是chowned,npm install 就会以非 root 用户身份运行。多亏了 postinstall 钩子,npm 运行 bower install && grunt assets,它负责其余的安装步骤,并避免了 npm install -g 任何节点 cli 工具,如 bower、grunt 或 coffeescript。

【问题讨论】:

只是好奇,您确定node 用户正在创建吗? @a.m.是的,用户肯定是创建的。当我运行docker run -it myimage /bin/bashwhoami 时,我以node 登录,这是我所期望的。 【参考方案1】:

这个有点棘手,其实是你从图片开始的。

如果您look at the source,您会注意到/data/ 是一个卷。因此,您在 Dockerfile 中所做的一切都将在运行时被随后挂载的卷丢弃和覆盖。

您可以通过将 CMD 更改为 CMD chown -R node /data && npm start 之类的方式在运行时 chown。

【讨论】:

有趣!我想我没有/没有完全理解卷的概念(我将开始阅读),也没有完全理解运行时和构建时间之间的区别。 不幸的是,CMD chown... 不起作用,因为它将以 node 用户的身份运行,而 obv 无权更改 root 拥有的文件的权限。我开始怀疑如果源代码在运行时不可写,只要应用程序可以访问日志和运行时编译文件等可写位置,是否可以。是否可以在运行时创建不属于 root 的卷? 我不知道。另一种方法是不使用 USER 节点,保留 root 并使用su node -c "npm start" 启动 npm。但是根据您的需要,只要您有权访问可写目录,就可以以只读方式运行。 所以我将接受这个作为正确答案,因为您发现问题是/data 作为一个卷。如果我将该目录更改为其他任何目录,/opt/foobar/home/node/app,那么chown 作为构建的一部分可以正常工作。此外,我已经确认 chown-ing 正在运行的容器中的卷似乎也可以工作。我认为最简单的解决方案是不为应用程序源使用卷。我在这里更新了生成的 Dockerfile:gist.github.com/thom-nic/5e561e800cbc0f71555c【参考方案2】:

查看这篇文章:http://www.yegor256.com/2014/08/29/docker-non-root.html 在rultor.com 我们在它们自己的 Docker 容器中运行所有构建。每次在容器内运行脚本之前,我们都会切换到非 root 用户。方法如下:

adduser --disabled-password --gecos '' r
adduser r sudo
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
su -m r -c /home/r/script.sh

r 是我们正在使用的用户。

【讨论】:

您可能必须显式创建用户的主目录:adduser -m --disabled-password --gecos '' r 基于 Red Hat 的系统,例如 Fedora 和 CentOS,您必须以不同的方式执行此操作。【参考方案3】:

2015-09-28 更新

我注意到这篇文章引起了一些关注。对任何可能对做这样的事情感兴趣的人的忠告。我会尝试使用 Python 或其他语言作为脚本执行的包装器。在尝试将各种参数传递给我的容器时,我在执行本机 bash 脚本时遇到了问题。具体来说,shell 对 " 和 ' 字符的解释/转义存在问题。


出于稍微不同的原因,我需要更改用户。

我创建了一个 docker 映像,其中包含 ImageMagickFfmpeg 的全功能安装,希望我可以对图像/视频进行转换在我的主机操作系统中。我的问题是这些是命令行工具,因此通过 docker 执行它们然后将结果返回到主机操作系统会稍微复杂一些。我设法通过安装 docker 卷来实现这一点。这似乎工作正常,只是图像/视频输出由 root 拥有(即运行 docker 容器的用户),而不是执行命令的用户。

我查看了@François Zaninotto 在他的answer 中提到的方法(您可以查看完整的制作脚本here)。这真的很酷,但我更喜欢创建一个 bash shell 脚本的选项,然后我会在我的路径上注册它。我从 Makefile 方法中获取了一些概念(特别是创建用户/组),然后创建了 shell 脚本。

这是我的 dockermagick shell 脚本的示例:

#!/bin/bash

### VARIABLES

DOCKER_IMAGE='acleancoder/imagemagick-full:latest'
CONTAINER_USERNAME='dummy'
CONTAINER_GROUPNAME='dummy'
HOMEDIR='/home/'$CONTAINER_USERNAME
GROUP_ID=$(id -g)
USER_ID=$(id -u)

### FUNCTIONS

create_user_cmd()

  echo \
    groupadd -f -g $GROUP_ID $CONTAINER_GROUPNAME '&&' \
    useradd -u $USER_ID -g $CONTAINER_GROUPNAME $CONTAINER_USERNAME '&&' \
    mkdir --parent $HOMEDIR '&&' \
    chown -R $CONTAINER_USERNAME:$CONTAINER_GROUPNAME $HOMEDIR


execute_as_cmd()

  echo \
    sudo -u $CONTAINER_USERNAME HOME=$HOMEDIR


full_container_cmd()

  echo "'$(create_user_cmd) && $(execute_as_cmd) $@'"


### MAIN

eval docker run \
    --rm=true \
    -a stdout \
    -v $(pwd):$HOMEDIR \
    -w $HOMEDIR \
    $DOCKER_IMAGE \
    /bin/bash -ci $(full_container_cmd $@)

此脚本绑定到“acleancoder/imagemagick-full”图像,但可以通过编辑脚本顶部的变量来更改。

它的基本作用是:

容器中创建一个用户ID和组,以匹配从主机操作系统执行脚本的用户。 将 host OS 的当前工作目录(使用 docker 卷)挂载到 home 目录 中,供我们在执行 docker 容器中创建的用户使用>. 将 tmp 目录设置为 容器 的工作目录。 传递传递给脚本的任何参数,然后由正在执行的 docker 容器的“/bin/bash”执行。

现在我可以对主机操作系统上的文件运行 ImageMagick/Ffmpeg 命令了。例如,假设我想将图像 MyImage.jpeg 转换为 PNG 文件,我现在可以执行以下操作:

$ cd ~/MyImages
$ ls
  MyImage.jpeg
$ dockermagick convert MyImage.jpeg Foo.png
$ ls
  Foo.png MyImage.jpeg

我还附加了“stdout”,因此我可以运行 ImageMagick identify 命令来获取主机上图像的信息,例如:

$ dockermagick identify MyImage.jpeg
  MyImage.jpeg JPEG 640x426 640x426+0+0 8-bit DirectClass 78.6KB 0.000u 0:00.000

挂载当前目录并允许传递任意命令定义以供执行存在明显的危险。但也有很多方法可以使脚本更安全/可靠。我在自己的非生产个人环境中执行此操作,所以这些对我来说不是最关心的。但是,如果您选择扩展此脚本,我强烈建议您考虑到危险。值得一提的是,这个脚本没有考虑 OS X 主机。我窃取想法/概念的make file 确实考虑到了这一点,因此您可以扩展此脚本来这样做。

另一个需要注意的限制是我只能引用当前在我正在执行脚本的路径中的文件。这是因为我安装卷的方式,所以以下方法不起作用:

$ cd ~/MyImages
$ ls
  MyImage.jpeg
$ dockermagick convert ~/DifferentDirectory/AnotherImage.jpeg Foo.png
$ ls
  MyImage.jpeg

最好只是转到包含图像的目录并直接对其执行。当然,我确信也有办法绕过这个限制,但对于我和我目前的需求,这会是可行的。

【讨论】:

这是一个非常简洁的概念,使用 docker 作为隔离/移植命令行工具的一种方式! 这太棒了!现在使用它,虽然适应了 Python 并集成到我自己的 docker 包装器中。 (唯一的问题是它对您可以使用的 Docker 映像设置了某些限制;也就是说,它们按原样支持所有这些命令。) 很高兴您使用 Python,我也这样做了,并且对我的脚本更满意。不确定我是否完全关注您提出的约束问题,但如果我理解正确,那么我认为假设您需要使用所需的适当命令行工具设置所需的 docker 映像是合理的。运行包含的交叉通信可能是一个有趣的问题... :) 如果你得到“sudo command not found error”。在 execute_as_cmd() 函数中使用echo "su $CONTAINER_USERNAME -c "【参考方案4】:

注意给出这个答案是因为许多寻找非 root 用户的人最终会来到这里。请注意,这并没有解决导致问题的问题,而是解决了 @yegor256 给出的答案的标题和说明,该答案在容器内使用非 root 用户。这个答案解释了如何为非 debian/non-ubuntu 用例实现这一点。这并没有解决卷的问题。

在基于 Red Hat 的系统上,例如 Fedora 和 CentOS,这可以通过以下方式完成:

RUN adduser user && \
    echo "user ALL=(root) NOPASSWD:ALL" | tee -a /etc/sudoers.d/user && \
    chmod 0440 /etc/sudoers.d/user

在您的 Dockerfile 中,您可以通过以下方式以该用户身份运行命令:

RUN su - user -c "echo Hello $HOME"

命令可以这样运行:

CMD ["su","-","user","-c","/bin/bash"]

可以在此处找到一个示例: https://github.com/gbraad/docker-dev/commit/644c51002f4b8e6fe5bb745638542a4c3d908b16

【讨论】:

这并没有解决 OP 的问题,即他们对不适用于最终图像的卷进行了 chown。将 echo 的输出发送到管道以对文件进行 tee 处理似乎很奇怪,为什么不直接附加 >>。并且授予用户对 root 的完全 sudo 访问权限会取消以非 root 用户身份运行容器的所有好处。 这与@yegor256 给出的答案有关。我无法在 cmets 部分澄清它......我将把它包含在地址的答案中 更新信息:gbraad.nl/blog/non-root-user-inside-a-docker-container.html【参考方案5】:

注意我在这里回答是因为,鉴于通用标题,当您寻找“以非 root 用户身份在 Docker 中运行应用程序”的解决方案时,这个问题会在 google 中弹出。希望对困在这里的人有所帮助。

使用 Alpine Linux,您可以像这样创建系统用户:

RUN adduser -D -H -S -s /bin/false -u 1000 myuser

此行之后Dockerfile 中的所有内容都使用myuser 执行。

myuser 用户拥有:

未分配密码 没有home目录 没有登录shell 没有 root 访问权限。

这是来自adduser --help

-h DIR      Home directory
-g GECOS    GECOS field
-s SHELL    Login shell
-G GRP      Add user to existing group
-S          Create a system user
-D          Don't assign a password
-H          Don't create home directory
-u UID      User id
-k SKEL     Skeleton directory (/etc/skel)

【讨论】:

以上是关于在 Docker 中以非 root 用户身份运行应用程序的主要内容,如果未能解决你的问题,请参考以下文章

如何在 docker 中以非 root 用户身份写入卷容器?

如何在 Docker 容器中以非 root 用户身份启动 cron?

以非 root 用户身份运行 Nginx

如何在官方 docker php 映像上以非 root 用户身份运行 composer

如何在 Debian 上以非 root 用户身份运行 Spring Boot 应用程序?

以非 root 身份运行闪亮的服务器