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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24308760/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 17:30:34  来源:igfitidea点击:

Running app inside Docker as non-root user

node.jsdockerlxc

提问by thom_nic

After yesterday's news of Shocker, it seems like apps inside a Docker container should not be run as root. I tried to update my Dockerfileto create an app user however changing permissions on app files (while still root) doesn't seem to work. I'm guessing this is because some LXC permission is not being granted to the root user maybe?

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

Here's my Dockerfile:

这是我的 Dockerfile:

# Node.js app Docker file

FROM dockerfile/nodejs
MAINTAINER Thom Nichols "[email protected]"

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"]

Pretty straightforward, but when I ls -leverything is still owned by root:

很简单,但是当我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/


My updated dockerfile works greatthanks to @creak's clarification of how volumes work. Once the initial files are chowned, npm installis run as the non-root user. And thanks to a postinstallhook, npm runs bower install && grunt assetswhich takes care of the remaining install steps and avoids any need to npm install -gany node cli tools like bower, grunt or coffeescript.

由于@creak对卷如何工作的说明,我更新后的 dockerfile 工作得很好。一旦初始文件被chown编辑,npm install以非 root 用户身份运行。多亏了一个postinstall钩子,npm 会运行bower install && grunt assets,它负责剩余的安装步骤,并避免对npm install -g任何节点 cli 工具(如 bower、grunt 或 coffeescript)的任何需要。

采纳答案by creack

This one is a bit tricky, it is actually due to the image you start from.

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

If you look at the source, you notice that /data/is a volume. So everything you do in the Dockerfilewill be discarded and overridden at runtime by the volume that gets mounted then.

如果您查看源代码,您会注意到这/data/是一个卷。因此,您在 中所做的一切都Dockerfile将在运行时被安装的卷丢弃并覆盖。

You can chown at runtime by changing your CMD to something like CMD chown -R node /data && npm start.

您可以通过将 CMD 更改为类似CMD chown -R node /data && npm start.

回答by yegor256

Check this post: http://www.yegor256.com/2014/08/29/docker-non-root.htmlIn rultor.comwe run all builds in their own Docker containers. And every time before running the scripts inside the container, we switch to a non-root user. This is how:

查看这篇文章:http: //www.yegor256.com/2014/08/29/docker-non-root.htmlrultor.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

ris the user we're using.

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

回答by ctrlplusb

Update 2015-09-28

更新 2015-09-28

I have noticed this post getting a bit of attention. A word of advice for anyone who is potentially interested in doing something like this. I would try to use Python or another language as a wrapper for your script executions. Doing native bash scripts I had problems when trying to pass through a variety of arguments to my containers. Specifically there was issues with the interpretation/escaping of " and ' characters by the shell.

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



I was needing to change the user for a slightly different reason.

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

I created a docker imagehousing a full featured install of ImageMagickand Ffmpegwith a desire that I could do transformations on images/videos within my host OS. My problem was that these are command line tools, so it is slightly trickier to execute them via docker and then get the results back into the host OS. I managed to allow for this by mounting a docker volume. This seemed to work okay except that the image/video output was coming out as being owned by root(i.e. the user the docker container was running as), rather than the user whom executed the command.

我创建了一个码头工人形象住房一个全功能的安装ImageMagick的ffmpeg的一个愿望,我可以在我的主机操作系统中做图像/视频转换。我的问题是这些是命令行工具,所以通过 docker 执行它们然后将结果返回到主机操作系统稍微有点棘手。我设法通过安装 docker 卷来实现这一点。这似乎工作正常,除了图像/视频输出是由root拥有的(即运行 docker 容器的用户),而不是执行命令的用户。

I looked at the approach that @Fran?ois Zaninotto mentioned in his answer(you can see the full make script here). It was really cool, but I preferred the option of creating a bash shell script that I would then register on my path. I took some of the concepts from the Makefile approach (specifically the user/group creation) and then I created the shell script.

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

Here is an example of my dockermagickshell script:

这是我的dockermagickshell 脚本的示例:

#!/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 $@)

This script is bound to the 'acleancoder/imagemagick-full' image, but that can be changed by editing the variable at the top of the script.

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

What it basically does is:

它的主要作用是:

  • Create a user id and group within the containerto match the user who executes the script from the host OS.
  • Mounts the current working directory of the host OS(using docker volumes) into home directoryfor the user we create within the executing docker container.
  • Sets the tmp directory as the working directory for the container.
  • Passes any arguments that are passed to the script, which will then be executed by the '/bin/bash' of the executing docker container.
  • 容器内创建用户 ID 和组以匹配从主机操作系统执行脚本的用户。
  • 主机操作系统的当前工作目录(使用 docker 卷)挂载到我们在执行docker 容器中创建的用户的主目录
  • 将 tmp 目录设置为容器的工作目录。
  • 传递传递给脚本的任何参数,然后由正在执行的docker 容器的“ /bin/bash”执行。

Now I am able to run the ImageMagick/Ffmpegcommands against files on my host OS. For example, say I want to convert an image MyImage.jpeginto a PNGfile, I could now do the following:

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

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

I have also attached to the 'stdout' so I could run the ImageMagick identify command to get info on an image on my host, for e.g.:

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

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

There are obvious dangers about mounting the current directory and allowing any arbitrary command definition to be passed along for execution. But there are also many ways to make the script more safe/secure. I am executing this in my own non-production personal environment, so these are not of highest concern for me. But I would highly recommend you take the dangers into consideration should you choose to expand upon this script. It's also worth me mentioning that this script doesn't take an OS X host into consideration. The make filethat I steal ideas/concepts from does take this into account, so you could extend this script to do so.

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

Another limitation to note is that I can only refer to files currently in the path for which I am executing the script. This is because of the way I am mounting the volumes, so the following would not work:

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

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

It's best just to go to the directory containing the image and execute against it directly. Of course I am sure there are ways to get around this limitation too, but for me and my current needs, this will do.

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

回答by Rotareti

Note: I answer here because, given the generic title, this Question pops up in google when you look for a solution to "Running app inside Docker as non-root user". Hope it helps those who are stranded here.

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

With AlpineLinux you can create a system user like this:

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

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

Everything in the Dockerfileafter this line is executed with myuser.

Dockerfile此行之后的所有内容都使用myuser.

myuseruser has:

myuser用户有:

  • no password assigned
  • no homedir
  • no login shell
  • no rootaccess.
  • 未分配密码
  • 没有家庭目录
  • 没有登录外壳
  • 没有root权限。

This is from adduser --help:

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

回答by gbraad

Note: This answer is given because many people looking for non-root usage will end up here. Beware, this does not address the issue that caused the problem, but is addressing the title and clarification to an answer given by @yegor256, which uses a non-root user inside the container. This answer explains how to accomplish this for non-debian/non-ubuntu use-case. This is not addressing the issue with volumes.

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

On Red Hat-based systems, such as Fedora and CentOS, this can be done in the following way:

在基于 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

In your Dockerfile you can run commands as this user by doing:

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

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

And the command can be run as:

该命令可以运行为:

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

An example of this can be found here: https://github.com/gbraad/docker-dev/commit/644c51002f4b8e6fe5bb745638542a4c3d908b16

一个例子可以在这里找到:https: //github.com/gbraad/docker-dev/commit/644c51002f4b8e6fe5bb745638542a4c3d908b16