作为非 root 用户在 Docker 中运行应用程序

昨天的震惊新闻之后,似乎 Docker 容器中的应用程序不应该以 root 用户身份运行。我尝试更新我的 Dockerfile来创建一个应用程序用户,但是改变应用程序文件的权限(虽然仍然根)似乎不起作用。我猜这可能是因为某些 LXC 权限没有授予 root 用户?

这是我的文件:

# 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时,所有东西仍然归 root 用户所有:

[ 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/

由于@creak 对卷的工作方式进行了澄清,我的更新后的 dockerfile 工作得非常好。初始文件被 chowned 之后,npm install作为非根用户运行。多亏了 postinstall钩子,npm 运行 bower install && grunt assets,它负责剩下的安装步骤,并且避免了 npm install -g任何节点 cli 工具(如 bower、 grunt 或 coffee eescript)的需要。

75059 次浏览

这是一个有点棘手,它实际上是由于你从图像开始。

如果是 看看源头,则会注意到 /data/是一个卷。因此,您在 Dockerfile中所做的一切都将在运行时被挂载的卷丢弃和重写。

您可以在运行时通过将您的 CMD 更改为类似于 CMD chown -R node /data && npm start的内容。

检查这篇文章: http://www.yegor256.com/2014/08/29/docker-non-root.htmlRutor.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是我们正在使用的用户。

更新2015-09-28

我注意到这篇文章引起了一些关注。对于那些可能有兴趣做这类事情的人,我有一个建议。我会尝试使用 Python 或其他语言作为脚本执行的包装器。在执行本机 bash 脚本时,我在尝试将各种参数传递到容器时遇到了问题。特别是 shell 对“和”字符的解释/转义存在问题。


我需要更改用户的原因稍有不同。

我创建了一个 码头图像,其中包含了 图像魔术Ffmpeg的全功能安装,我希望能够在我的主机操作系统中对图像/视频进行转换。我的问题是,这些都是命令行工具,所以通过 docker 执行它们,然后将结果返回到主机操作系统,这稍微有点棘手。我设法允许这通过安装一个码头卷。除了图像/视频输出显示为由 (即 docker 容器所运行的用户)拥有,而不是执行命令的用户之外,这似乎还可以正常工作。

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

下面是我的 码头魔术 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/image-magick-full”图像,但是可以通过编辑脚本顶部的变量来更改。

它的基本功能是:

  • 集装箱中创建一个用户 ID 和组,以匹配从 主机操作系统执行脚本的用户。
  • 为我们在执行的 码头集装箱中创建的用户将当前的 主机操作系统工作目录(使用 docker 卷)安装到 主目录中。
  • 将 tmp 目录设置为 集装箱的工作目录。
  • 传递任何传递给脚本的参数,然后由正在执行的 码头集装箱的“ /bin/bash”执行该脚本。

现在我能够对主机操作系统上的文件运行 ImageMagick/Ffmpeg命令。例如,假设我想将一个图像 MyImage.jpeg转换成 巴布亚新几内亚文件,我现在可以执行以下操作:

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

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

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

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

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

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

最好只是转到包含映像的目录并直接对其执行。当然,我相信也有办法可以绕过这个限制,但是对于我和我目前的需求来说,这就够了。

注意 : < em > 之所以给出这个答案,是因为许多寻找非 root 用法的人最终都会出现在这里。注意,这并没有解决导致问题的问题,而是解决了@yegor256给出的答案的标题和说明,它在容器中使用了一个非 root 用户。这个答案解释了如何为非 debian/非 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

注意 : < em > 我在这里回答这个问题是因为,给定的通用标题,这个问题会在你寻找“以非 root 用户身份在 Docker 中运行应用程序”的解决方案时在 google 中弹出。希望它能帮助那些被困在这里的人。

使用 阿尔卑斯山 Linux,您可以创建这样的系统用户:

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

这一行之后的 Dockerfile中的所有内容都用 myuser执行。

myuser用户有:

  • 没有指定密码
  • 没有 目录
  • 没有登录 shell
  • 没有 访问权限。

这是来自 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)