3. 镜像签名基础:Cosign的工作原理、生成密钥对、对容器镜像进行签名

在Serverless流水线中实施镜像签名验证,首先需要理解签名工具的核心机制。Cosign是Sigstore项目下最成熟的容器镜像签名工具,它利用公钥基础设施(PKI)或更现代的密钥less模式,为OCI制品提供加密签名。本章将深入Cosign的工作原理,并演示从密钥生成到镜像签名的完整操作流程。

3.1 Cosign签名架构与工作流程

Cosign的签名模型基于“签名-验证”分离原则:签名者使用私钥对镜像摘要(Digest)进行签名,验证者使用对应的公钥验证签名的合法性与完整性。签名本身不修改原始镜像层,而是作为独立的OCI制品(通常是.sig或.att标签)存储在镜像仓库中。

核心设计理念:签名绑定的是镜像的加密摘要(SHA256),而非镜像标签(Tag)。这防止了标签被篡改后签名仍然有效的风险。
Cosign 签名与验证流程 签名阶段(Sign) 容器镜像 私钥(Private Key) 生成签名(Signature .sig) 推送至镜像仓库(.sig 制品) 验证阶段(Verify) 镜像 + 签名 公钥(Public Key) 验证签名与摘要匹配 验证通过 / 拒绝

3.2 生成密钥对:Cosign的密钥管理

Cosign支持两种密钥生成模式:本地密钥对和密钥less模式(Keyless)。在Serverless流水线中,推荐使用密钥less模式结合OIDC身份认证,但理解本地密钥对是掌握签名基础的前提。

3.2.1 本地密钥对生成

使用cosign generate-key-pair命令生成一对加密密钥:

# 生成密钥对(默认使用ECDSA-P256算法)
cosign generate-key-pair

# 输出文件:
#   cosign.key  —— 私钥(必须安全保管,建议使用KMS或密钥管理服务)
#   cosign.pub  —— 公钥(可公开,用于验证签名)

# 指定输出路径
cosign generate-key-pair --output-key-prefix myapp
# 生成 myapp.key 和 myapp.pub
安全警告:私钥(.key文件)是签名的唯一凭证。在CI/CD流水线中,不要将私钥明文存储在代码仓库或环境变量中。应使用密钥管理服务(如AWS KMS、Azure Key Vault、HashiCorp Vault)或Cosign的KMS集成。

3.2.2 密钥less模式(推荐用于Serverless)

密钥less模式利用Sigstore的Fulcio证书颁发机构和Rekor透明日志,无需管理本地密钥对:

# 密钥less签名(自动获取临时证书)
cosign sign --keyless <镜像引用>

# 密钥less验证(自动查询Rekor日志)
cosign verify --keyless <镜像引用>
特性 本地密钥对 密钥less模式
密钥管理 需要自行保管私钥 无需管理密钥,临时证书自动签发
身份绑定 绑定公钥指纹 绑定OIDC身份(如GitHub Actions、GitLab CI)
审计追溯 无内置透明日志 签名记录写入Rekor透明日志,可公开审计
适用场景 私有仓库、离线环境 公有云CI/CD、Serverless流水线

3.3 对容器镜像进行签名

签名操作的核心是计算镜像的加密摘要,并使用私钥对该摘要进行签名。Cosign自动处理摘要提取、签名生成和制品推送。

3.3.1 使用本地密钥签名

# 1. 构建或拉取镜像
docker build -t registry.example.com/myapp:v1 .
docker push registry.example.com/myapp:v1

# 2. 使用私钥签名
cosign sign --key cosign.key registry.example.com/myapp:v1

# 3. 验证签名
cosign verify --key cosign.pub registry.example.com/myapp:v1

# 输出示例:
# Verification for registry.example.com/myapp:v1 --
# The following checks were performed on each of these signatures:
#   - The cosign claims were validated
#   - The signatures were verified against the specified public key

3.3.2 签名后的仓库结构

签名完成后,镜像仓库中会新增一个与镜像摘要关联的签名制品:

# 查看镜像标签
skopeo list-tags docker://registry.example.com/myapp

# 输出:
# {
#   "Repository": "registry.example.com/myapp",
#   "Tags": [
#     "v1",
#     "v1.sig"          ← Cosign自动生成的签名标签
#   ]
# }

# 签名标签的命名规则:
# <原始标签>.sig  —— 签名制品
# <原始标签>.att  —— 附加声明(in-toto attestation)

3.3.3 签名验证的详细过程

当验证者执行cosign verify时,Cosign执行以下步骤:

  1. 拉取签名制品:从镜像仓库获取与镜像标签关联的.sig制品。
  2. 计算镜像摘要:对当前镜像内容重新计算SHA256摘要。
  3. 解密签名:使用公钥解密签名,得到原始摘要的哈希值。
  4. 摘要比对:比较解密后的哈希值与重新计算的摘要是否一致。
  5. 验证声明:检查签名中的声明(如签名时间、身份信息)是否有效。
Cosign 签名验证详细步骤 ① 拉取签名制品 (.sig 标签) ② 计算镜像摘要 (SHA256) ③ 解密签名 (公钥解密) ④ 摘要比对 (哈希匹配?) ✅ 验证通过 ❌ 验证拒绝

3.4 多平台镜像签名注意事项

对于多架构镜像(manifest list),Cosign要求对每个平台特定的镜像摘要分别签名,或对整个manifest list签名:

# 对多平台镜像签名(自动处理所有子镜像)
cosign sign --key cosign.key registry.example.com/myapp:multi-arch

# 验证多平台镜像
cosign verify --key cosign.pub registry.example.com/myapp:multi-arch

# 查看签名是否覆盖所有平台
cosign verify-attestation --key cosign.pub registry.example.com/myapp:multi-arch
最佳实践:在Serverless流水线中,建议在镜像构建完成后立即执行签名操作,并将签名步骤作为流水线的强制门禁。结合OPA策略,可以在部署前验证签名是否满足组织定义的策略规则。

3.5 本章小结

  • Cosign签名模型:基于镜像摘要的加密签名,不修改原始镜像层,签名作为独立OCI制品存储。
  • 密钥管理:本地密钥对适用于私有环境;密钥less模式利用OIDC和透明日志,更适合Serverless流水线。
  • 签名操作:cosign sign自动完成摘要计算、签名生成和制品推送;cosign verify执行四步验证流程。
  • 多平台支持:Cosign原生支持manifest list签名,确保多架构镜像的完整性。