4. 镜像签名与验证:Notary 与 Cosign 工具、镜像签名流程、验证策略与信任链建立
在云原生环境中,确保容器镜像的完整性与来源可信是安全供应链的基石。镜像签名与验证机制通过密码学手段,防止镜像被篡改或替换,是准入控制策略的核心数据来源。本章深入剖析主流签名工具 Notary 与 Cosign 的工作原理、签名流程、验证策略及信任链建立方法。
4.1 镜像签名核心概念与价值
镜像签名并非加密镜像内容,而是对镜像清单(Manifest)的摘要进行数字签名。验证方通过公钥验证签名,确保镜像自签名后未被修改,且签名者身份可信。
| 概念 | 说明 | 安全价值 |
|---|---|---|
| 摘要(Digest) | 镜像内容的 SHA256 哈希值,唯一标识镜像版本 | 防篡改:任何字节变化都会导致摘要变更 |
| 数字签名 | 使用私钥对摘要加密生成的签名数据 | 防抵赖:只有私钥持有者能生成有效签名 |
| 信任锚(Trust Anchor) | 验证签名的根公钥或证书颁发机构(CA) | 建立信任链起点,防止中间人攻击 |
| 签名元数据 | 包含签名算法、签名时间、签名者身份等信息 | 提供审计与策略决策依据 |
实践要点:签名验证必须与准入控制器(如 OPA Gatekeeper、Kyverno)结合,在 Pod 创建前强制检查签名有效性,否则签名本身无法阻止恶意镜像运行。
4.2 主流签名工具对比:Notary 与 Cosign
Notary 是 Docker 内容信任(DCT)的底层实现,基于 TUF(The Update Framework)规范;Cosign 是 Sigstore 项目的一部分,设计更轻量,原生支持 OCI 制品。两者在架构、密钥管理和集成方式上存在显著差异。
| 特性 | Notary(Docker Content Trust) | Cosign(Sigstore) |
|---|---|---|
| 底层规范 | TUF(The Update Framework) | Sigstore 协议(基于 TUF 简化) |
| 密钥管理 | 离线根密钥 + 在线目标密钥,多层轮换 | 支持静态密钥、KMS、OIDC 临时密钥(Keyless) |
| 签名存储 | 独立的 Notary 服务端 + 数据库 | OCI 仓库 Tag 或 Referrers API(直接附加) |
| 集成复杂度 | 高:需部署 Notary Server、Signer、数据库 | 低:CLI 工具 + OCI 仓库即可运行 |
| Keyless 模式 | 不支持 | 支持(通过 Fulcio CA + OIDC 身份) |
| K8s 原生支持 | 需额外适配(如 Docker Engine 集成) | 原生支持,Kyverno/OPA 可直接调用 Cosign 验证 |
选型建议:新项目优先选择 Cosign,因其轻量、Keyless 模式降低密钥管理风险,且与 K8s 生态集成更紧密。遗留 Docker Registry 环境且已部署 Notary 的团队可继续使用,但需关注运维成本。
4.3 镜像签名流程详解(以 Cosign 为例)
Cosign 的签名流程简洁高效,核心步骤包括:生成密钥对、签名镜像、存储签名、验证签名。下图展示了完整的签名与验证链路。
4.3.1 签名操作步骤(Cosign CLI)
- 生成密钥对(可选,Keyless 模式跳过):
cosign generate-key-pair # 生成 cosign.key(私钥)和 cosign.pub(公钥) - 签名镜像:
# 使用私钥签名 cosign sign --key cosign.key <registry>/<image>:<tag> # Keyless 模式(推荐) cosign sign <registry>/<image>:<tag> # 浏览器跳转 OIDC 认证,自动获取临时证书 - 验证签名:
# 使用公钥验证 cosign verify --key cosign.pub <registry>/<image>:<tag> # Keyless 模式验证 cosign verify <registry>/<image>:<tag>
安全警告:私钥(cosign.key)必须妥善保管,建议使用 KMS(如 AWS KMS、GCP Cloud KMS)或硬件安全模块(HSM)存储。切勿将私钥硬编码在 CI 配置文件或代码仓库中。
4.4 验证策略与信任链建立
验证策略定义了“谁可以签名”以及“哪些签名是可信的”。信任链从根信任锚出发,通过中间证书或密钥层级传递信任。在 K8s 环境中,验证策略通常由准入控制器执行。
4.4.1 信任链模型
信任链确保验证方能够追溯签名者的身份。Cosign 的 Keyless 模式通过 Fulcio CA 签发短期证书,证书链锚定到 Sigstore 根 CA,实现无需预先分发公钥的信任模型。
4.4.2 验证策略定义(Kyverno 示例)
在 K8s 中,通过 Kyverno ClusterPolicy 定义镜像签名验证策略,强制所有 Pod 使用的镜像必须经过指定签名者签名。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce
rules:
- name: check-cosign-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "registry.example.com/*"
attestors:
- count: 1
entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----
required: true
mutateDigest: true
策略要点:
validationFailureAction: Enforce 表示拒绝不符合策略的 Pod;attestors 定义可信签名者列表,支持多密钥轮换;mutateDigest: true 自动将镜像 Tag 转换为摘要,防止 Tag 漂移攻击。
4.4.3 验证策略对比
| 策略类型 | 验证方式 | 适用场景 | 安全强度 |
|---|---|---|---|
| 公钥验证 | 预分发公钥,验证签名 | 内部私有仓库,密钥管理成熟 | 高(需保护私钥) |
| Keyless 验证 | OIDC 身份 + 临时证书链 | 开源项目、多团队协作 | 高(无长期密钥泄露风险) |
| 多签名者策略 | 要求 N 个签名者中 M 个通过 | 合规审计、多角色审批 | 极高(防止单点故障) |
| 忽略未签名镜像 | 仅允许签名镜像,其余拒绝 | 生产环境严格管控 | 最高(零信任基线) |
4.5 故障排查与常见问题
镜像签名验证失败是准入控制中的常见故障。以下列出典型问题及排查方法:
- 签名未找到:检查镜像仓库是否存储了签名对象(Cosign 默认使用
sha256-<digest>.sigTag)。使用cosign verify命令手动验证。 - 公钥不匹配:确认准入控制器配置的公钥与签名时使用的私钥对应。Keyless 模式需确保 Fulcio 证书链可访问。
- 证书过期:Keyless 模式证书有效期短(通常 10 分钟),CI 流程中签名后应立即推送并验证,避免延迟。
- 网络隔离:验证时需访问 Sigstore 服务(fulcio、rekor)或内部公钥分发点。检查网络策略是否允许。
- 镜像 Tag 漂移:启用
mutateDigest: true后,准入控制器将 Tag 替换为摘要,确保后续验证基于不可变标识。
快速诊断命令:
# 手动验证签名
cosign verify --key cosign.pub registry.example.com/myapp:v1.0
# 查看签名元数据
cosign verify-attestation --key cosign.pub registry.example.com/myapp:v1.0
# 检查 Keyless 证书链
cosign verify registry.example.com/myapp:v1.0 --certificate-identity "email@example.com" --certificate-oidc-issuer "https://accounts.google.com"
4.6 最佳实践总结
- 优先使用 Keyless 模式:减少密钥管理负担,利用 OIDC 身份绑定提升审计能力。
- 结合准入控制器:签名验证必须与 Kyverno 或 OPA Gatekeeper 联动,实现自动化强制策略。
- 定期轮换密钥:使用静态密钥时,制定密钥轮换计划,并通过多签名者策略平滑过渡。
- 启用摘要突变:避免 Tag 引用,确保验证基于不可变镜像摘要。
- 监控签名失败事件:通过 K8s 审计日志或准入控制器指标,及时发现签名验证异常。