4. 镜像签名实战:使用 Cosign 对容器镜像进行签名、签名格式解析、签名与镜像的关联机制

镜像签名是云原生供应链安全的核心防线。本节将围绕 Cosign 工具,从密钥生成、镜像签名、签名格式解析到签名与镜像的关联机制,进行完整的实战讲解与原理剖析。

4.1 环境准备与密钥生成

Cosign 支持多种密钥后端,包括本地密钥对、KMS、Tink 等。本节以本地密钥对为例,演示最通用的操作流程。

前置条件: 确保已安装 Cosign(v2.0+)和 Docker / containerd 环境。推荐使用 Cosign v2.2 以上版本以支持更完善的 OCI 1.1 规范。

4.1.1 生成密钥对

# 生成加密私钥与公钥对
cosign generate-key-pair

# 执行后会在当前目录生成两个文件:
#   cosign.key   —— 加密私钥(需密码保护)
#   cosign.pub   —— 公钥(用于验证签名)
文件 用途 安全等级
cosign.key 签名操作,必须妥善保管 高(建议硬件安全模块或 KMS)
cosign.pub 验证签名,可公开分发 低(仅用于验证)
安全提醒: 私钥密码建议使用 16 位以上随机字符串,并纳入密钥管理系统的轮换策略。生产环境中避免将私钥存储在代码仓库或 CI 日志中。

4.2 镜像签名实战

使用 Cosign 对容器镜像进行签名,本质是将签名数据以 OCI 制品的形式附加到镜像仓库中。以下为完整签名流程。

4.2.1 签名命令与参数

# 对镜像进行签名(使用本地私钥)
cosign sign --key cosign.key \
  registry.example.com/my-app:v1.0.0

# 使用环境变量传递密码(避免交互输入)
export COSIGN_PASSWORD=mySecurePass123
cosign sign --key cosign.key \
  registry.example.com/my-app:v1.0.0

4.2.2 签名流程示意图

镜像签名流程 容器镜像 my-app:v1.0.0 摘要计算 SHA-256 哈希 签名生成 ECDSA-P256 签名制品 application/vnd.dev.cosign.sig 镜像仓库 OCI 存储 签名不修改原镜像 以独立 OCI 制品存储

4.3 签名格式解析

Cosign 生成的签名遵循 OCI 制品规范,以标准 MIME 类型存储在镜像仓库中。签名数据本身是一个 JSON 负载,包含关键元数据。

4.3.1 签名 OCI 制品结构

# 查看签名制品列表
cosign verify --key cosign.pub \
  registry.example.com/my-app:v1.0.0

# 输出示例(简化):
# Verification for registry.example.com/my-app:v1.0.0 --
# The following checks were performed on each of these signatures:
#   - The cosign claims were validated
#   - The signatures were verified against the specified public key
#   - Any certificates were verified against the Fulcio roots

4.3.2 签名负载(Payload)结构

Cosign 签名负载是一个 JSON 对象,包含以下核心字段:

字段 类型 说明
payloadType string 负载类型,固定为 application/vnd.dev.cosign.simplesigning.v1+json
payload base64 Base64 编码的简单签名 JSON 对象
signatures array 签名列表,包含 keyid、sig 等字段

4.3.3 简单签名对象(Simple Signing)

{
  "critical": {
    "identity": {
      "docker-reference": "registry.example.com/my-app"
    },
    "image": {
      "docker-manifest-digest": "sha256:abcdef1234567890..."
    },
    "type": "cosign container image signature"
  },
  "optional": {
    "creator": "cosign 2.2.0",
    "timestamp": 1698000000
  }
}
关键字段解析:
  • critical.image.docker-manifest-digest:镜像的 SHA-256 摘要,用于防篡改绑定。
  • critical.identity.docker-reference:镜像仓库地址,防止签名被重放攻击。
  • optional.timestamp:签名时间戳,用于时效性校验。

4.4 签名与镜像的关联机制

Cosign 通过 OCI 引用(Reference)机制将签名与镜像关联。每个签名制品通过镜像摘要(Digest)建立不可篡改的绑定关系。

4.4.1 关联机制原理

签名与镜像关联机制 容器镜像 sha256:abc123... my-app:v1.0.0 签名制品 sha256:def456... cosign.sig 通过 Digest 绑定 OCI 仓库 registry.example.com/my-app 不可篡改绑定 修改镜像 → 签名失效

4.4.2 关联验证流程

  1. 提取镜像摘要: 从镜像清单(Manifest)中获取 sha256:... 摘要。
  2. 查找签名制品: 在仓库中查找 sha256-...-sig 标签的 OCI 制品。
  3. 解析签名负载: 读取签名中的 critical.image.docker-manifest-digest 字段。
  4. 摘要比对: 将签名中的摘要与当前镜像摘要进行比对,一致则通过。
  5. 公钥验证: 使用公钥对签名数据进行 ECDSA 验签。

4.4.3 验证命令实战

# 使用公钥验证签名
cosign verify --key cosign.pub \
  registry.example.com/my-app:v1.0.0

# 验证通过时输出:
# Verification OK -- The following checks were performed:
#   - The cosign claims were validated
#   - The signatures were verified against the specified public key

# 验证失败时输出:
# Error: verifying signature: invalid signature when validating
最佳实践: 在 CI/CD 流水线中,将 cosign verify 作为镜像部署前的强制检查步骤。结合 Kyverno 或 OPA 策略引擎,可以实现自动化的签名验证准入控制。

4.5 签名管理高级操作

4.5.1 多签名与签名追加

# 使用不同密钥追加签名
cosign sign --key cosign-team.key \
  registry.example.com/my-app:v1.0.0

cosign sign --key cosign-security.key \
  registry.example.com/my-app:v1.0.0

# 验证所有签名
cosign verify --key cosign-team.pub \
  --key cosign-security.pub \
  registry.example.com/my-app:v1.0.0

4.5.2 签名清理与轮换

操作 命令 说明
列出签名 cosign tree <image> 显示镜像的所有关联制品
删除签名 cosign clean <image> 移除指定镜像的所有签名
密钥轮换 生成新密钥 → 重新签名 → 废弃旧公钥 建议每 90 天轮换一次
注意: 删除签名操作不可逆,建议在删除前使用 cosign tree 确认签名列表,并确保有备份验证策略。

4.6 本章小结

通过本节实战,我们掌握了以下核心能力:

  • 使用 Cosign 生成密钥对并对容器镜像进行签名。
  • 解析 Cosign 签名格式,理解 Simple Signing 负载结构。
  • 理解签名与镜像通过 OCI Digest 建立的不可篡改关联机制。
  • 掌握多签名管理、签名验证与清理操作。