Sea*_*ean 3 shell docker dockerfile docker-compose hardening
我正在努力强化我们的 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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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:
Run Code Online (Sandbox Code Playgroud)
docker-compose shell 脚本:
docker-compose -f app.yml -f another-app.yml $@
Run Code Online (Sandbox Code Playgroud)
我期望的是,任何试图在内部访问容器的人都将以 appuser 而不是 root 的身份这样做。目标是防止有人弄乱他们不应该做的事情(即 docker 本身)。
正在发生的事情是脚本将在应用程序启动后更改用户(通过 echo 命令证明),但它似乎没有得到维护。如果我执行它,我仍然是 root。
您本质上无法阻止对容器的根级别访问。
任何可以运行任何 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
Run Code Online (Sandbox Code Playgroud)
通常认为最佳实践是以非 root 用户身份运行容器,可以使用USERDockerfile 中的指令,也可以在入口点脚本中运行类似gosu的内容,如您所展示的那样。但是,面对有足够兴趣获取 root 访问权限的特权用户,您无法阻止root 访问。
正如大卫所提到的,一旦有人可以访问 docker 套接字(通过 API 或使用dockerCLI),这通常意味着他们对您的主机具有 root 访问权限。使用该访问权限运行具有主机命名空间和卷挂载的特权容器是微不足道的,攻击者可以做任何事情。
当您需要使用以 root 身份运行的步骤来初始化容器时,我确实建议使用gosu,su因为su它不是为容器设计的,并且会使进程以 root pid 运行。确保您exec调用gosu并且这将消除以 root 身份运行的任何内容。但是,您启动容器的用户与用于 的用户相同docker exec,并且由于您需要以 root 身份启动,除非您使用-u标志覆盖它,否则您的 exec 将以 root 身份运行。
一般来说,您可以采取其他步骤来锁定 docker:
使用用户命名空间。这些是在整个守护进程中定义的,需要您销毁所有容器,然后再次拉取镜像,因为 uid 映射会影响镜像层的存储。用户命名空间抵消了 docker 使用的 uid,因此容器内的 root 不是主机上的 root,而在容器内,您仍然可以绑定到编号较低的端口并运行管理活动。
考虑authz 插件。Open policy agent 和 Twistlock 是我所知道的两个,但我不知道它们是否允许您限制docker exec命令的用户。他们可能要求您为用户提供连接到 docker 的证书,而不是让他们直接访问 docker 套接字,因为套接字在其接收的 API 请求中没有包含任何用户详细信息。
考虑无根 docker。这仍然是实验性的,但由于 docker 不是以 root 身份运行,它无法返回主机来执行 root 活动,从而缓解了容器以 root 身份运行时出现的许多问题。