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


77

昨天发布了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一切仍然由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将以非root用户身份运行。而且由于采用了postinstall钩,NPM运行bower install && grunt assets这需要的剩余安装步骤护理和避免任何需要npm install -g的任何节点CLI工具,如凉亭,咕噜或CoffeeScript的。


只是好奇,您确定node要创建用户吗?

@am是,肯定是创建用户。在运行时docker run -it myimage /bin/bashwhoami我已登录,node这是我期望的。
thom_nic 2014年

Answers:


16

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

如果查看源,您会注意到这/data/是一个卷。因此,您在中所做的所有操作Dockerfile都会在运行时被丢弃,并被随后装入的卷覆盖。

您可以在运行时通过将CMD更改为来进行调整CMD chown -R node /data && npm start


有趣!我想我没有/没有完全理解卷的概念(我将开始阅读),也没有完全理解运行时和构建时间之间的区别。
thom_nic 2014年

3
不幸的CMD chown...是,它将无法运行,因为它将作为nodeobv的用户无权更改拥有的文件的权限而运行root。我开始怀疑是否可以在运行时写入源代码,只要该应用程序可以访问日志和运行时编译文件之类的可写位置即可。是否可以在运行时创建不属于root的卷?
thom_nic 2014年

从来没听说过。另一种选择是不使用USER节点,保留root并从开始npm su node -c "npm start"。但是根据您的需要,只要可以访问可写目录,就可以以只读方式运行。
creack 2014年

1
因此,我将接受此作为正确的答案,因为您发现了该问题/data的数量。如果我将该目录更改为其他目录,/opt/foobar或者/home/node/app,则chown可以作为构建的一部分正常工作。另外,我已经确认-chown在正在运行的容器中添加卷似乎也可以工作。我认为最简单的解决方案是不为应用程序源使用卷。我在这里更新了生成的Dockerfile:gist.github.com/thom-nic/5e561e800cbc0f71555c
thom_nic 2014年

26

检查此帖子: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

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


1
您可能必须使用以下命令显式创建用户的主目录:adduser -m --disabled-password --gecos '' r
bonh 2014年

基于Red Hat的系统(例如Fedora和CentOS)必须执行不同的操作。
gbraad

18

更新2015-09-28

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


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

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

我看了@FrançoisZaninotto在他的回答中提到的方法(您可以在此处查看完整的make脚本)。确实很棒,但是我更喜欢创建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和组,以匹配从主机OS执行脚本的用户。
  • 主机操作系统的当前工作目录(使用docker卷)挂载到我们在执行的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 Identification命令来获取主机上图像的信息,例如:

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

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

要注意的另一个限制是,我只能引用我正在执行脚本的路径中当前的文件。这是由于我装入卷的方式,因此以下操作无效:

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

最好只是转到包含图像的目录并直接对其执行。当然,我敢肯定,也有一些方法可以解决这个限制,但是对于我和我目前的需求,这是可以做到的。


2
这是一个非常简洁的概念,使用docker作为隔离/移植命令行工具的一种方式!
thom_nic 2014年

太好了!现在使用它,尽管适应了Python并集成到了我自己的docker包装器中。
Lightness Races in Orbit

(唯一的问题是,它对您可以使用的Docker映像设置了某些约束;即,它们按原样支持所有这些命令。)
Lightness Races in Orbit

很高兴您使用Python,我做了同样的事情,并且对我的脚本更加满意。不确定我是否正在关注您提出的约束问题,但是如果我理解正确,那么我认为可以合理地假设您需要使用所需的适当命令行工具来设置所需的docker映像。正在运行的
容器的

如果出现“找不到sudo命令错误”。使用echo "su $CONTAINER_USERNAME -c "在execute_as_cmd()函数。
arulraj.net,2016年

5

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

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

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

Dockerfile该行之后的所有内容都使用来执行myuser

myuser 用户具有:

  • 没有分配密码
  • 没有目录
  • 没有登录外壳
  • 没有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)

0

注意事项之所以给出此答案,是因为许多寻求非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


这不能解决OP的问题,即他们在不适用于最终图像的卷上进行了chown。将echo的输出发送到文件的tee管道看起来很奇怪,为什么不附加一个>>。向用户提供对root的完全sudo访问权限会撤消以非root用户身份运行容器的所有好处。
BMitch

这与@ yegor256给出的答案有关。我无法在评论部分中澄清它……我将其包含在地址答案中
gbraad

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.