4. 镜像签名实战:对容器镜像进行签名(cosign sign)
镜像签名是供应链信任的核心执行动作。通过 cosign sign 命令,开发者使用私钥对镜像摘要(Digest)进行数字签名,并将签名产物存储在 OCI 仓库中。本节将深入签名命令的完整用法、密钥指定方式、签名格式与元数据,以及签名后镜像的不可变性原理。
4.1 签名命令基础与密钥指定
cosign sign 的基本语法如下:
cosign sign --key <私钥路径> <镜像引用>
镜像引用支持 Tag 和 Digest 两种形式。推荐使用 Digest 形式以确保签名对象的确定性。
| 参数 | 说明 | 示例 |
|---|---|---|
--key |
指定私钥文件路径或 KMS 资源标识符 | --key cosign.key |
--tlog-upload |
是否将签名记录上传至 Rekor 透明度日志(默认 true) | --tlog-upload=false |
--annotations |
附加自定义元数据到签名载荷 | --annotations env=prod |
--recursive |
对多架构镜像的所有子镜像签名 | --recursive |
最佳实践: 在 CI/CD 流水线中,始终使用
--key env://COSIGN_PRIVATE_KEY 从环境变量读取密钥,避免密钥文件暴露在构建日志中。
4.2 签名格式与元数据
Cosign 生成的签名遵循 OCI 签名规范,签名产物以 application/vnd.dev.cosign.simplesigning.v1+json 的媒体类型存储在镜像仓库中。签名载荷包含以下关键字段:
- critical:包含镜像标识(identity)、类型(type)和哈希(image.docker-reference/digest)。
- optional:可扩展的元数据区域,支持自定义 annotations。
- signature:使用私钥对上述 JSON 载荷进行签名的 Base64 编码值。
签名后的镜像在仓库中会多出一个 .sig 标签的镜像层,例如:
registry.example.com/myapp:latest
registry.example.com/myapp:sha256-xxxx.sig # 签名层
元数据注入示例: 在签名时添加构建流水线 ID 和审批人信息,便于事后审计。
cosign sign --key cosign.key \
--annotations "pipeline-id=build-1234" \
--annotations "approver=alice@corp.com" \
registry.example.com/myapp@sha256:abc123
4.3 签名后镜像的不可变性
签名操作本身不修改原始镜像层,而是将签名作为独立对象附加到仓库中。但签名与镜像摘要之间建立了加密绑定:一旦镜像内容发生变化(即使一个字节),镜像摘要就会改变,原有签名立即失效。
这种设计实现了逻辑不可变性:
- 内容不变:签名不修改镜像层数据。
- 绑定不变:签名载荷中记录了镜像的精确摘要。
- 验证不变:任何对镜像的篡改都会导致摘要不匹配,验证失败。
4.4 多架构镜像签名
对于支持多架构(如 amd64、arm64)的镜像,Cosign 提供 --recursive 参数实现批量签名:
cosign sign --key cosign.key --recursive \
registry.example.com/myapp:latest
该命令会:
- 解析 manifest list,获取所有子镜像的 digest。
- 对每个子镜像分别生成签名。
- 为 manifest list 本身也生成一个顶层签名。
注意: 多架构签名时,必须确保所有子镜像的 digest 在签名前已固定。不要在签名过程中同时推送新镜像,否则会导致签名与镜像不一致。
4.5 签名验证与信任边界
签名完成后,使用 cosign verify 验证签名有效性:
cosign verify --key cosign.pub \
registry.example.com/myapp@sha256:abc123
验证通过的条件:
- 签名载荷中的 digest 与当前镜像 digest 一致。
- 签名使用对应的公钥解密成功。
- 签名未过期(如果签名时设置了有效期)。
下表总结了签名前后镜像状态的变化:
| 维度 | 签名前 | 签名后 |
|---|---|---|
| 镜像层内容 | 原始层 | 不变 |
| 仓库中对象 | 镜像层 + 配置 + manifest | 增加 .sig 签名层 |
| 可篡改性 | 可被中间人替换 | 篡改后验证失败 |
| 信任锚点 | 无 | 公钥 / KMS 密钥 |
生产环境建议: 在 CI 流水线中,将签名步骤放在镜像推送之后、部署之前。使用
cosign copy 将签名同步到灾备仓库,确保签名与镜像始终共存。