首页 > 解决方案 > 如何防止对我的 docker 容器的 root 访问

问题描述

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

当我通过 docker-compose 启动我的容器时,运行的启动脚本需要以 root 身份运行,因为它处理导入证书和挂载文件(在外部创建并通过卷挂载查看)。完成后,我希望用户成为“appuser”以供将来访问。这个问题似乎与我正在寻找的内容非常匹配,但我使用的是 docker-compose,而不是 docker run:如何禁用 docker 容器的 root 访问权限?

这似乎是相关的,因为启动命令与我们说的 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 身份进行访问。目标是防止有人弄乱他们不应该做的事情(即码头工人本身)。

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

标签: shelldockerdocker-composedockerfilehardening

解决方案


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

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

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

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

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

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


推荐阅读