痛点:你的容器镜像到底有多少漏洞?
用 Trivy 扫一下你生产环境的基础镜像,结果往往触目惊心:
$ trivy image python:3.12-slim
Total: 287 (UNKNOWN: 0, LOW: 112, MEDIUM: 98, HIGH: 62, CRITICAL: 15)
即使是 -slim 变体,Debian/Ubuntu 基础镜像也天然携带大量系统级包,其中不乏已知 CVE。这些漏洞绝大多数与你的应用毫无关系,但安全审计不管——扫出来就要修,修不动就要写豁免申请。
核心矛盾:传统发行版为通用性设计,塞了太多你用不到的东西;而 scratch/distroless 又缺少必要的工具链,调试排障极其困难。
方案:Wolfi OS + Chainguard Images
Wolfi OS 是 Chainguard 开源的面向容器的 Linux 发行版(非通用 OS),专为生成最小化、可签名、可溯源的容器镜像而设计。核心特性:
- 使用
apk(Alpine 的包管理器)但基于 glibc(非 musl),兼容性远超 Alpine - 所有包每日从源码构建,CVE 修复平均响应时间 < 24 小时
- 原生支持 SBOM(Software Bill of Materials)生成
- 包粒度极细,可精确控制镜像内容
Chainguard Images 是基于 Wolfi 构建的预置镜像集合,涵盖 Python、Node.js、Go、Java、Nginx 等常用运行时,开箱即实现零或近零已知 CVE。
实操步骤
第 1 步:替换基础镜像对比效果
以 Python 应用为例,原始 Dockerfile:
# 旧方案:基于 Debian slim
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]
替换为 Chainguard 镜像:
# 新方案:基于 Chainguard Python
FROM cgr.dev/chainguard/python:latest-dev AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
FROM cgr.dev/chainguard/python:latest
WORKDIR /app
COPY --from=builder /install /usr/
COPY . .
CMD ["python", "main.py"]
关键区别:
- latest-dev 标签含 pip、shell 等构建工具,用于编译阶段
- latest 标签是最终运行时,仅包含 Python 解释器和必要库
- 多阶段构建确保最终镜像干净
扫描对比:
$ trivy image my-app:chainguard
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
第 2 步:用 apko 自定义 Wolfi 镜像
当 Chainguard 预置镜像不满足需求时(如需要额外系统工具),用 apko 从 Wolfi 包仓库自行组装:
# apko.yaml
contents:
repositories:
- https://packages.wolfi.dev/os
keyring:
- https://packages.wolfi.dev/os/wolfi-signing.rsa.pub
packages:
- wolfi-baselayout
- python-3.12
- py3-pip
- ca-certificates-bundle
- curl # 仅在确实需要时加入
accounts:
groups:
- groupname: app
gid: 1000
users:
- username: app
uid: 1000
gid: 1000
run-as: 1000
entrypoint:
command: /usr/bin/python3
archs:
- x86_64
- aarch64
构建镜像:
# 安装 apko
go install chainguard.dev/apko@latest
# 构建并加载到 Docker
apko build apko.yaml my-wolfi-python:latest my-wolfi-python.tar
docker load < my-wolfi-python.tar
第 3 步:集成到 CI/CD 流水线
在 GitHub Actions 中自动构建 + 签名 + 扫描:
name: Build Wolfi Image
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # cosign keyless 签名需要
steps:
- uses: actions/checkout@v4
- name: Build with apko
uses: chainguard-images/actions/apko-build@main
with:
config: apko.yaml
tag: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Sign with Cosign
uses: sigstore/cosign-installer@v3
- run: cosign sign --yes ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Scan with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }}
exit-code: 1 # CVE 非零即失败
severity: HIGH,CRITICAL
第 4 步:验证 SBOM 和来源证明
Chainguard Images 自带 SBOM 和构建证明(attestation),可直接验证:
# 查看镜像 SBOM
cosign download attestation cgr.dev/chainguard/python:latest \
| jq -r '.payload' | base64 -d | jq '.predicate.packages[]' | head -20
# 验证签名
cosign verify cgr.dev/chainguard/python:latest \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--certificate-identity-regexp='.*chainguard.*'
避坑指南
1. musl vs glibc 兼容性问题
从 Alpine 迁移到 Wolfi 时注意:Wolfi 用 glibc,Alpine 用 musl。如果你的应用依赖预编译的 musl 二进制,需要重新编译。反过来,从 Debian/Ubuntu 迁移则基本无缝。
2. -dev 标签不要用于生产
cgr.dev/chainguard/python:latest-dev 包含 shell 和包管理器,仅用于多阶段构建的编译阶段。最终镜像必须用 latest(不含 shell),否则攻击面并未缩小。
3. 免费版 vs 付费版的差异
Chainguard 的免费 Developer 镜像仅提供 :latest 标签且无 SLA。生产环境需要固定版本标签(如 python:3.12.4)需订阅 Production 版。替代方案:直接用 apko + Wolfi 公开包仓库自行构建,完全免费且可固定版本。
总结
| 维度 | Debian slim | Alpine | Chainguard/Wolfi |
|---|---|---|---|
| 典型 CVE 数 | 50-300 | 10-50 | 0-5 |
| C 库 | glibc | musl | glibc |
| 镜像大小 | 80-150 MB | 30-50 MB | 20-40 MB |
| 调试便利性 | 高 | 中 | 低(无 shell) |
| 供应链安全 | 无 SBOM | 无 SBOM | 原生 SBOM + 签名 |
核心结论:对安全审计有硬性要求的团队,Chainguard Images 是性价比最高的「零 CVE」路径——替换一行 FROM 即可。需要更多自定义控制时,apko + Wolfi 包是 DIY 的正确姿势。两者都应搭配 Cosign 签名 + Trivy 扫描形成完整的容器供应链安全闭环。