4

我正在努力强化我们的 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 命令证明),但它似乎没有得到维护。如果我执行它,我仍然是根。

4

4 回答 4

5

正如大卫所提到的,一旦有人可以访问 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 身份运行时出现的许多问题。

于 2019-08-30T19:19:27.820 回答
2

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

任何可以运行任何 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 用户身份运行容器是最佳实践,无论是使用USERDockerfile 中的指令还是在入口点脚本中运行类似 gosu 的东西就像你展示的那样。但是,面对有足够兴趣获得它的特权用户,您无法阻止root 访问。

于 2019-08-30T18:51:49.550 回答
0

当 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.

于 2019-08-31T22:06:06.980 回答
0

不确定这是否有效,但您可以尝试。允许具有有限执行命令的用户/组进行 sudo 访问。sudo 配置只允许执行 docker-cli。docker-cli使用运行 docker 命令的名称创建一个 shell 脚本,例如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 命令

于 2020-10-25T21:52:32.973 回答