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)。
4.2 签名流程与密钥管理
Cosign 支持两种签名模式:密钥对模式(传统公/私钥)和密钥自由模式(基于 OIDC + Fulcio 短期证书)。生产环境推荐后者,因为它消除了长期密钥的存储与轮换风险。
4.2.1 密钥对模式签名流程
- 生成密钥对:
cosign generate-key-pair生成cosign.key(私钥)和cosign.pub(公钥)。 - 签名镜像:
cosign sign --key cosign.key <image>对镜像摘要签名,签名信息以 OCI 制品形式存储在镜像仓库中。 - 上传公钥: 将公钥安全分发给验证方(如通过密钥管理服务 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 日志查询失败 | ❌ | ✅ | 无法验证签名透明度记录 |
4.4 回滚场景中的签名验证
在回滚操作中,镜像签名验证同样关键。回滚通常涉及拉取历史版本的镜像,这些镜像可能由不同开发者、不同 CI 管道签名,甚至可能未签名(如果回滚到签名策略实施前的版本)。
回滚签名验证策略建议:
- 宽松模式: 允许回滚到未签名镜像,但记录审计警告。
- 严格模式: 所有回滚镜像必须通过签名验证,否则拒绝回滚。
- 混合模式: 对回滚操作使用独立的验证策略(如仅验证镜像摘要,不验证签名者身份)。
通过将签名验证与准入控制结合,团队可以在回滚时自动检查镜像的完整性和来源,避免因回滚到被篡改或恶意镜像而导致安全事件。