4. 镜像签名与验证:签名原理、流程与准入控制集成

镜像签名是容器供应链安全的最后一道防线。它确保镜像从构建到部署的全路径中,内容未被篡改且发布者身份可信。本章深入解析基于 Sigstore/Cosign 的签名体系,涵盖签名原理、密钥管理、验证策略以及与 Kubernetes 准入控制器的深度集成。

4.1 镜像签名原理:Sigstore 与 Cosign 架构

Sigstore 是一套开源的软件签名与透明度基础设施,Cosign 是其面向容器镜像的签名工具。Sigstore 的核心创新在于消除了传统 PGP 签名中复杂的密钥分发与信任锚问题,通过短生命周期密钥和无中心化信任模型实现“开箱即用”的签名体验。

核心组件:
  • Cosign: 命令行工具,负责对镜像进行签名、验证和存储签名信息。
  • Fulcio: 根证书颁发机构(CA),根据 OIDC 身份(如 GitHub、Google、Microsoft 账号)签发短期证书。
  • Rekor: 不可变透明度日志,记录所有签名事件,提供审计和溯源能力。
  • OIDC Provider: 身份提供者,用于验证签名者身份(如 GitHub Actions、GitLab CI、Google IAM)。
Sigstore 签名流程架构图 开发者 / CI OIDC 身份 OIDC Provider GitHub / Google Fulcio CA 短期证书签发 Rekor 日志 透明度记录 镜像仓库 OCI 制品存储 身份认证 短期证书 推送签名镜像 记录签名事件 💡 签名流程简述: 1. 开发者通过 OIDC 身份向 Fulcio 申请短期证书 → 2. Fulcio 验证身份后签发证书 → 3. Cosign 使用证书私钥对镜像摘要签名 → 4. 签名与证书一并存入镜像仓库,同时记录到 Rekor 日志。

4.2 签名流程与密钥管理

Cosign 支持两种签名模式:密钥对模式(传统公/私钥)和密钥自由模式(基于 OIDC + Fulcio 短期证书)。生产环境推荐后者,因为它消除了长期密钥的存储与轮换风险。

4.2.1 密钥对模式签名流程

  1. 生成密钥对: cosign generate-key-pair 生成 cosign.key(私钥)和 cosign.pub(公钥)。
  2. 签名镜像: cosign sign --key cosign.key <image> 对镜像摘要签名,签名信息以 OCI 制品形式存储在镜像仓库中。
  3. 上传公钥: 将公钥安全分发给验证方(如通过密钥管理服务 KMS 或环境变量)。

4.2.2 密钥自由模式(Keyless Signing)流程

步骤 操作 说明
1 身份认证 Cosign 通过 OIDC 协议获取身份令牌(如 GitHub OAuth )。
2 证书申请 向 Fulcio 提交令牌,Fulcio 验证后签发 X.509 短期证书(有效期通常为 10 分钟)。
3 签名 使用证书私钥对镜像摘要签名,生成签名载荷(包含证书链)。
4 记录 签名载荷上传至 Rekor 透明度日志,获取日志索引(UUID)。
5 存储 签名与证书以 OCI 制品形式推送至镜像仓库,与镜像关联。
⚠️ 密钥管理最佳实践:
  • 避免私钥硬编码: 永远不要将私钥写入代码仓库或 CI 日志。
  • 使用 KMS 托管: 集成 AWS KMS、GCP Cloud KMS 或 Azure Key Vault 管理签名密钥。
  • 短期证书优先: Keyless 模式自动处理证书轮换,降低密钥泄露风险。
  • 定期轮换: 若使用密钥对模式,建议每 90 天轮换一次密钥。

4.3 验证策略与准入控制集成

验证策略定义了“什么样的签名才是可信的”。在 Kubernetes 环境中,通过 Kyverno 或 OPA Gatekeeper 等准入控制器,可以在 Pod 创建前强制验证镜像签名。

4.3.1 Cosign 验证命令

# 验证镜像签名(密钥对模式)
cosign verify --key cosign.pub <image>

# 验证镜像签名(Keyless 模式)
cosign verify --certificate-identity <email> --certificate-oidc-issuer <issuer> <image>

# 验证并检查 Rekor 日志
cosign verify --rekor-url https://rekor.sigstore.dev <image>

4.3.2 准入控制策略示例(Kyverno)

以下 Kyverno 策略强制要求所有镜像必须经过 Cosign 签名验证,且签名者身份必须来自受信任的 OIDC 提供者。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-cosign-signature
spec:
  validationFailureAction: Enforce
  rules:
  - name: verify-image-signature
    match:
      any:
      - resources:
          kinds:
          - Pod
    verifyImages:
    - imageReferences:
      - "*"
      attestors:
      - count: 1
        entries:
        - keys:
            publicKeys: |-
              -----BEGIN PUBLIC KEY-----
              MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
              -----END PUBLIC KEY-----
        - keyless:
            identities:
            - issuer: "https://accounts.google.com"
              subject: "user@example.com"
      repository: "docker.io/mycompany"
💡 策略要点:
  • attestors.count: 指定需要满足的验证条件数量(如至少 1 个签名者通过)。
  • keys: 使用公钥验证传统签名。
  • keyless: 基于 OIDC 身份和签发者验证 Keyless 签名。
  • repository: 限制仅验证特定仓库的镜像。

4.3.3 验证策略决策矩阵

验证条件 通过 拒绝 说明
签名存在且公钥匹配 ✅ ❌ 传统密钥对模式验证通过
Keyless 签名且身份匹配 ✅ ❌ OIDC 身份与签发者均符合策略
签名存在但公钥不匹配 ❌ ✅ 镜像可能被篡改或使用不同密钥签名
无签名 ❌ ✅ 镜像未签名,策略强制要求签名
Rekor 日志查询失败 ❌ ✅ 无法验证签名透明度记录
准入控制镜像签名验证流程 创建 Pod 请求 Kyverno 准入控制器 Cosign 签名验证 策略规则评估 允许 / 拒绝 Pod 创建 🔒 验证通过条件:签名有效 + 签名者身份匹配策略 + Rekor 日志可查 → 允许创建;否则拒绝并记录审计事件。

4.4 回滚场景中的签名验证

在回滚操作中,镜像签名验证同样关键。回滚通常涉及拉取历史版本的镜像,这些镜像可能由不同开发者、不同 CI 管道签名,甚至可能未签名(如果回滚到签名策略实施前的版本)。

回滚签名验证策略建议:
  • 宽松模式: 允许回滚到未签名镜像,但记录审计警告。
  • 严格模式: 所有回滚镜像必须通过签名验证,否则拒绝回滚。
  • 混合模式: 对回滚操作使用独立的验证策略(如仅验证镜像摘要,不验证签名者身份)。

通过将签名验证与准入控制结合,团队可以在回滚时自动检查镜像的完整性和来源,避免因回滚到被篡改或恶意镜像而导致安全事件。