4、镜像签名基础:对容器镜像进行签名、使用Cosign sign命令、签名格式与存储位置

镜像签名是供应链安全的第一道防线。通过为容器镜像附加数字签名,可以确保镜像在构建、传输和存储过程中未被篡改,并且能够验证其发布者的真实身份。本章将深入讲解如何使用 Cosign 对镜像进行签名,解析签名格式及其存储机制。

4.1 签名前的准备:密钥对生成

Cosign 支持多种密钥管理方式,包括本地密钥对、云 KMS(Key Management Service)以及硬件安全模块。对于入门场景,推荐使用本地生成的密钥对。

🔑 密钥对生成命令:
# 生成私钥和公钥(默认使用加密强度较高的 ECDSA P-256 曲线)
cosign generate-key-pair

# 执行后会在当前目录生成两个文件:
#   cosign.key  —— 私钥(必须妥善保管,建议设置密码保护)
#   cosign.pub  —— 公钥(用于验证签名,可公开分发)
💡 安全提示:私钥文件 cosign.key 是签名的核心凭证。建议将其存储在密钥管理服务(如 AWS KMS、Azure Key Vault、HashiCorp Vault)中,避免以明文形式保存在代码仓库或 CI/CD 配置文件中。

4.2 使用 cosign sign 命令签名

签名操作的核心命令是 cosign sign。它会对镜像的摘要(Digest)进行签名,并将签名信息附加到镜像仓库中。签名过程不修改镜像层数据,仅添加元数据。

4.2.1 基本签名流程

  1. 推送镜像到仓库:确保目标镜像已推送到支持 OCI(Open Container Initiative)标准的镜像仓库(如 Harbor、Docker Hub、GitHub Container Registry)。
  2. 执行签名命令:
# 使用本地私钥对镜像进行签名
cosign sign --key cosign.key <镜像仓库地址>/<镜像名>:<标签>

# 示例:对 myapp:v1.0 签名
cosign sign --key cosign.key registry.example.com/myapp:v1.0
⚠️ 注意:签名时推荐使用镜像的摘要(Digest)而非标签(Tag),因为标签是可变的。使用摘要可以确保签名与不可变的镜像内容绑定。
# 先获取镜像摘要
cosign verify --key cosign.pub registry.example.com/myapp:v1.0

# 对摘要进行签名(更安全)
cosign sign --key cosign.key registry.example.com/myapp@sha256:abcdef...

4.2.2 签名参数详解

参数 说明 示例
--key 指定私钥文件路径或 KMS 引用 --key cosign.key
--tlog-upload 是否将签名记录上传到 Rekor 透明度日志(默认 true) --tlog-upload=false
--annotations 附加自定义元数据(键值对) --annotations "env=prod"
--force 覆盖已有签名(谨慎使用) --force
--recursive 对多架构镜像的所有变体签名 --recursive

4.3 签名格式与存储位置

Cosign 的签名遵循 OCI 规范,以“镜像签名对象”的形式存储在镜像仓库中。理解签名格式和存储结构,对于后续的验证和审计至关重要。

4.3.1 签名格式:DSSE + Payload

Cosign 使用 DSSE(Dead Simple Signing Envelope) 格式封装签名。DSSE 是一种轻量级的签名信封,包含三个部分:

  • Payload(负载):被签名的原始数据,即镜像清单(Manifest)的 SHA256 摘要。
  • Signature(签名值):使用私钥对 Payload 进行签名后生成的数字签名。
  • Signer(签名者标识):公钥的哈希或证书信息,用于快速定位验证密钥。
📄 签名负载示例(简化):
{
  "payloadType": "application/vnd.dev.cosign.simplesigning.v1+json",
  "payload": "eyJjcml0aWNhbCI6eyJpZGVudGl0eSI6eyJkb2NrZXItcmVmZXJlbmNlIjoicmVnaXN0cnkuZXhhbXBsZS5jb20vbXlhcHA6djEuMCJ9fX0=",
  "signatures": [
    {
      "keyid": "sha256:abc123...",
      "sig": "MEUCIQ..."
    }
  ]
}

4.3.2 存储位置:OCI 签名标签

签名不会覆盖原始镜像,而是作为独立的 OCI 制品(Artifact)存储在镜像仓库中。Cosign 使用特定的标签命名约定来关联签名与镜像。

存储组件 说明 示例
原始镜像 镜像层和清单,存储在标准标签下 myapp:v1.0
签名对象 存储在 sha256-<镜像摘要>.sig 标签下 sha256-abcdef...sig
签名索引 多签名场景下的索引清单 sha256-abcdef...sig-index
SBOM(可选) 软件物料清单,存储在 .sbom 标签下 sha256-abcdef...sbom
🔍 查看签名存储:可以使用 cosign tree 命令查看镜像及其关联的签名、SBOM 等制品的完整树状结构。
cosign tree registry.example.com/myapp:v1.0

4.4 签名流程架构图

下图展示了从密钥生成到签名存储的完整流程,帮助理解各组件之间的交互关系。

Cosign 镜像签名流程架构图 Cosign 镜像签名流程架构图 密钥生成 cosign generate-key-pair 私钥 (cosign.key) 公钥 (cosign.pub) 容器镜像 registry.example.com/myapp:v1.0 cosign sign --key cosign.key DSSE 签名对象 Payload + Signature OCI 镜像仓库 myapp:v1.0 sha256-xxx.sig sha256-xxx.sbom (签名 + 元数据)

4.5 多签名与协同签名

在实际生产环境中,一个镜像可能需要多个角色(如开发、安全、发布经理)分别签名,以体现责任分离和审批流程。Cosign 支持对同一镜像附加多个签名。

🔄 多签名示例:
# 开发者签名
cosign sign --key dev.key registry.example.com/myapp:v1.0

# 安全团队签名
cosign sign --key security.key registry.example.com/myapp:v1.0

# 发布经理签名
cosign sign --key release.key registry.example.com/myapp:v1.0

每个签名都会生成独立的 DSSE 对象,存储在镜像仓库的 .sig 标签下。验证时,可以指定需要满足的签名策略(如“至少需要 2 个有效签名”)。

4.6 签名验证快速验证

签名完成后,可以使用 cosign verify 命令立即验证签名是否有效。

# 使用公钥验证签名
cosign verify --key cosign.pub registry.example.com/myapp:v1.0

# 验证通过时输出签名信息,包括签名者、签名时间等
# 验证失败时输出错误信息,提示签名无效或密钥不匹配
⚠️ 常见问题:
  • 签名失败:检查私钥路径是否正确,以及镜像是否已推送到仓库。
  • 验证失败:确认使用的公钥与签名时使用的私钥是否匹配。
  • 存储位置不可见:部分仓库 UI 可能不显示 .sig 标签,但通过 API 或 cosign tree 可以查看。