4. Harbor 中的签名机制:架构、存储与镜像关系

Harbor 的镜像签名机制基于 Notary 项目(Docker Content Trust 的后端实现),为镜像提供端到端的完整性验证。签名机制的核心在于:镜像在被推送到 Harbor 时,由客户端(Docker 或 Helm)对镜像摘要进行数字签名,并将签名元数据存储在独立的 Notary 服务中;Harbor 本身不直接存储签名数据,而是通过 Notary 的 API 查询签名状态,并在镜像拉取或部署时进行校验。

4.1 Harbor 签名架构

Harbor 的签名架构采用“镜像仓库 + 签名仓库”分离的设计模式。镜像层数据存储在 Harbor 的 Registry(基于 Distribution)中,而签名元数据存储在 Notary Server 的数据库(通常为 MySQL 或 PostgreSQL)中。两者通过统一的认证网关(Harbor Core)进行关联。

架构要点:
  • Notary Server:负责接收、存储和查询签名数据,提供 TUF(The Update Framework)元数据服务。
  • Notary Signer:负责对签名进行私钥管理和签名操作,通常与 Notary Server 分离部署。
  • Harbor Core:作为统一入口,在镜像推送/拉取时调用 Notary API 检查签名状态。
  • 客户端(Docker/Helm):在推送镜像前使用 docker trust sign 或 helm push 配合 Notary 客户端完成签名。
Harbor 镜像签名架构图 Docker/Helm 客户端 docker trust sign Harbor Core 认证 & 策略引擎 Registry 镜像层存储 Notary Server TUF 元数据 Notary Signer 私钥管理 & 签名 Notary DB 推送/拉取请求 镜像层操作 签名查询 签名请求 元数据持久化 关键设计:签名与镜像分离 签名不存储在 Registry 中

4.2 签名存储位置

签名数据并不存储在 Harbor 的镜像仓库(Registry)中,而是存储在独立的 Notary 数据库中。这种分离设计带来了安全性和可扩展性的优势:即使 Registry 被攻破,攻击者也无法篡改签名数据;同时,签名查询不会影响镜像拉取的性能。

存储组件 存储内容 存储位置 访问方式
Registry 镜像层(Blob)、镜像清单(Manifest) 文件系统 / S3 / GCS 等对象存储 Docker Registry API
Notary Server TUF 元数据(root.json、targets.json、snapshot.json、timestamp.json) Notary 数据库(MySQL/PostgreSQL) Notary API(/v2/_trust/tuf/)
Notary Signer 签名私钥(加密存储) Notary 数据库或 HSM(硬件安全模块) gRPC 内部接口
Harbor Core 签名策略配置、项目级别的签名要求 Harbor 数据库(PostgreSQL) Harbor API
存储路径示例: 在 Notary 数据库中,签名元数据按 docker.io/library/nginx 这样的镜像全名组织。每个镜像仓库对应一组 TUF 元数据文件,其中 targets.json 记录了所有已签名的镜像摘要(digest)及其签名信息。

4.3 签名与镜像的关系

签名与镜像之间是 多对一 的关系:一个镜像可以拥有多个签名(来自不同签名者或不同密钥),但每个签名只对应一个特定的镜像摘要(digest)。签名并不绑定到镜像标签(tag),而是绑定到镜像的 内容哈希,这确保了签名的不可篡改性。

签名与镜像的关系模型 镜像标签 nginx:latest 镜像摘要 (Digest) sha256:abc123... 签名 A 密钥: 开发者甲 签名 B 密钥: CI 系统 签名 C 密钥: 安全审核员 引用 签名 签名 签名 核心关系:标签 → 摘要(1:1)← 签名(1:N) 签名绑定的是摘要而非标签,标签可以移动到新的摘要,但旧签名仍然有效

4.4 签名验证流程

当客户端拉取镜像时,Harbor 的准入控制机制会触发签名验证。验证流程分为以下步骤:

  1. 客户端请求拉取镜像:Docker 客户端向 Harbor Core 发起 GET /v2/<project>/<repo>/manifests/<tag> 请求。
  2. Harbor Core 查询签名策略:检查项目配置是否要求镜像必须签名。
  3. Harbor Core 调用 Notary API:向 Notary Server 查询该镜像摘要对应的签名元数据。
  4. Notary Server 返回签名状态:返回 targets.json 中记录的签名信息,包括签名者、签名时间、使用的密钥 ID。
  5. Harbor Core 执行策略判定:如果项目要求签名但未找到有效签名,则拒绝拉取请求;否则允许拉取。
  6. 客户端验证签名:Docker 客户端使用本地信任根(root.json)验证签名的完整性和可信度。
重要注意事项:
  • 签名验证的 最终责任在客户端:Harbor 的准入控制只能确保镜像在仓库层面有签名,但客户端仍需通过 DOCKER_CONTENT_TRUST=1 环境变量启用本地验证。
  • 签名与标签的 解耦:如果镜像标签被更新(指向新的摘要),旧的签名不会自动迁移到新摘要上,需要重新签名。
  • 多签名者场景:在大型企业中,一个镜像可能需要经过开发、测试、安全三个角色的签名才能部署,Harbor 支持配置“最少签名数量”策略。

4.5 签名元数据内容

Notary 存储的 TUF 元数据是签名机制的核心。以下是一个简化的 targets.json 示例,展示了签名与镜像摘要的绑定关系:

{
  "signed": {
    "_type": "Targets",
    "expires": "2026-01-01T00:00:00Z",
    "targets": {
      "nginx:latest": {
        "hashes": {
          "sha256": "abc123def456..."
        },
        "length": 1234567,
        "custom": {
          "harbor-signature": {
            "signer": "developer-a",
            "timestamp": "2025-03-15T10:30:00Z",
            "key-id": "a1b2c3d4e5f6..."
          }
        }
      }
    },
    "version": 1
  },
  "signatures": [
    {
      "keyid": "a1b2c3d4e5f6...",
      "method": "ecdsa",
      "sig": "3045022100..."
    }
  ]
}
关键字段说明:
  • targets:键为镜像标签,值为对应的摘要哈希和自定义签名信息。
  • custom.harbor-signature:Harbor 扩展字段,记录签名者身份和时间戳。
  • signatures:使用 ECDSA 算法对 targets 元数据本身的签名,确保元数据完整性。