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"
  }
}
实践要点: optional 字段可用于承载缓存失效策略所需的元数据,例如签名时间戳。在后续章节的缓存削峰设计中,时间戳将作为缓存过期判断的依据之一。

3.3 OCI 制品:签名作为独立制品存储

Cosign 没有将签名嵌入镜像层内部,而是利用 OCI 制品的扩展机制,将签名作为一个独立的 OCI 制品(Artifact)存储在容器仓库中。这种设计保持了镜像本身的纯净性,同时允许签名独立更新和分发。

OCI 制品与镜像共享相同的仓库命名空间,但通过不同的 mediaType 进行区分。签名制品的 mediaType 为 application/vnd.dev.cosign.simplesigning.v1+json。

签名制品的引用方式遵循 OCI 引用标签规范(Referrers API),使用 sha256-xxx.sig 格式的标签名,其中 xxx 是镜像摘要的十六进制前缀。

兼容性注意: 并非所有容器仓库都原生支持 OCI 引用标签。对于不支持的仓库,Cosign 会回退到使用固定标签名(如 sha256-xxx.sig)存储签名,这可能导致签名与镜像的关联关系依赖命名约定而非仓库原生索引。

3.4 签名存储路径与拉取流程

当执行 cosign verify 时,验证工具需要先定位签名制品,再拉取签名载荷进行验签。签名存储路径决定了拉取延迟的瓶颈位置。

下图展示了签名在 OCI 仓库中的存储结构以及验证时的拉取路径:

签名存储与拉取路径 签名存储与拉取路径 OCI 容器仓库 镜像清单 mediaType: v1.manifest digest: sha256:abc... 签名制品 mediaType: cosign.sig tag: sha256-abc.sig 签名载荷 critical + optional Base64 签名值 验证客户端 cosign verify 引用标签关联 拉取清单 拉取签名 拉取延迟瓶颈 签名制品需额外 HTTP 请求 仓库引用索引查询耗时

3.5 签名验证的完整链路

一次完整的签名验证涉及以下步骤,每一步都可能成为延迟来源:

  1. 拉取镜像清单:根据镜像标签或摘要获取 Manifest。
  2. 解析镜像摘要:从 Manifest 中提取 digest 字段。
  3. 查询签名引用:通过 Referrers API 或固定标签名查找签名制品。
  4. 拉取签名制品:获取签名制品的 Manifest 和签名载荷。
  5. 解析签名载荷:提取 critical 和 optional 字段。
  6. 公钥验证:使用预先导入的公钥验证签名值是否匹配镜像摘要。
  7. 策略评估:检查签名时间、签名者身份等可选条件。

其中步骤 3 和步骤 4 是网络 I/O 密集型操作,在仓库响应延迟较高或签名制品较多时,会成为整体验证延迟的主要贡献者。这正是后续章节设计验签缓存和拉取削峰的核心切入点。

削峰启示: 签名验证的 7 个步骤中,步骤 3(查询签名引用)和步骤 4(拉取签名制品)是唯一涉及远程仓库网络交互的环节。缓存签名制品的元数据和签名载荷,可以将平均验证延迟从 200-500ms 降低到 1-5ms。