如何防止root用户访问我的Docker容器?

7

我正在加固我们的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脚本:
docker-compose -f app.yml -f another-app.yml $@

我希望的是任何内部尝试访问容器的人都会使用appuser而不是root进行访问。目标是防止某些人对不应该被更改的事物(如docker本身)进行篡改。

现在的情况是脚本会在应用程序启动后更改用户(通过echo命令证明),但似乎没有得到维护。如果我使用exec进入,我仍然是root。


我会假设您是正确的,即在容器初始化时实际上需要root权限,但我会尝试在我的建议之前解决这个问题,我的建议是使用gosu,它允许您以root身份运行容器,然后在entrypoint中切换到另一个用户 - https://dev59.com/KFoV5IYBdhLWcg3wCrEn。 - leeman24
4个回答

7
正如David所提到的,一旦某人可以访问docker套接字(通过API或使用docker CLI),通常意味着他们有root访问主机的权限。使用该访问权限运行具有主机名称空间和卷挂载的特权容器以使攻击者执行几乎任何操作是微不足道的。
当您需要以root身份运行步骤来初始化容器时,我建议使用gosu而不是像su这样的东西,因为su不是为容器而设计的,并且会让一个进程作为根pid运行。确保您exec调用gosu并消除任何作为root运行的内容。但是,您启动容器的用户与用于docker exec的用户相同,因此,除非您使用-u标志覆盖它,否则您的exec将以root身份运行。
还有其他措施可以采取来限制docker:
  1. 使用用户命名空间。这些在整个守护进程上定义,需要销毁所有容器并再次拉取镜像,因为 uid 映射会影响镜像层的存储。用户命名空间偏移了 docker 使用的 uid,使得容器内的 root 不是主机上的 root,同时在容器内仍然可以绑定到低端口号并运行管理活动。

  2. 考虑使用授权插件。Open Policy Agent 和 Twistlock 是我知道的两个,但我不知道是否允许你限制 docker exec 命令的用户。它们可能要求你给用户一个证书来连接 docker,而不是直接给他们访问 docker socket 的权限,因为 socket 在接收 API 请求时没有包含任何用户详细信息。

  3. 考虑使用无根 Docker。这仍处于实验阶段,但由于 docker 不以 root 用户身份运行,它无法访问主机以执行 root 活动,从而缓解了在以 root 身份运行容器时出现的许多问题。


5

你无法从根本上防止对容器的访问。

任何能够运行任何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的东西来实现。尽管如此,在面对足够感兴趣的特权用户时,您无法防止根访问。


1
好吧,这让我想到另一个问题。我将su命令更改为“exec /usr/local/bin/gosu $appUser“$@””。那实际上对我有什么作用呢?如果启动脚本仍然以root身份运行,文件系统所有权和权限仍然相同,并且您仍然以root身份进入容器,那么这会增加哪些安全性呢?我想我只是不完全理解“以非root用户身份运行”的含义/作用。 - Sean
这是一篇关于Docker的极好但被低估的帖子... @DavidMaze“确实知道他们的洋葱”(一个奇怪的短语,但这是一个赞美)。 - M__

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之后,在.bash_rc中添加exit


试图保护根帐户同时仍然允许用户以root身份访问运行的docker容器很可能会失败。使用非登录命令远程运行可以轻松绕过.bashrc。而且,通过调整路径并放置一个假的docker命令来运行任何命令,可以使用setuid shell脚本来获取root shell。 - BMitch

0

不确定这是否有效,但您可以尝试。允许用户/组使用有限执行命令的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
@someonewithpc,不确定您所指的风险是什么。整个想法是为sudo提供有限的执行权限。在我们的实现中,这个功能完美地运作。虽然开发人员具有sudo权限,但他们只能运行一部分命令,并将其包装在一个shell脚本中。在这种情况下是docker-cli。我们可以在脚本中做很多事情来控制开发人员如何运行docker命令。我们尝试了twistlock和opa策略,它们是很好的authz插件,但不能提供我们允许开发人员使用的细粒度权限。通过将其包装在docker-cli中,我们可以做很多事情。 - jlim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接