首页 文章

管理Docker共享卷权限的(最佳)方法是什么?

提问于
浏览
284

我一直在玩Docker一段时间,并在处理持久数据时继续寻找相同的问题 .

我创建 Dockerfileexpose a volume或使用 --volumes-frommount a host folder inside my container .

我应该对主机上的共享卷应用哪些权限?

我可以想到两个选择:

  • 到目前为止,我已经为每个人提供了读/写访问权限,因此我可以从Docker容器中写入该文件夹 .

  • 将用户从主机映射到容器,这样我就可以分配更细化的权限 . 不确定这是否可行,但没有找到太多相关信息 . 到目前为止,我所能做的就是以某个用户身份运行容器: docker run -i -t -user="myuser" postgres ,但此用户的UID与我的主机 myuser 不同,因此权限不起作用 . 此外,我不确定映射用户是否会带来一些安全风险 .

还有其他选择吗?

你们这些人/女士如何处理这个问题?

13 回答

  • 28

    和你一样,我一直在寻找一种方法将用户/组从主机映射到docker容器,这是我迄今为止找到的最短路径:

    version: "3"
        services:
          my-service:
            .....
            volumes:
              # take uid/gid lists from host
              - /etc/passwd:/etc/passwd:ro
              - /etc/group:/etc/group:ro
              # mount config folder
              - path-to-my-configs/my-service:/etc/my-service:ro
            .....
    

    这是我的docker-compose.yml的摘录 .

    这个想法是从主机到容器挂载(以只读模式)用户/组列表,因此在容器启动后它将具有与主机相同的uid->用户名(以及组)匹配 . 现在,您可以在容器内配置服务的用户/组设置,就像它在主机系统上工作一样 .

    当您决定将容器移动到另一个主机时,您只需将服务配置文件中的用户名更改为该主机上的用户名 .

  • 15

    这是一种仍然使用仅数据容器的方法,但不要求它与应用程序容器同步(就具有相同的uid / gid而言) .

    据推测,您希望在容器中运行某个应用程序作为非root $ USER而不使用登录shell .

    在Dockerfile中:

    RUN useradd -s /bin/false myuser
    
    # Set environment variables
    ENV VOLUME_ROOT /data
    ENV USER myuser
    
    ...
    
    ENTRYPOINT ["./entrypoint.sh"]
    

    然后,在entrypoint.sh中:

    chown -R $USER:$USER $VOLUME_ROOT
    su -s /bin/bash - $USER -c "cd $repo/build; $@"
    
  • 0

    To share folder between docker host and docker container, try below command

    $ docker run -v“$(pwd):$(pwd)” - i -t ubuntu

    -v标志将当前工作目录挂载到容器中 . 当装入装入卷的主机目录不存在时,Docker会自动为您在主机上创建此目录,

    但是,这里有两个问题:

    • 如果您是 non-root user ,则无法写入已装入的卷,因为共享文件将归主机中的其他用户所有,

    • 你不应该与笔记本电脑上的用户/ Jenkins相匹配,

    Solution:

    容器:创建用户说'testuser',默认情况下用户ID将从1000开始,

    主持人:创建一个组名为'testgroup',组ID为1000,并将目录chown到新组(testgroup)

  • -2

    如果您使用Docker Compose,请以previleged模式启动容器:

    wordpress:
        image: wordpress:4.5.3
        restart: always
        ports:
          - 8084:80
        privileged: true
    
  • 5

    我的方法是检测当前的UID / GID然后在容器内创建这样的用户/组并执行他下面的脚本,因此他将创建的所有文件将与运行脚本的用户匹配:

    # get location of this script no matter what your current folder is, this might break between shells so make sure you run bash
    LOCAL_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
    
    # get current IDs
    USER_ID=$(id -u)
    GROUP_ID=$(id -g)
    
    echo "Mount $LOCAL_DIR into docker, and match the host IDs ($USER_ID:$GROUP_ID) inside the container."
    
    docker run -v $LOCAL_DIR:/host_mount -i debian:9.4-slim bash -c "set -euo pipefail && groupadd -r -g $GROUP_ID lowprivgroup && useradd -u $USER_ID lowprivuser -g $GROUP_ID && cd /host_mount && su -c ./runMyScriptAsRegularUser.sh lowprivuser"
    
  • 8

    好的,现在正在tracked at docker issue #7198

    现在,我正在使用您的第二个选项来解决这个问题:

    将用户从主机映射到容器

    Dockerfile

    #=======
    # Users
    #=======
    # TODO: Idk how to fix hardcoding uid & gid, specifics to docker host machine
    RUN (adduser --system --uid=1000 --gid=1000 \
            --home /home/myguestuser --shell /bin/bash myguestuser)
    

    CLI

    # DIR_HOST and DIR_GUEST belongs to uid:gid 1000:1000
    docker run -d -v ${DIR_HOST}:${DIR_GUEST} elgalu/myservice:latest
    

    UPDATE 我目前更倾向于Hamy answer

  • 0

    对于大多数情况来说,这可能不是最好的方式,但它还没有被提及,所以它可能对某人有所帮助 .

    • 绑定装载主机卷

    Host folder FOOBAR is mounted in container /volume/FOOBAR

    • 修改容器的启动脚本以查找您感兴趣的卷的GID

    $ TARGET_GID=$(stat -c "%g" /volume/FOOBAR)

    • 确保您的用户属于具有此GID的组(您可能必须创建新组) . 对于这个例子,我假装我的软件在容器内运行为 nobody 用户,所以我想确保 nobody 属于组ID等于 TARGET_GID 的组
    EXISTS=$(cat /etc/group | grep $TARGET_GID | wc -l)
    
      # Create new group using target GID and add nobody user
      if [ $EXISTS == "0" ]; then
        groupadd -g $TARGET_GID tempgroup
        usermod -a -G tempgroup nobody
      else
        # GID exists, find group name and add
        GROUP=$(getent group $TARGET_GID | cut -d: -f1)
        usermod -a -G $GROUP nobody
      fi
    

    我喜欢这个,因为我可以轻松修改主机卷上的组权限,并知道这些更新的权限适用于docker容器 . 没有对我的主机文件夹/文件的任何许可或所有权修改就会发生这种情况,这让我很开心 .

    我没有把自己添加到容器内的任意组中,而这些组恰好正在使用您想要的GID . 它不能与Dockerfile中的 USER 子句一起使用(除非该用户具有我认为的root权限) . 此外,它尖叫黑客工作;-)

    如果你想成为硬核,你显然可以通过多种方式扩展它 - 例如搜索任何子文件,多个卷等的所有组 .

  • 4

    为了安全和更改docker容器的root,docker主机尝试使用 --uidmap--private-uids 选项

    https://github.com/docker/docker/pull/4572#issuecomment-38400893

    此外,您可以删除docker容器中的多个功能( --cap-drop )以确保安全性

    http://opensource.com/business/14/9/security-for-docker

    UPDATE 支持应该在 docker > 1.7.0

    UPDATE Version 1.10.0 (2016-02-04)添加 --userns-remap flag https://github.com/docker/docker/blob/master/CHANGELOG.md#security-2

  • 151

    基本图像

    使用此图片:https://hub.docker.com/r/reduardo7/docker-host-user

    重要提示: this destroys container portability across hosts .

    1)init.sh

    #!/bin/bash
    
    if ! getent passwd $DOCKDEV_USER_NAME > /dev/null
      then
        echo "Creating user $DOCKDEV_USER_NAME:$DOCKDEV_GROUP_NAME"
        groupadd --gid $DOCKDEV_GROUP_ID -r $DOCKDEV_GROUP_NAME
        useradd --system --uid=$DOCKDEV_USER_ID --gid=$DOCKDEV_GROUP_ID \
            --home-dir /home --password $DOCKDEV_USER_NAME $DOCKDEV_USER_NAME
        usermod -a -G sudo $DOCKDEV_USER_NAME
        chown -R $DOCKDEV_USER_NAME:$DOCKDEV_GROUP_NAME /home
      fi
    
    sudo -u $DOCKDEV_USER_NAME bash
    

    2)Dockerfile

    FROM ubuntu:latest
    # Volumes
        VOLUME ["/home/data"]
    # Copy Files
        COPY /home/data/init.sh /home
    # Init
        RUN chmod a+x /home/init.sh
    

    3)run.sh

    #!/bin/bash
    
    DOCKDEV_VARIABLES=(\
      DOCKDEV_USER_NAME=$USERNAME\
      DOCKDEV_USER_ID=$UID\
      DOCKDEV_GROUP_NAME=$(id -g -n $USERNAME)\
      DOCKDEV_GROUP_ID=$(id -g $USERNAME)\
    )
    
    cmd="docker run"
    
    if [ ! -z "${DOCKDEV_VARIABLES}" ]; then
      for v in ${DOCKDEV_VARIABLES[@]}; do
        cmd="${cmd} -e ${v}"
      done
    fi
    
    # /home/usr/data contains init.sh
    $cmd -v /home/usr/data:/home/data -i -t my-image /home/init.sh
    

    4)用docker构建

    4)跑!

    sh run.sh
    
  • 1

    一个非常优雅的解决方案可以在官方redis image上看到,一般在所有官方图片中都可以看到 .

    逐步说明过程:

    • 先创建redis用户/组

    如Dockerfile评论所示:

    首先添加我们的用户和组,以确保一致地分配他们的ID,无论添加什么依赖项

    • 使用Dockerfile安装gosu

    gosu是 su / sudo 的替代品,可以从root用户轻松降级 . (Redis始终使用 redis 用户运行)

    • 配置 /data 卷并将其设置为workdir

    通过使用 VOLUME /data 命令配置/ data卷,我们现在有一个单独的卷,可以是docker卷,也可以绑定挂载到主机目录 .

    将其配置为workdir( WORKDIR /data )使其成为执行命令的默认目录 .

    • 添加docker-entrypoint文件并使用默认CMD redis-server将其设置为ENTRYPOINT

    这意味着所有容器执行都将通过docker-entrypoint脚本运行,默认情况下,要运行的命令是redis-server .

    docker-entrypoint 是一个执行简单功能的脚本:更改当前目录(/ data)的所有权,并从 root 降级为 redis 用户以运行 redis-server . (如果执行的命令不是redis-server,它将直接运行命令 . )

    This has the following effect

    如果/ data目录绑定到主机,则docker-entrypoint将在 redis user下运行redis-server之前准备用户权限 .

    这使您可以轻松地设置零设置,以便在任何卷配置下运行容器 .

    当然,如果您需要在不同映像之间共享卷,则需要确保它们使用相同的userid / groupid,否则最新容器将劫持前一个容器的用户权限 .

  • 3

    尝试向Dockerfile添加命令

    RUN usermod -u 1000 www-data
    

    积分去https://github.com/denderello/symfony-docker-example/issues/2#issuecomment-94387272

  • 8

    在我的具体情况下,我试图用节点docker镜像构建我的节点包,这样我就不必在部署服务器上安装npm了 . 它运行良好,直到容器外部和主机上,我试图将文件移动到节点docker映像创建的node_modules目录中,因为它归root所有,我被拒绝了 . 我意识到我可以通过将容器中的目录复制到主机上来解决这个问题 . 通过docker docs ...

    复制到本地计算机的文件是使用调用docker cp命令的用户的UID:GID创建的 .

    这是我用来更改由docker容器创建的目录的所有权的bash代码 .

    NODE_IMAGE=node_builder
    docker run -v $(pwd)/build:/build -w="/build" --name $NODE_IMAGE node:6-slim npm i --production
    # node_modules is owned by root, so we need to copy it out 
    docker cp $NODE_IMAGE:/build/node_modules build/lambda 
    # you might have issues trying to remove the directory "node_modules" within the shared volume "build", because it is owned by root, so remove the image and its volumes
    docker rm -vf $NODE_IMAGE || true
    

    如果需要,可以使用第二个docker容器删除该目录 .

    docker run -v $(pwd)/build:/build -w="/build" --name $RMR_IMAGE node:6-slim rm -r node_modules
    
  • 39

    UPDATE 2016-03-02 :从Docker 1.9.0开始,Docker的named volumesreplace data-only containers . 下面的答案,以及我链接的博客文章,仍然具有如何考虑docker内部数据的意义,但考虑使用命名卷来实现下面描述的模式而不是数据容器 .


    我相信解决这个问题的规范方法是使用data-only containers . 使用这种方法,所有对卷数据的访问都是通过使用 -volumes-from 数据容器的容器进行的,因此主机uid / gid无关紧要 .

    例如,文档中给出的一个用例是备份数据卷 . 为此,使用另一个容器通过 tar 进行备份,并且它也使用 -volumes-from 来安装卷 . 所以我认为grok的关键点在于:不要考虑如何使用适当的权限访问主机上的数据,而是考虑如何通过另一个容器执行任何操作(备份,浏览等) . . 容器本身需要使用一致的uid / gids,但它们不需要映射到主机上的任何东西,从而保持可移植性 .

    这对我来说也是相对较新的,但如果您有特定用例,请随时发表评论,我会尝试扩展答案 .

    UPDATE :对于注释中的给定用例,您可能有一个运行graphite的图像 some/graphite 和一个图像 some/graphitedata 作为数据容器 . 所以,忽略端口等, Dockerfile 的图像 some/graphitedata 是这样的:

    FROM debian:jessie
    # add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
    RUN groupadd -r graphite \
      && useradd -r -g graphite graphite
    RUN mkdir -p /data/graphite \
      && chown -R graphite:graphite /data/graphite
    VOLUME /data/graphite
    USER graphite
    CMD ["echo", "Data container for graphite"]
    

    构建并创建数据容器:

    docker build -t some/graphitedata Dockerfile
    docker run --name graphitedata some/graphitedata
    

    some/graphite Dockerfile也应该获得相同的uid / gids,因此它可能看起来像这样:

    FROM debian:jessie
    # add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
    RUN groupadd -r graphite \
      && useradd -r -g graphite graphite
    # ... graphite installation ...
    VOLUME /data/graphite
    USER graphite
    CMD ["/bin/graphite"]
    

    它将运行如下:

    docker run --volumes-from=graphitedata some/graphite
    

    好的,现在这给了我们石墨容器和相关的仅数据容器以及正确的用户/组(注意你也可以重新使用 some/graphite 容器作为数据容器,在运行它时覆盖入口/ cmd,但是有它们作为单独的图像,IMO更清晰) .

    现在,假设您要编辑数据文件夹中的某些内容 . 因此,而不是绑定安装卷主机并在那里编辑它,创建一个新容器来完成这项工作 . 让我们称之为 some/graphitetools . 让我们也创建适当的用户/组,就像 some/graphite 图像一样 .

    FROM debian:jessie
    # add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
    RUN groupadd -r graphite \
      && useradd -r -g graphite graphite
    VOLUME /data/graphite
    USER graphite
    CMD ["/bin/bash"]
    

    您可以通过继承Dockerfile中的 some/graphitesome/graphitedata 来创建此DRY,或者不是创建新映像而只是重用其中一个(根据需要覆盖入口点/ cmd) .

    现在,您只需运行:

    docker run -ti --rm --volumes-from=graphitedata some/graphitetools
    

    然后 vi /data/graphite/whatever.txt . 这非常有效,因为所有容器都具有相同的石墨用户和匹配的uid / gid .

    由于您从不从主机挂载 /data/graphite ,因此您无需关心主机uid / gid如何映射到 graphitegraphitetools 容器内定义的uid / gid . 这些容器现在可以部署到任何主机,它们将继续完美地工作 .

    关于这一点的好处是 graphitetools 可以拥有各种有用的实用程序和脚本,您现在也可以以可移植的方式部署 .

    UPDATE 2 :写完这个答案之后,我决定写一个关于这个方法的more complete blog post . 我希望它有所帮助 .

    UPDATE 3 :我更正了这个答案并添加了更多细节 . 它之前包含一些关于所有权和权限的错误假设 - 所有权通常在卷创建时分配,即在数据容器中分配,因为这是在创建卷时 . 见this blog . 这不是一个要求 - 您可以将数据容器用作"reference/handle",并通过入口点中的chown将所有权/ perms设置在另一个容器中,该入口点以gosu结束以将命令作为正确的用户运行 . 如果有人对此方法感兴趣,请发表评论,我可以使用此方法提供样本的链接 .

相关问题