Docker 安全加固指南:非 root 运行、镜像扫描与运行时权限控制

Docker 让应用的交付和部署变得前所未有的高效,但默认配置下的容器远谈不上安全。很多团队在追求迭代速度时,往往忽略了容器运行时的权限边界、镜像来源的可信度以及运行时的行为约束。一旦攻击者利用容器逃逸或镜像漏洞进入宿主机,后果可能是整个基础设施的沦陷。

本文从三个最关键的维度出发,给出一套可落地的 Docker 安全加固方案:非 root 运行、镜像扫描、运行时权限控制。

为什么默认的 Docker 容器不够安全

默认情况下,Docker 容器以 root 用户运行。这意味着容器内的进程拥有 root 权限,一旦攻击者通过应用漏洞拿到容器内的执行权限,就可以尝试利用内核漏洞、挂载敏感目录或访问 Docker Socket 进行逃逸。更危险的是,如果容器以 --privileged 模式启动,它几乎等同于宿主机上的 root shell。

除此之外,随意拉取的公共镜像可能包含已知 CVE 漏洞、挖矿脚本或后门;容器运行时如果缺乏权限约束,攻击者可以在容器内安装工具、横向扫描内网,甚至修改挂载的宿主机文件。

安全加固的核心思路是:最小权限原则——容器只拥有完成其任务所必需的能力,不多一分。

一、非 root 运行:从 Dockerfile 开始

让容器以非 root 用户运行,是最基础也最有效的加固手段。具体可以从三个层面实现。

1.1 在 Dockerfile 中创建专用用户

以 Node.js 应用为例,推荐使用官方镜像自带的非 root 用户,或手动创建:

FROM node:20-alpine

# 创建专用用户和组
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app
COPY --chown=appuser:appgroup . .

# 切换用户
USER appuser

CMD ["node", "server.js"]

关键点在于 --chown 确保应用文件归属正确,USER 指令必须在启动命令之前生效。对于 Java 应用,可以在基础镜像中创建 appuser;对于 Python 应用,避免使用 root 安装依赖后再切换,而应在安装完成后立即切换。

1.2 使用 --user 参数强制指定

即使镜像中没有定义用户,也可以在运行时强制指定 UID:

docker run --user 1000:1000 my-app

这种方式适合临时验证或无法修改镜像的场景。但要注意,UID 1000 在容器内可能没有对应的家目录,某些应用会因此报错,需要提前挂载可写目录或设置 HOME 环境变量。

1.3 处理端口绑定与文件权限

非 root 用户无法绑定 1024 以下的端口。解决方案有两种:一是让应用监听 8080 等高端口,再通过反向代理转发;二是在 Dockerfile 中授予 CAP_NET_BIND_SERVICE 能力:

RUN setcap 'cap_net_bind_service=+ep' /usr/local/bin/node

文件权限方面,确保应用需要写入的目录(如日志、缓存)对非 root 用户可写,可以通过 VOLUME 或挂载卷配合 chmod 解决。

二、镜像扫描:把漏洞挡在部署之前

镜像一旦构建完成,其中的漏洞就固定下来。定期扫描并修复,是防止已知漏洞被利用的关键。

2.1 选择扫描工具

目前主流的开源镜像扫描工具包括:

  • Trivy:轻量、速度快,支持 OS 包和语言依赖扫描,适合集成到 CI。
  • Grype:Anchore 出品,输出格式丰富,便于自动化处理。
  • Clair:适合自建镜像仓库时做持续扫描。

以 Trivy 为例,扫描一个镜像只需一条命令:

trivy image --severity HIGH,CRITICAL my-app:latest

输出会列出漏洞 ID、严重等级、受影响包和修复版本,非常直观。

2.2 集成到 CI/CD 流水线

扫描不应只在本地手动执行。推荐在 CI 中加入强制门禁:

# GitHub Actions 示例
- name: Scan image
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: my-app:${{ github.sha }}
    severity: 'CRITICAL,HIGH'
    exit-code: '1'

这样,存在高危漏洞的镜像会直接导致构建失败,无法进入部署环节。对于无法立即修复的漏洞,可以维护一个 .trivyignore 文件进行白名单管理,但必须定期审查。

2.3 镜像来源与最小化

除了扫描,还要控制镜像来源:优先使用官方镜像或组织内部维护的基础镜像,避免使用来源不明的公共镜像。同时,尽量选择 alpinedistroless 等精简基础镜像,减少攻击面。例如,Google 的 distroless 镜像只包含应用和运行时依赖,没有 shell 和包管理器,极大降低了被利用的风险。

三、运行时权限控制:让容器“安分守己”

即使镜像本身安全,运行时如果权限过大,依然可能被攻击者利用。运行时加固的目标是:限制容器能做什么、能访问什么。

3.1 丢弃不必要的能力

Linux 能力(Capabilities)将 root 权限拆分为细粒度的操作。默认情况下,Docker 容器拥有约 14 项能力,其中很多并非必需。推荐做法是丢弃所有能力,再按需添加:

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE my-app

对于大多数 Web 应用,只需要 NET_BIND_SERVICE(绑定低端口)或完全不添加任何能力。

3.2 禁止特权模式与敏感挂载

绝对避免使用 --privileged。如果确实需要访问宿主机设备,使用 --device 精确指定,而不是开放全部。同时,禁止挂载 Docker Socket:

# 危险做法
-v /var/run/docker.sock:/var/run/docker.sock

# 如果必须与 Docker 交互,使用受限的代理或 rootless 方案

挂载宿主机敏感目录(如 /etc/root)也应严格禁止。

3.3 只读文件系统与安全选项

将容器根文件系统设为只读,可以防止攻击者写入恶意文件:

docker run --read-only --tmpfs /tmp my-app

配合 --security-opt=no-new-privileges,可以阻止容器内进程通过 setuid 程序提权:

docker run --security-opt=no-new-privileges my-app

3.4 资源限制与网络隔离

通过 cgroups 限制 CPU 和内存,防止容器被入侵后耗尽宿主机资源:

docker run --memory=512m --cpus=1.0 my-app

网络方面,为不同应用创建自定义 bridge 网络,避免使用默认的 docker0 网络,减少容器间不必要的互通。对于不需要外网访问的容器,可以使用 --network=none 或内部网络。

四、组合实践:一份安全启动命令

将上述措施组合起来,一个相对安全的容器启动命令如下:

docker run -d \
  --name my-app \
  --user 1000:1000 \
  --read-only \
  --tmpfs /tmp \
  --cap-drop=ALL \
  --security-opt=no-new-privileges \
  --memory=512m --cpus=1.0 \
  --network=app-net \
  my-app:latest

在 Kubernetes 环境中,对应的安全上下文(SecurityContext)可以这样配置:

securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false
  capabilities:
    drop: ["ALL"]

五、持续监控与迭代

安全加固不是一次性的任务。建议将以下实践纳入日常流程:

  • 定期重新扫描镜像:即使镜像未变,漏洞库也在更新,建议每周扫描一次生产镜像。
  • 审计运行中容器的权限:使用 docker inspect 检查是否有容器意外获得了额外能力。
  • 启用 Docker 审计日志:通过 auditd 监控 Docker 守护进程和容器关键操作。
  • 关注内核与 Docker 版本:及时更新以修复逃逸类漏洞。

结语

Docker 安全加固的核心并不复杂:让容器以非 root 运行、把漏洞挡在部署之前、在运行时收紧权限。这三者相辅相成,缺一不可。非 root 运行降低了逃逸后的影响,镜像扫描减少了已知漏洞的利用面,运行时权限控制则限制了攻击者的横向移动能力。

从今天开始,检查你的 Dockerfile 是否包含 USER 指令,在 CI 中加入一次镜像扫描,给容器加上 --cap-drop=ALL。这些看似微小的改动,会在关键时刻成为保护整个基础设施的防线。

未经允许不得转载:任鹏个人博客 » Docker 安全加固指南:非 root 运行、镜像扫描与运行时权限控制

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏