使用自托管 Sigstore 为 GitLab 制品和容器镜像签名

适用版本层级:Free、Premium、Ultimate;适用部署:GitLab Self-Managed。

运行自管理 GitLab 时,不能使用公共 Sigstore 服务为 CI/CD 制品和容器镜像签名,因为该服务只信任 GitLab.com 的流水线。可以将自己的 Sigstore 基础设施——Fulcio、Rekor 和证书透明度日志——接入 GitLab 实例,通过 Cosign 签名和验证制品,无须依赖 GitLab.com 或互联网。

GitLab OpenID Connect(OIDC)提供程序负责证明身份;Cosign 在使用后丢弃签名密钥;Rekor 将每次签名事件记入透明日志。

Fulcio 签发的证书嵌入签名,其中包含建立签名的流水线信息,例如项目路径、提交 SHA、流水线来源、Runner 环境和任务 URL。

前提条件

  • 有权配置 Sigstore 基础设施(Fulcio、Rekor)及 GitLab CI/CD Runner。
  • 网络内运行自托管 Sigstore 栈,包括 Fulcio、Rekor 和证书透明度日志。证书透明度日志不可省略:Cosign 验证时会检查签名证书时间戳;未配置此日志的 Fulcio 签发的证书不能通过 Cosign 验证。部署说明见 Sigstore 透明日志安装指南(Rekor 和 Trillian 后端)及 Fulcio 仓库(Fulcio 和证书透明度日志)。
  • Rekor 使用持久化签名密钥。
  • 如果 GitLab 使用私有证书颁发机构签发的 HTTPS 证书,Fulcio 和 Runner 都必须信任该机构。将 CA 证书挂载到 Fulcio 容器,并用 SSL_CERT_FILE 指向它;同时在每个 Runner 的操作系统信任存储中安装该 CA 证书。
  • Runner 安装 Cosign v2.x 或更新版本。
  • 在 Runner 的共享位置(例如 /etc/sigstore/)放置 Sigstore 栈的信任材料:Fulcio 根 CA 证书 fulcio-root.pem、Rekor 透明日志公钥 rekor-pub.pem、证书透明度日志公钥 ctfe-pub.pem。

让 Fulcio 信任 GitLab 实例

配置 Fulcio,使其在无密钥签名时能验证 GitLab CI/CD 任务的 OIDC 令牌。Fulcio 将令牌声明映射为签名证书字段。配置文件需要同时包含 oidc-issuers 和 ci-issuer-metadata 两个部分。

第一步,获取精确的 OIDC issuer URL。将下面的示例域名替换为 GitLab 实例地址。Fulcio 要求精确匹配,协议以及末尾有无斜杠都影响结果。

   curl --silent "https://gitlab.example.com/.well-known/openid-configuration" | jq --raw-output .issuer

第二步,创建 Fulcio OIDC 配置,为实例添加 oidc-issuers 条目。用上一步的输出替换 <gitlab_issuer_url>:

   oidc-issuers:
     <gitlab_issuer_url>:
       issuer-url: <gitlab_issuer_url>
       client-id: sigstore
       type: ci-provider
       ci-provider: gitlab-pipeline
       contact: admin@example.com
       description: "GitLab Self-Managed OIDC"

第三步,在同一个文件中加入 ci-issuer-metadata,复制 Fulcio 上游配置中的 GitLab 声明模板。使用与上一步相同的 issuer URL。各声明与证书字段的映射见 OIDC 声明到 Fulcio OID 映射表格的 GitLab 列。

   ci-issuer-metadata:
     gitlab-pipeline:
       default-template-values:
         url: "<gitlab_issuer_url>"
         environment: ""
       extension-templates:
         build-signer-uri: "https://{{ .ci_config_ref_uri }}"
         build-signer-digest: "ci_config_sha"
         runner-environment: "runner_environment"
         source-repository-uri: "{{ .url }}/{{ .project_path }}"
         source-repository-digest: "sha"
         source-repository-ref: >-
           refs/{{if eq .ref_type "branch"}}heads/{{ else }}tags/{{end}}{{ .ref }}
         source-repository-identifier: "project_id"
         source-repository-owner-uri: "{{ .url }}/{{ .namespace_path }}"
         source-repository-owner-identifier: "namespace_id"
         build-config-uri: "https://{{ .ci_config_ref_uri }}"
         build-config-digest: "ci_config_sha"
         build-trigger: "pipeline_source"
         run-invocation-uri: >-
           {{ .url }}/{{ .project_path }}/-/jobs/{{ .job_id }}
         source-repository-visibility-at-signing: "project_visibility"
         deployment-environment: "environment"
       subject-alternative-name-template: "https://{{ .ci_config_ref_uri }}"

这部分由 Sigstore 项目维护。应定期检查上游文件的变化,并同步自己的配置。

为制品和容器镜像签名

使用 id_tokens 为任务生成 OIDC 令牌。Cosign 将令牌发送给 Fulcio,由后者签发短期签名证书。

Cosign v3 使用签名配置文件指定 Sigstore 服务端点和信任材料。可以生成一次后分发给 Runner,也可以像下例一样在每次任务中生成。

sign-artifact:
  stage: sign
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
  variables:
    COSIGN_YES: "true"
  script:
    - cosign signing-config create
        --fulcio="url=http://<sigstore-host>:5555,api-version=1,start-time=2024-01-01T00:00:00Z,operator=my-org"
        --rekor="url=http://<sigstore-host>:3000,api-version=1,start-time=2024-01-01T00:00:00Z,operator=my-org"
        --rekor-config="ANY"
        --oidc-provider="url=https://gitlab.example.com,api-version=1,start-time=2024-01-01T00:00:00Z,operator=my-org"
        --out signing-config.json
    - cosign trusted-root create
        --fulcio="url=http://<sigstore-host>:5555,certificate-chain=/etc/sigstore/fulcio-root.pem,start-time=2024-01-01T00:00:00Z"
        --rekor="url=http://<sigstore-host>:3000,public-key=/etc/sigstore/rekor-pub.pem,start-time=2024-01-01T00:00:00Z"
        --ctfe="url=http://<sigstore-host>:6962,public-key=/etc/sigstore/ctfe-pub.pem,start-time=2024-01-01T00:00:00Z"
        --out trusted-root.json
    - cosign sign-blob
        --signing-config=signing-config.json
        --trusted-root=trusted-root.json
        --oidc-client-id=sigstore
        --identity-token=$SIGSTORE_ID_TOKEN
        --bundle=artifact.bundle
        artifact.txt

start-time 表示该服务端点被视为有效的最早时间,其定义见 Sigstore protobuf 规范。实际配置时使用 Sigstore 栈的部署日期。

将 --oidc-client-id 中的 sigstore 替换为 Fulcio OIDC issuer 条目里设置的 client ID。--oidc-provider 必须使用注册在 Fulcio oidc-issuers 配置中的同一个 issuer URL。

为容器镜像签名

使用 cosign sign,将文件路径替换为镜像引用;签名配置相同。

容器镜像仓库必须通过可解析主机名的 HTTPS 地址访问。仓库在自己的外部 URL 上公布认证 realm。Cosign 会拒绝主机为私有或链路本地 IP 字面量的 realm。若 registry_external_url 直接使用 IP 地址,实例可以签署文件,但不能签署容器镜像。

Cosign v2.x 的配置

Cosign v2.x 使用 URL 参数和环境变量,替代 v3 的配置文件:

sign-artifact:
  stage: sign
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
  variables:
    COSIGN_YES: "true"
    SIGSTORE_ROOT_FILE: /etc/sigstore/fulcio-root.pem
    SIGSTORE_REKOR_PUBLIC_KEY: /etc/sigstore/rekor-pub.pem
    SIGSTORE_CT_LOG_PUBLIC_KEY_FILE: /etc/sigstore/ctfe-pub.pem
  script:
    - cosign sign-blob
        --fulcio-url=http://<sigstore-host>:5555
        --rekor-url=http://<sigstore-host>:3000
        --oidc-issuer=https://gitlab.example.com
        --identity-token=$SIGSTORE_ID_TOKEN
        --output-signature=artifact.sig
        --output-certificate=artifact.crt
        artifact.txt

容器镜像签名请使用前文的 Cosign v3 方式。

Cosign v2 写出的证书是经过 base64 编码的 PEM。在交给其他工具前应先解码。--oidc-issuer 也要使用 Fulcio oidc-issuers 中注册的同一个 issuer URL。

验证签名

验证 Cosign v3 bundle 时,使用信任材料及预期的签名者身份:

cosign verify-blob \
  --trusted-root=trusted-root.json \
  --bundle=artifact.bundle \
  --certificate-oidc-issuer=https://gitlab.example.com \
  --certificate-identity=https://gitlab.example.com/my-group/my-project//.gitlab-ci.yml@refs/heads/main \
  artifact.txt

--certificate-identity 指定签名证书中的 Subject Alternative Name,由 CI/CD 配置路径组成:

https://<CI_SERVER_HOST>/<CI_PROJECT_PATH>//.gitlab-ci.yml@<full ref>

这个路径模式有三个关键细节:

  • 协议始终为 https://,因为 Fulcio 的 subject-alternative-name-template 固定如此,即使 GitLab 实例本身通过 HTTP 访问也不例外。
  • .gitlab-ci.yml 前的双斜杠是正确的:前一个路径段是项目,其后的空路径段表示默认 CI/CD 配置位置。
  • 分支流水线的完整引用是 refs/heads/<branch>,标签流水线是 refs/tags/<tag>。

验证 Cosign v2 签署的制品时,传入独立签名和证书:

cosign verify-blob \
  --signature=artifact.sig \
  --certificate=artifact.crt \
  --certificate-oidc-issuer=https://gitlab.example.com \
  --certificate-identity=https://gitlab.example.com/my-group/my-project//.gitlab-ci.yml@refs/heads/main \
  artifact.txt

相关文档

排查问题

自托管 Sigstore 签名可能遇到以下问题。

metadata not found for ci provider gitlab-pipeline

Fulcio 配置缺少 ci-issuer-metadata。加入前文配置 Fulcio章节给出的完整配置块。

ctfe public key not found for payload

Cosign 找不到证书透明度日志的公钥。Cosign v3 的 cosign trusted-root create 命令需包含 --ctfe;Cosign v2 需设置 SIGSTORE_CT_LOG_PUBLIC_KEY_FILE。

not enough verified log entries from transparency log

trusted-root.json 的 Rekor 公钥与运行中的 Rekor 密钥不再一致。内存签名器每次重启都会产生新密钥和空 Merkle 树。因此,重启会让信任材料失效,所有任务的签名都会失败,直到信任材料被重建。

从运行中的实例读取当前公钥,然后重新生成 trusted root:

curl --silent --fail "http://<sigstore-host>:3000/api/v1/log/publicKey" --output /etc/sigstore/rekor-pub.pem
cosign trusted-root create \
  --fulcio="url=http://<sigstore-host>:5555,certificate-chain=/etc/sigstore/fulcio-root.pem,start-time=2024-01-01T00:00:00Z" \
  --rekor="url=http://<sigstore-host>:3000,public-key=/etc/sigstore/rekor-pub.pem,start-time=2024-01-01T00:00:00Z" \
  --ctfe="url=http://<sigstore-host>:6962,public-key=/etc/sigstore/ctfe-pub.pem,start-time=2024-01-01T00:00:00Z" \
  --out trusted-root.json

将新生成的 trusted-root.json 分发给每个 Runner。要避免问题反复出现,应为 Rekor 配置持久化签名密钥和固定树标识符。

failed to verify signed certificate timestamp

Fulcio 未接入证书透明度日志时,证书缺少签名证书时间戳。此时签名本身可以成功,验证会失败。给 Fulcio 配置指向证书透明度日志的 --ct-log-url,并在创建 trusted root 时包含 --ctfe。

x509: certificate signed by unknown authority

Fulcio 无法验证 GitLab 实例的 TLS 证书。将私有 CA 证书挂载到 Fulcio 容器,并用 SSL_CERT_FILE 指向其路径。

即使 OIDC 提供程序初始化失败,Fulcio 的 /healthz 仍可能报告 SERVING。需要查看容器日志,确认提供程序确实已经加载。


来源:GitLab Inc.,Sign artifacts and container images with self-hosted Sigstore。Copyright (c) 2011-present GitLab Inc.。仓库许可证明确规定 doc/ 内容采用 CC BY-SA 4.0;本中文译稿同样按 CC BY-SA 4.0 提供。修改内容为中文翻译及标题组织,9 个原文代码/输出块逐字保留。Fulcio 声明模板的上游来源链接保留在正文。所有示例仅作静态核对,没有执行部署、签名、验证或网络命令。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容