2. Docker 镜像安全:镜像漏洞扫描、镜像签名与验证、使用可信基础镜像、最小化镜像原则
镜像安全是容器安全的第一道防线。一个被植入后门或包含已知漏洞的镜像,会在整个交付和运行链上引入不可控风险。本章从四个核心维度构建镜像安全防线:漏洞扫描(发现风险)、签名验证(防止篡改)、可信基础镜像(源头可信)、最小化原则(减少攻击面)。
2.1 镜像漏洞扫描
漏洞扫描是镜像安全的“体检”环节。通过将镜像层文件系统与已知 CVE(Common Vulnerabilities and Exposures)数据库进行比对,识别出操作系统包、应用依赖库中的安全缺陷。
2.1.1 主流扫描工具对比
| 工具 | 扫描方式 | 数据库源 | CI/CD 集成 | 特点 |
|---|---|---|---|---|
| Trivy | 静态扫描 | NVD、RedHat、Debian、Alpine 等 | 原生支持 | 轻量、无状态、速度快 |
| Clair | 静态扫描 | NVD、Ubuntu、Debian、RedHat | 需 API 封装 | CoreOS 生态,支持离线 |
| Anchore Grype | 静态扫描 | NVD、GitHub Advisory | 原生支持 | 与 Syft 配合生成 SBOM |
| Docker Scout | 静态+动态 | Docker Hub 安全数据库 | Docker Desktop 内置 | 提供修复建议和策略评估 |
2.1.2 扫描策略与阻断门禁
- 构建阶段扫描:在
docker build完成后立即执行扫描,阻断高危镜像进入仓库。 - 仓库准入扫描:镜像推送至 Harbor、Docker Registry 时触发 Webhook 扫描,拒绝未通过策略的镜像。
- 运行时周期性扫描:对已运行的容器镜像进行定期重扫,发现新披露的 CVE。
- 门禁策略示例:
CRITICAL漏洞数 > 0 则阻断;HIGH漏洞数 > 5 则告警。
实践建议:在 CI 流水线中集成 Trivy,使用
--severity CRITICAL,HIGH --exit-code 1 参数实现自动阻断。
2.2 镜像签名与验证
镜像签名确保镜像的发布者身份可信且内容未被篡改。Docker Content Trust(DCT)和 Notary 项目是业界主流实现方案,基于 TUF(The Update Framework)规范。
2.2.1 签名流程与验证机制
下图展示了从开发者签名到部署节点验证的完整链路:
2.2.2 启用 Docker Content Trust
# 启用 DCT 环境变量
export DOCKER_CONTENT_TRUST=1
# 推送镜像时自动签名(需提前生成根密钥和仓库密钥)
docker push registry.example.com/myapp:1.0
# 拉取镜像时验证签名
docker pull registry.example.com/myapp:1.0
# 查看签名信息
docker trust inspect --pretty registry.example.com/myapp:1.0
密钥管理风险:根密钥(root key)必须离线存储,建议使用硬件安全模块(HSM)或密钥管理服务(KMS)。仓库密钥(repository key)可在线使用,但需定期轮换。
2.3 使用可信基础镜像
基础镜像的选择直接决定了镜像的安全基线。官方镜像、发行商认证镜像、以及经过内部安全加固的镜像,是构建可信供应链的起点。
2.3.1 基础镜像安全分级
| 级别 | 来源 | 示例 | 安全特征 |
|---|---|---|---|
| L1 官方认证 | Docker Official Images | python:3.12-slim |
Docker 团队维护,定期扫描,签名验证 |
| L2 发行商认证 | RedHat UBI、Distroless | registry.access.redhat.com/ubi9/ubi-minimal |
厂商安全响应,最小化组件,CVE 修复 SLA |
| L3 内部加固 | 企业自建基础镜像 | registry.internal.com/base/jre17:1.0 |
裁剪非必要包,配置安全基线,定期重建 |
| L4 不可信 | Docker Hub 个人仓库 | user/myapp:latest |
无安全背书,可能包含恶意软件 |
2.3.2 选择原则
- 优先使用 slim 或 distroless 变体:减少包管理器、shell 和系统工具,降低漏洞面。
- 固定版本标签:避免使用
latest,改用3.12-slim-bookworm等不可变标签。 - 定期重建:基础镜像的底层 OS 包会披露新 CVE,建议每周或每月重建一次应用镜像。
- 内部镜像仓库代理:通过 Harbor 或 Nexus 代理官方镜像,统一扫描和缓存策略。
2.4 最小化镜像原则
最小化镜像原则的核心是“只包含运行时必需的内容”。每减少一个包、一个文件、一个层,就减少一个潜在的攻击入口。
2.4.1 多阶段构建实践
多阶段构建是达成最小化镜像的标准方法。构建阶段使用完整工具链,运行阶段只复制编译产物。
# 构建阶段
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server
# 运行阶段
FROM alpine:3.20
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /app/server /server
EXPOSE 8080
USER 1001
ENTRYPOINT ["/server"]
2.4.2 镜像层数与大小控制
| 优化项 | 做法 | 效果 |
|---|---|---|
| 合并 RUN 指令 | RUN apt-get update && apt-get install -y pkg1 pkg2 && rm -rf /var/lib/apt/lists/* |
减少层数,清理缓存 |
| 使用 .dockerignore | 排除 .git、node_modules、__pycache__ |
减少构建上下文和层体积 |
| 选择更小的基础镜像 | alpine 替代 ubuntu,distroless 替代 debian |
镜像体积减少 50%~90% |
| 删除包管理器缓存 | rm -rf /var/cache/apk/*(Alpine) |
每层减少数 MB |
2.4.3 最小化原则检查清单
- 镜像中是否包含
curl、wget、bash、vim等调试工具? - 是否以非 root 用户运行?
USER 1001或USER nobody。 - 是否设置了只读根文件系统?
--read-only或readOnlyRootFilesystem: true。 - 是否移除了
setuid/setgid权限?chmod a-s /usr/bin/*。 - 是否只暴露了必要端口?
安全基线检查工具:使用
docker scout quickview 或 trivy image --severity CRITICAL 快速评估镜像安全状态。结合 dive 工具分析每层内容,发现冗余文件。
2.5 四维防线总结
镜像安全不是单一动作,而是贯穿构建、分发、部署的持续过程。下表汇总了四个维度的关键控制点:
| 维度 | 关键控制点 | 工具/方法 | 阻断条件 |
|---|---|---|---|
| 漏洞扫描 | 构建时、推送时、运行时 | Trivy、Clair、Docker Scout | CRITICAL 漏洞 > 0 |
| 签名验证 | 推送签名、拉取验证 | DCT、Notary、Cosign | 签名缺失或无效 |
| 可信基础镜像 | 来源认证、版本固定、定期重建 | 官方镜像、UBI、Distroless | 非白名单来源 |
| 最小化原则 | 多阶段构建、层合并、非 root 运行 | Dockerfile 优化、.dockerignore | 镜像大小超过基线 |