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 的签名流程简洁高效,核心步骤包括:生成密钥对、签名镜像、存储签名、验证签名。下图展示了完整的签名与验证链路。

Cosign 镜像签名与验证流程 Cosign 镜像签名与验证流程 签名阶段(Signer) 开发者 / CI 系统 持有私钥 / OIDC OCI 镜像仓库 存储镜像 + 签名 推送镜像 Cosign CLI cosign sign 存储签名 验证阶段(Verifier) K8s 准入控制器 Kyverno / OPA 公钥 / Sigstore 信任锚 获取公钥 验证通过 / 拒绝 准入决策 说明: Cosign 支持 Keyless 模式,通过 OIDC 获取临时证书,无需管理长期私钥,降低泄露风险。

4.3.1 签名操作步骤(Cosign CLI)

  1. 生成密钥对(可选,Keyless 模式跳过):
    cosign generate-key-pair
    # 生成 cosign.key(私钥)和 cosign.pub(公钥)
  2. 签名镜像:
    # 使用私钥签名
    cosign sign --key cosign.key <registry>/<image>:<tag>
    
    # Keyless 模式(推荐)
    cosign sign <registry>/<image>:<tag>
    # 浏览器跳转 OIDC 认证,自动获取临时证书
  3. 验证签名:
    # 使用公钥验证
    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,实现无需预先分发公钥的信任模型。

Cosign Keyless 信任链 Cosign Keyless 信任链架构 Sigstore 根 CA 离线根证书 Fulcio 中间 CA 在线签发证书 开发者临时证书 OIDC 身份绑定 镜像签名 包含证书链 验证时,准入控制器通过证书链回溯到 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>.sig Tag)。使用 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 审计日志或准入控制器指标,及时发现签名验证异常。