4. 镜像签名基础:使用 Cosign 对容器镜像进行签名、签名格式解析、签名存储位置
容器镜像签名是供应链安全的第一道防线。Cosign 作为 Sigstore 生态的核心工具,提供了开箱即用的镜像签名与验证能力。本节将深入讲解如何使用 Cosign 对镜像进行签名、解析签名数据的内部结构,以及签名在镜像仓库中的实际存储机制。
4.1 使用 Cosign 对容器镜像进行签名
Cosign 支持两种主要的签名模式:密钥对签名(适用于企业自有 PKI)和 Keyless 签名(基于 OIDC 身份与 Fulcio 证书)。以下分别演示两种方式。
4.1.1 密钥对签名
首先生成密钥对,然后对镜像进行签名:
# 生成密钥对(私钥加密存储)
cosign generate-key-pair
# 输出文件:
# cosign.key — 私钥(需妥善保管)
# cosign.pub — 公钥(分发给验证方)
# 对镜像签名(需先推送镜像到仓库)
cosign sign --key cosign.key registry.example.com/myapp:v1.0.0
# 验证签名
cosign verify --key cosign.pub registry.example.com/myapp:v1.0.0
4.1.2 Keyless 签名(无密钥模式)
Keyless 模式利用 GitHub/GitLab/Google 等 OIDC 身份自动获取短期证书,无需管理密钥:
# 直接签名(自动触发 OIDC 流程)
cosign sign registry.example.com/myapp:v1.0.0
# 验证(自动拉取证书链与 Rekor 时间戳)
cosign verify registry.example.com/myapp:v1.0.0
生产建议: 在 CI/CD 流水线中优先使用 Keyless 签名,避免私钥泄露风险;对于离线环境或严格合规场景,使用密钥对签名并配合硬件安全模块(HSM)保护私钥。
4.2 签名格式解析
Cosign 生成的签名并非单一文件,而是由多个组件构成的 签名包(Signature Bundle)。下表列出了签名包的核心组成部分:
| 组件 | 格式 | 说明 |
|---|---|---|
| 签名载荷 | Base64 编码的 JSON | 包含镜像 Digest、签名算法、签名时间等元数据 |
| 签名值 | Base64 编码的原始签名 | 对载荷的签名结果(ECDSA P-256 或 RSA) |
| 证书链 | PEM 格式 | Keyless 模式下包含 Fulcio 签发的短期证书及中间 CA |
| Rekor 条目 | JSON | 透明日志中的条目 ID,用于审计与不可否认性 |
以下是一个签名载荷的示例(解码后):
{
"critical": {
"identity": {
"docker-reference": "registry.example.com/myapp"
},
"image": {
"docker-manifest-digest": "sha256:abc123..."
},
"type": "cosign container image signature"
},
"optional": {
"creator": "cosign 2.2.0",
"timestamp": "2025-03-15T10:30:00Z"
}
}
关键字段解析:
critical.image.docker-manifest-digest— 签名绑定的镜像摘要,防止签名被挪用到其他镜像。critical.identity.docker-reference— 签名时使用的镜像引用,用于验证上下文。optional.timestamp— 签名时间戳(Keyless 模式下由 Rekor 提供不可篡改的时间证明)。
4.3 签名存储位置:镜像仓库注解
Cosign 将签名存储在 镜像仓库的注解(Annotations) 中,而非修改镜像层本身。具体存储机制如下:
- 签名作为独立 OCI 制品:Cosign 将签名包打包为一个 OCI 镜像制品,推送到与原始镜像相同的仓库。
- 命名约定:签名制品的标签为
sha256-<镜像摘要>.sig,例如sha256-abc123....sig。 - 注解键值对:签名元数据存储在制品的注解中,键为
dev.cosignproject.cosign/signature,值为 Base64 编码的签名载荷。
以下 SVG 展示了签名在镜像仓库中的存储结构:
重要注意事项:
- 签名制品与原始镜像共享同一个仓库,但作为独立的 OCI 清单存在,不会污染镜像层。
- 删除原始镜像时,签名制品不会自动删除,需要单独清理(建议使用
cosign clean命令)。 - 部分镜像仓库(如 Harbor、Docker Hub)支持在 UI 中查看签名注解,但大多数仓库需要 API 查询。
4.4 签名验证流程
验证签名时,Cosign 执行以下步骤:
- 拉取签名制品:根据镜像 Digest 构造签名制品标签
sha256-<digest>.sig。 - 解析注解:从签名制品中读取
dev.cosignproject.cosign/signature注解。 - 解码签名载荷:Base64 解码后得到 JSON 载荷,提取镜像 Digest 和签名值。
- 验证签名:使用公钥或 Fulcio 证书验证签名值是否与载荷匹配。
- 验证镜像 Digest:确认载荷中的 Digest 与当前镜像的 Digest 一致,防止签名复用攻击。
- (可选)查询 Rekor:验证签名是否已记录在透明日志中,确保签名的时效性与不可否认性。
以下 SVG 展示了完整的签名验证流程:
验证命令示例:
# 使用公钥验证
cosign verify --key cosign.pub registry.example.com/myapp:v1.0.0
# Keyless 验证(自动拉取证书与 Rekor)
cosign verify registry.example.com/myapp:v1.0.0
# 输出示例(验证通过时):
# Verification for registry.example.com/myapp:v1.0.0 --
# The following checks were performed on each of these signatures:
# - The cosign claims were validated
# - Existence of the claims in the transparency log was verified
# - The signatures were verified against the specified public key
4.5 签名管理最佳实践
- 签名策略:在 Kubernetes 集群中配置
cosign verify-admission准入控制器,强制所有部署的镜像必须经过签名验证。 - 密钥轮换:使用密钥对签名时,定期轮换密钥并更新公钥分发机制(如存储在 Kubernetes Secret 或 OCI 仓库中)。
- 签名审计:利用 Rekor 透明日志审计所有签名操作,确保签名行为的可追溯性。
- 多签名支持:Cosign 支持对同一镜像添加多个签名(不同密钥或不同身份),适用于多团队协作场景。