3、镜像签名原理:镜像摘要、签名载荷、OCI制品与签名存储
镜像签名是供应链安全的第一道防线。理解其底层原理,有助于在后续章节中精准设计缓存策略与削峰方案。本节从镜像摘要的不可变性出发,逐步拆解签名载荷结构、OCI 制品的组织方式,以及签名在容器仓库中的实际存储路径。
3.1 镜像摘要:不可变标识的生成逻辑
容器镜像由多层文件系统(Layer)和容器配置(Config)组成。镜像摘要(Digest)是对镜像清单(Manifest)进行哈希运算后得到的唯一标识。只要镜像内容发生任何字节变化,摘要值就会改变,从而提供防篡改保证。
摘要计算遵循 OCI 规范,采用 algorithm:hex 格式,例如 sha256:a1b2c3d4...。当前主流算法为 SHA-256,部分场景支持 SHA-512。
| 组件 | 哈希对象 | 摘要用途 |
|---|---|---|
| 镜像清单(Manifest) | JSON 序列化后的完整内容 | 镜像拉取、验证、引用 |
| 容器配置(Config) | JSON 序列化后的配置内容 | 镜像层索引、运行时元数据 |
| 文件系统层(Layer) | 压缩后的 tar 包内容 | 层缓存、内容寻址 |
3.2 签名载荷:Cosign 的签名数据结构
Cosign 使用私钥对镜像摘要进行签名,生成签名载荷(Signature Payload)。签名载荷不是直接对原始镜像签名,而是对镜像摘要的签名,这样既保证了验证效率,又避免了暴露完整镜像内容。
签名载荷的核心结构包含以下字段:
- critical:包含镜像标识(
image)和签名类型(type: cosign container image signature)。 - optional:可扩展元数据,如签名时间、签名者身份、CI 流水线 ID 等。
- signature:对 critical 和 optional 字段的签名结果,使用 Base64 编码。
{
"critical": {
"identity": {
"docker-reference": "registry.example.com/my-app:latest"
},
"image": {
"docker-manifest-digest": "sha256:a1b2c3d4e5f6..."
},
"type": "cosign container image signature"
},
"optional": {
"creator": "cosign 2.2.0",
"timestamp": 1710000000,
"ci-run-id": "12345"
}
}
3.3 OCI 制品:签名作为独立制品存储
Cosign 没有将签名嵌入镜像层内部,而是利用 OCI 制品的扩展机制,将签名作为一个独立的 OCI 制品(Artifact)存储在容器仓库中。这种设计保持了镜像本身的纯净性,同时允许签名独立更新和分发。
OCI 制品与镜像共享相同的仓库命名空间,但通过不同的 mediaType 进行区分。签名制品的 mediaType 为 application/vnd.dev.cosign.simplesigning.v1+json。
签名制品的引用方式遵循 OCI 引用标签规范(Referrers API),使用 sha256-xxx.sig 格式的标签名,其中 xxx 是镜像摘要的十六进制前缀。
sha256-xxx.sig)存储签名,这可能导致签名与镜像的关联关系依赖命名约定而非仓库原生索引。
3.4 签名存储路径与拉取流程
当执行 cosign verify 时,验证工具需要先定位签名制品,再拉取签名载荷进行验签。签名存储路径决定了拉取延迟的瓶颈位置。
下图展示了签名在 OCI 仓库中的存储结构以及验证时的拉取路径:
3.5 签名验证的完整链路
一次完整的签名验证涉及以下步骤,每一步都可能成为延迟来源:
- 拉取镜像清单:根据镜像标签或摘要获取 Manifest。
- 解析镜像摘要:从 Manifest 中提取
digest字段。 - 查询签名引用:通过 Referrers API 或固定标签名查找签名制品。
- 拉取签名制品:获取签名制品的 Manifest 和签名载荷。
- 解析签名载荷:提取 critical 和 optional 字段。
- 公钥验证:使用预先导入的公钥验证签名值是否匹配镜像摘要。
- 策略评估:检查签名时间、签名者身份等可选条件。
其中步骤 3 和步骤 4 是网络 I/O 密集型操作,在仓库响应延迟较高或签名制品较多时,会成为整体验证延迟的主要贡献者。这正是后续章节设计验签缓存和拉取削峰的核心切入点。