【问题标题】:How do I prevent root access to my docker container如何防止对我的 docker 容器的 root 访问
【发布时间】:2022-01-22 18:25:06
【问题描述】:

我正在努力强化我们的 docker 镜像,但我对它的理解已经有点薄弱了。话虽如此,我当前的步骤是阻止用户以 root 身份运行容器。对我来说,这就是说“当用户运行 'docker exec -it my-container bash' 时,他将是一个非特权用户”(如果我错了,请纠正我)。

当我通过 docker-compose 启动容器时,运行的启动脚本需要以 root 身份运行,因为它处理导入证书和挂载文件(在外部创建并通过卷挂载查看)。完成后,我希望用户成为“appuser”以供将来访问。这个问题似乎很符合我正在寻找的内容,但我使用的是 docker-compose,而不是 docker run:How to disable the root access of a docker container?

这似乎是相关的,因为启动命令与我们说的 tomcat 不同。我们正在运行一个 Spring Boot 应用程序,我们使用一个简单的“java -jar jarFile”启动它,并且该图像是使用 maven 的 dockerfile-maven-plugin 构建的。话虽如此,我应该在运行之前将用户更改为非特权用户,还是之后?

我相信更改 Dockerfile 中的用户而不是启动脚本会做到这一点……但它不会以 root 身份运行启动脚本,从而导致需要 root 的调用失败。我也搞砸了使用 ENTRYPOINT,但可能在那里做错了。同样,在 yml 文件中使用“user:”似乎会使 start.sh 脚本以该用户而不是 root 身份运行,所以这不起作用。

Dockerfile:

FROM parent/image:latest

ENV APP_HOME                            /apphome
ENV APP_USER                            appuser
ENV APP_GROUP                           appgroup

# Folder containing our application, i.e. jar file, resources, and scripts.
# This comes from unpacking our maven dependency
ADD target/classes/app ${APP_HOME}/

# Primarily just our start script, but some others
ADD target/classes/scripts /scripts/

# Need to create a folder that will be used at runtime
RUN mkdir -p ${APP_HOME}/data && \
    chmod +x /scripts/*.sh && \
    chmod +x ${APP_HOME}/*.*

# Create unprivileged user
RUN groupadd -r ${APP_GROUP} && \
    useradd -g ${APP_GROUP} -d ${APP_HOME} -s /sbin/nologin  -c "Unprivileged User" ${APP_USER} && \
    chown -R ${APP_USER}:${APP_GROUP} ${APP_HOME}

WORKDIR $APP_HOME

EXPOSE 8443

CMD /opt/scripts/start.sh

start.sh 脚本:

#!/bin/bash

# setup SSL, modify java command, etc

# run our java application
java -jar "boot.jar"

# Switch users to always be unprivileged from here on out? 
# Whatever "hardening" wants...  Should this be before starting our application?
exec su -s "/bin/bash" $APP_USER

app.yml 文件:

version: '3.3'

services:
  app:
    image: app_image:latest
    labels:
      c2core.docker.compose.display-name: My Application
      c2core.docker.compose.profiles: a_profile
    volumes:
      - "data_mount:/apphome/data"
      - "cert_mount:/certs"
    hostname: some-hostname
    domainname: some-domain
    ports:
    - "8243:8443"
    environment:
      - some_env_vars
    depends_on:
    - another-app
    networks:
      a_network:
        aliases:
          - some-network
networks:
  a_network:
    driver: bridge
volumes:
  data_mount:
  cert_mount:

docker-compose shell 脚本:

docker-compose -f app.yml -f another-app.yml $@

我期望的是,任何试图在内部访问容器的人都将以 appuser 而不是 root 身份进行访问。目标是防止有人弄乱他们不应该做的事情(即 docker 本身)。

发生的情况是脚本将在应用启动后更改用户(通过 echo 命令证明),但似乎没有得到维护。如果我执行它,我仍然是 root。

【问题讨论】:

  • 我假设你是正确的,你在容器初始化时实际上需要 root,但我会尝试在我的建议之前解决这个问题,即使用 gosu,它允许你以 root 身份运行你的容器,然后在您的入口点切换到另一个用户 - stackoverflow.com/questions/36781372/docker-using-gosu-vs-user.

标签: shell docker docker-compose dockerfile hardening


【解决方案1】:

您本质上无法阻止对容器的根级别访问。

任何可以运行任何 Docker 命令的人都可以运行以下三个命令中的任何一个:

# Get a shell, as root, in a running container
docker exec -it -u 0 container_name /bin/sh

# Launch a new container, running a root shell, on some image
docker run --rm -it -u 0 --entrypoint /bin/sh image_name

# Get an interactive shell with unrestricted root access to the host
# filesystem (cd /host/var/lib/docker)
docker run --rm -it -v /:/host busybox /bin/sh

通常认为最好的做法是以非 root 用户身份运行容器,或者在 Dockerfile 中使用 USER 指令,或者在入口点脚本中运行类似 gosu 的东西,就像你展示的那样。但是,您无法阻止 root 访问,因为特权用户对获得它有足够的兴趣。

【讨论】:

  • 很好。这让我想到另一个问题。我将 su 命令更改为 'exec /usr/local/bin/gosu $appUser "$@"'。这对我有什么实际作用?如果启动脚本仍然以 root 身份运行,文件系统都具有相同的所有权和权限,并且您仍然以 root 身份执行到容器中,这会增加什么安全性?我想我只是不完全理解“以非 root 用户身份运行”的含义/作用。
【解决方案2】:

正如 David 所提到的,一旦有人可以访问 docker 套接字(通过 API 或使用 docker CLI),这通常意味着他们对您的主机具有 root 访问权限。使用该访问权限来运行具有主机命名空间和卷挂载的特权容器是微不足道的,攻击者几乎可以做任何事情。

当您需要使用以 root 身份运行的步骤来初始化容器时,我建议您使用 gosu 而不是 su 之类的东西,因为 su 不是为容器设计的,并且会让进程以 root pid 身份运行。确保您exec 调用gosu,这将消除以root 身份运行的任何内容。但是,您启动容器的用户与用于 docker exec 的用户相同,并且由于您需要以 root 身份启动,因此您的 exec 将以 root 身份运行,除非您使用 -u 标志覆盖它。

一般来说,您可以采取其他步骤来锁定 docker:

  1. 使用user namespaces。这些是在整个守护进程上定义的,要求您销毁所有容器,然后再次拉取图像,因为 uid 映射会影响图像层的存储。用户命名空间偏移了 docker 使用的 uid,因此容器内的 root 不是主机上的 root,而在容器内您仍然可以绑定到低编号端口并运行管理活动。

  2. 考虑authz plugins。开放策略代理和 Twistlock 是我所知道的两个,但我不知道是否允许您限制 docker exec 命令的用户。他们可能会要求您为用户提供连接 docker 的证书,而不是让他们直接访问 docker 套接字,因为套接字在其接收的 API 请求中不包含任何用户详细信息。

  3. 考虑rootless docker。这仍然是实验性的,但由于 docker 不是以 root 身份运行的,因此它无法访问主机来执行 root 活动,从而缓解了容器以 root 身份运行时出现的许多问题。

【讨论】:

    【解决方案3】:

    当docker正常从一台主机运行时,你可以做一些步骤。
    通过在从接受的主机挂载的目录中查找秘密来确保它不是从其他主机运行的。

    更改主机上用户的.bashrc,以便他们在登录后立即开始运行docker。当您的用户需要在主机上做其他事情时,给他们一个没有docker 访问权限的帐户,并让他们sudo 给一个具有docker 访问权限的特殊用户(或使用带有 setuid 标志的 startdocker 脚本)。

    使用您制作并强化的脚本启动 docker,例如 startserver

    #!/bin/bash
    
    settings() {
       # Add mount dirs. The homedir in the docker will be different from the one on the host.
       mountdirs="-v /mirrored_home:/home -v /etc/dockercheck:/etc/dockercheck:ro"
       usroptions="--user $(id -u):$(id -g) -v /etc/passwd:/etc/passwd:ro"   
       usroptions="${usroptions} -v/etc/shadow:/etc/shadow:ro -v /etc/group:/etc/group:ro"
    }
    
    # call function that fills special variables
    settings
    
    image="my_image:latest"
    
    docker run -ti --rm ${usroptions} ${mountdirs} -w $HOME --entrypoint=/bin/bash "${image}"
    

    添加变量--env HOSTSERVER=${host} 不会帮助强化,在另一台服务器上可以添加--env HOSTSERVER=servername_that_will_be_checked

    当用户登录主机时,会调用startserver并启动docker。调用 startserver 后,将exit 添加到.bash_rc

    【讨论】:

    • 尝试保护 root 帐户,同时仍然允许用户访问以 root 身份运行的 docker 可能会失败。使用远程运行的非登录命令绕过 .bashrc 很简单。 setuid shell 脚本可用于运行任何命令,方法是调整路径并放置一个只为您提供 root shell 的假 docker 命令。
    【解决方案4】:

    不确定这是否有效,但您可以尝试。允许具有有限执行命令的用户/组进行 sudo 访问。 sudo 配置只允许执行 docker-cli。创建一个名为docker-cli 的shell 脚本,其内容运行docker 命令,例如docker "$@"。在此文件中,检查参数并强制用户在执行 docker 的 exec 或 attach 命令时提供开关 --user 或 -u。还要确保验证用户没有提供说 -u root 的开关。例如

    sudo docker-cli exec -it containerid sh (failed)
    
    sudo docker-cli exec -u root ... (failed)
    
    sudo docker-cli exec -u mysql ... (Passed)
    

    您甚至可以限制用户可以在此 shell 脚本中运行的 docker 命令

    【讨论】:

    • 通常不是一个好主意,尝试过滤安全的东西是一个冒险的提议
    • @someonewithpc,不确定您指的是什么风险。整个想法是为 sudo 提供有限的执行。这在我们的实现中非常有效。尽管开发人员拥有 sudo 权限,但他们只能运行一部分命令,我们将其封装在 shell 脚本中。在这种情况下,docker-cli。我们可以在脚本中做很多事情来控制开发人员应该如何运行 docker 命令。我们尝试了 twistlock 和 opa policy,这些都是很棒的 authz 插件,但没有提供我们为开发人员允许的细粒度权限。通过将它包装在 docker-cli 中,我们可以做很多事情
    猜你喜欢
    • 1970-01-01
    • 2022-10-08
    • 2019-01-05
    • 2017-10-04
    • 1970-01-01
    • 2022-10-25
    • 1970-01-01
    • 2017-04-23
    相关资源
    最近更新 更多