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

在昨天的Shocker消息之后 ,似乎Docker容器内的应用程序不应该以root身份运行。 我试图更新我的Dockerfile来创build一个应用程序用户,但是改变应用程序文件的权限(虽然仍然是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/ 

我更新的dockerfile很有用,感谢@ creak澄清了卷的工作原理。 一旦初始文件被chownnpm install将作为非root用户运行。 感谢postinstall钩子,npm运行bower install && grunt assets ,负责处理剩下的安装步骤,并避免任何需要npm install -g任何节点cli工具,如bower,grunt或coffeescript。

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

如果您查看源代码 ,您会注意到/data/是一个卷。 因此,在Dockerfile执行的所有操作Dockerfile将在运行时被安装的卷丢弃和覆盖。

你可以在运行时改变你的CMD到CMD chown -R node /data && npm start

检查这个职位: 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 -mr -c /home/r/script.sh 

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

更新2015-09-28

我注意到这个post得到了一些关注。 任何人都可能有兴趣做这样的事情。 我会尝试使用Python或其他语言作为脚本执行的包装。 做本地bash脚本当我尝试通过各种parameter passing给我的容器时遇到了问题。 具体来说,shell对“和”字符的解释/转义存在问题。


我需要改变用户的一个稍微不同的原因。

我创build了一个Docker镜像 ,包含ImageMagickFfmpeg的全function安装,希望能在我的主机操作系统中对图像/video进行转换。 我的问题是,这些是命令行工具,所以通过docker执行它们稍微有点棘手,然后将结果返回到主机操作系统。 我设法通过安装泊坞窗容量。 这似乎工作正常,除了图像/video输出作为拥有的 (即用户docker容器运行),而不是执行命令的用户出来。

我看了@FrançoisZaninotto在他的回答中提到的方法(你可以在这里看到完整的make脚本)。 这真的很酷,但我更喜欢创build一个bash shell脚本的选项,然后我将在我的path上注册。 我从Makefile的方法(特别是用户/组的创build)采取了一些概念,然后我创build了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”图像上,但是可以通过编辑脚本顶部的variables来改变。

它基本上做的是:

  • 容器中创build一个用户标识和组,以匹配主机操作系统执行脚本的用户。
  • 主机操作系统的当前工作目录(使用docker卷)装载到我们在正在执行的docker容器中创build的用户的主目录
  • 将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 

我也附加到“标准输出”,所以我可以运行ImageMagick标识命令来获取我的主机上的图像信息,例如:

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

关于挂载当前目录以及允许任何任意的命令定义被传递以执行是有明显的危险的。 但是也有很多方法可以使脚本更安全。 我在我自己的非生产性的个人环境中执行这个,所以这些都不是我最关心的。 但是,如果你select扩展这个脚本,我强烈build议你考虑危险。 还值得一提的是,这个脚本不考虑OS X主机。 我从盗取想法/概念的make文件考虑到了这一点,所以你可以扩展这个脚本来做到这一点。

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

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

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

注意这个答案是给出的,因为许多人寻找非根用法将在这里结束。 请注意,这并没有解决导致问题的问题,而是解决了标题和@ 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"] 

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