如何防止对我的 docker 容器的 root 访问 [英] How do I prevent root access to my docker container

查看:45
本文介绍了如何防止对我的 docker 容器的 root 访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am working on hardening our docker images, which I already have a bit of a weak understanding of. With that being said, the current step I am on is preventing the user from running the container as root. To me, that says "when a user runs 'docker exec -it my-container bash', he shall be an unprivileged user" (correct me if I'm wrong).

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

When I start up my container via docker-compose, the start script that is run needs to be as root since it deals with importing certs and mounted files (created externally and seen through a volume mount). After that is done, I would like the user to be 'appuser' for any future access. This question seems to match pretty well what I'm looking for, but I am using docker-compose, not docker run: How to disable the root access of a docker container?

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

This seems to be relevant, as the startup command differs from let's say tomcat. We are running a Spring Boot application that we start up with a simple 'java -jar jarFile', and the image is built using maven's dockerfile-maven-plugin. With that being said, should I be changing the user to an unprivileged user before running that, or still after?

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

I believe changing the user inside of the Dockerfile instead of the start script will do this... but then it will not run the start script as root, thus blowing up on calls that require root. I had messed with using ENTRYPOINT as well, but could have been doing it wrong there. Similarly, using "user:" in the yml file seemed to make the start.sh script run as that user instead of root, so that wasn't working.

Dockerfile:

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 脚本:

start.sh script:

#!/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 文件:

app.yml file:

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 shell script:

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

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

What I would expect is that anyone trying to access the container internally will be doing so as appuser and not root. The goal is to prevent someone from messing with things they shouldn't (i.e. docker itself).

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

What is happening is that the script will change users after the app has started (proven via an echo command), but it doesn't seem to be maintained. If I exec into it, I'm still root.

推荐答案

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

As David mentions, once someone has access to the docker socket (either via API or with the docker CLI), that typically means they have root access to your host. It's trivial to use that access to run a privileged container with host namespaces and volume mounts that let the attacker do just about anything.

当您需要使用以 root 身份运行的步骤来初始化容器时,我建议您使用 gosu 而不是 su 因为 su 不是为容器设计的,并且会留下一个进程作为根 pid 运行.确保您 execgosu 的调用,这将消除以 root 身份运行的任何内容.但是,您启动容器的用户与用于 docker exec 的用户相同,并且由于您需要以 root 身份启动,除非您使用 覆盖它,否则您的 exec 将以 root 身份运行>-u 标志.

When you need to initialize a container with steps that run as root, I do recommend gosu over something like su since su was not designed for containers and will leave a process running as the root pid. Make sure that you exec the call to gosu and that will eliminate anything running as root. However, the user you start the container as is the same as the user used for docker exec, and since you need to start as root, your exec will run as root unless you override it with a -u flag.

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

There are additional steps you can take to lock down docker in general:

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

  1. Use user namespaces. These are defined on the entire daemon, require that you destroy all containers, and pull images again, since the uid mapping affects the storage of image layers. The user namespace offsets the uid's used by docker so that root inside the container is not root on the host, while inside the container you can still bind to low numbered ports and run administrative activities.

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

Consider authz plugins. Open policy agent and Twistlock are two that I know of, though I don't know if either would allow you to restrict the user of a docker exec command. They likely require that you give users a certificate to connect to docker rather than giving them direct access to the docker socket since the socket doesn't have any user details included in API requests it receives.

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

Consider rootless docker. This is still experimental, but since docker is not running as root, it has no access back to the host to perform root activities, mitigating many of the issues seen when containers are run as root.

这篇关于如何防止对我的 docker 容器的 root 访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆