Docker和--userns-remap,如何管理卷权限以在主机和容器之间共享数据?

Sté*_* C. 79 docker

在docker中,在容器内创建的文件在从主机检查时往往具有不可预测的所有权.默认情况下,卷上文件的所有者是root(uid 0),但只要非root用户帐户涉及容器并写入文件系统,所有者就会从主机角度变得或多或少随机.

当您需要使用调用docker命令的同一用户帐户从主机访问卷数据时,这是一个问题.

典型的解决方法是

  • 在Dockerfiles中创建时强制用户uID(非可移植)
  • 将主机用户的UID docker run作为环境变量传递给命令,然后chown在入口点脚本中的卷上运行某些命令.

这两种解决方案都可以控制容器外的实际权限.

我希望用户命名空间是这个问题的最终解决方案.我已经使用最近发布的版本1.10和--userns-remap设置到我的桌面帐户运行了一些测试.但是,我不确定它是否可以使安装卷上的文件所有权更容易处理,我担心它实际上可能正好相反.

假设我启动这个基本容器

docker run -ti -v /data debian:jessie /bin/bash
echo 'hello' > /data/test.txt
exit
Run Code Online (Sandbox Code Playgroud)

然后检查来自主机的内容:

ls -lh /var/lib/docker/100000.100000/volumes/<some-id>/_data/

-rw-r--r-- 1 100000 100000 6 Feb  8 19:43 test.txt
Run Code Online (Sandbox Code Playgroud)

这个数字'100000'是我的主机用户的子UID,但由于它与我的用户的UID不对应,我仍然无法在没有权限的情况下编辑test.txt.此子用户似乎与docker之外的实际常规用户没有任何关联.它没有映射回来.

由于UID->sub-UID名称空间中发生的映射,本文前面提到的由主机和容器之间的UID对齐组成的变通方法不再起作用.

那么,有没有办法在启用用户命名空间的情况下运行docker(为了提高安全性),同时仍然可以让运行docker的主机用户拥有在卷上生成的文件?

ama*_*nov 40

如果您可以提前预先安排用户和组,则可以以特定方式分配UID和GID,以便主机用户对应容器内的命名空间用户.

这是一个例子(Ubuntu 14.04,Docker 1.10):

  1. 创建一些具有固定数字ID的用户:

    useradd -u 5000 ns1
    
    groupadd -g 500000 ns1-root
    groupadd -g 501000 ns1-user1
    
    useradd -u 500000 -g ns1-root ns1-root
    useradd -u 501000 -g ns1-user1 ns1-user1 -m
    
    Run Code Online (Sandbox Code Playgroud)
  2. /etc/subuid/etc/subgid文件中手动编辑自动生成的从属ID范围:

    ns1:500000:65536
    
    Run Code Online (Sandbox Code Playgroud)

    (注意没有记录ns1-root,ns1-user1由于MAX_UIDMAX_GID限制/etc/login.defs)

  3. 启用用户名称空间/etc/default/docker:

    DOCKER_OPTS="--userns-remap=ns1"
    
    Run Code Online (Sandbox Code Playgroud)

    重启守护进程service docker restart,确保/var/lib/docker/500000.500000创建目录.

    现在,容器里面你有rootuser1,和主机上- ns1-root并且ns1-user1,与之相配的ID

    更新:为了保证非root用户在容器中具有固定ID(例如user1 1000:1000),在映像构建期间显式创建它们.

试驾:

  1. 准备卷目录

    mkdir /vol1
    chown ns1-root:ns1-root /vol1
    
    Run Code Online (Sandbox Code Playgroud)
  2. 从容器中试试

    docker run --rm -ti -v /vol1:/vol1 busybox sh
    echo "Hello from container" > /vol1/file
    exit
    
    Run Code Online (Sandbox Code Playgroud)
  3. 试试主持人

    passwd ns1-root
    login ns1-root
    cat /vol1/file
    echo "can write" >> /vol1/file
    
    Run Code Online (Sandbox Code Playgroud)

不便携,看起来像黑客,但有效.

  • 非常有趣,值得+1.但是您仍然需要确保图像中的user1被分配了UID 1000.否则您无法确定它是否会在主机上接收UID 501000.顺便说一句,如果我们在ID为1000的用户运行许多不同的图像时,我们绝对确定公式总是"subUID下限+图像中的UID". (3认同)
  • 如果您在主机和容器中手动排列用户和组,您真的需要“用户命名空间”功能吗? (2认同)
  • 您创建的命名空间将主机用户与容器用户分开,但您可能需要容器的多个命名空间,尤其是当官方镜像(如 mysql 镜像)创建一个没有显式 uid 的用户时。当 --userns-remap 选项只需要一个时,您如何处理多个命名空间? (2认同)
  • @amartynov我可以问你为什么要为你的"ns1"用户指定一个UID(5000)吗?因为它是你在subuid和subgid文件中引用的名称(不是UID),所以看起来这个用户得到的UID并不重要.我错过了一些关系,因为5000和500000之间的相似性可能暗示? (2认同)

ken*_*orb 6

一种解决方法是在构建时动态分配用户的 uid 以匹配主机。

例子Dockerfile

FROM ubuntu
# Defines argument which can be passed during build time.
ARG UID=1000
# Create a user with given UID.
RUN useradd -d /home/ubuntu -ms /bin/bash -g root -G sudo -u $UID ubuntu
# Switch to ubuntu user by default.
USER ubuntu
# Check the current uid of the user.
RUN id
# ...
Run Code Online (Sandbox Code Playgroud)

然后构建为:

docker build --build-arg UID=$UID -t mycontainer .
Run Code Online (Sandbox Code Playgroud)

并运行为:

docker run mycontainer
Run Code Online (Sandbox Code Playgroud)

如果您已有容器,请使用以下内容创建一个包装容器Dockerfile

FROM someexistingcontainer
ARG UID=1000
USER root
# This assumes you've the existing user ubuntu.
RUN usermod -u $UID ubuntu
USER ubuntu
Run Code Online (Sandbox Code Playgroud)

这可以包含在docker-compose.yml如下内容中:

version: '3.4'
services:
  myservice:
    command: id
    image: myservice
    build:
      context: .
    volumes:
    - /data:/data:rw
Run Code Online (Sandbox Code Playgroud)

然后构建并运行为:

docker-compose build --build-arg UID=$UID myservice; docker-compose run myservice
Run Code Online (Sandbox Code Playgroud)

  • 我不想看起来不友好,但这实际上是原始问题中列出的解决方法之一,但不是解决方案,并且与用户名称空间无关。 (8认同)