使用 Sigstore 进行无密钥签名和验证

Sigstore 项目提供了一个名为 Cosign 的 CLI,可用于使用极狐GitLab CI/CD 构建的容器镜像的无密钥签名。无密钥签名有很多优点,包括无需管理、保护和轮换私钥。Cosign 请求一个短期密钥对用于签名,将其记录在证书透明度日志中,然后将其丢弃。密钥是通过使用运行流水线的用户的 OIDC 身份从极狐GitLab 服务器获取的令牌生成的。该令牌包含证明该令牌是由 CI/CD 流水线生成的独特声明。要了解更多信息,请参阅有关无密钥签名的 Cosign 文档

有关极狐GitLab OIDC 声明和 Fulcio 证书扩展之间映射的详细信息,请参阅将 OIDC 令牌声明映射到 Fulcio OID 的极狐GitLab 专栏。

要求

  • 您必须使用 JihuLab.com。
  • 您项目的 CI/CD 配置必须位于项目中。

使用 Cosign 对容器镜像和构建产物进行签名和验证

您可以使用 Cosign 对容器镜像和构建产物进行签名和验证。

先决条件:

  • 您必须使用版本为 >= 2.0.1 的 Cosign。

限制

  • The id_tokens portion of the CI/CD configuration file must be located in the project that is being built and signed. AutoDevOps, CI files included from another repository, and child pipelines are not supported. Work to remove this limitation is being tracked in issue 411317.
  • CI/CD 配置文件中的 id_tokens 部分必须位于正在构建和签名的项目中。AutoDevOps、从另一个仓库包含的 CI 文件和子流水线不受支持。

最佳实践:

  • 在构建和签名容器镜像时,在同一作业中构建和签名镜像,以防止镜像在签名之前被篡改。
  • 在签名容器镜像时,签名摘要(不可变)而不是标签。

极狐GitLab ID 令牌可以由 Cosign 用于无密钥签名。令牌必须将 sigstore 设置为 aud 声明。当在 SIGSTORE_ID_TOKEN 环境变量中设置该令牌时,Cosign 可以自动使用该令牌。

要学习关于安装 Cosign 的更多详情,请参阅 Cosign 安装文档

签名

容器镜像

Cosign.gitlab-ci.yml 模版可用于在极狐GitLab CI 中构建和签名容器镜像。签名会自动存储在与镜像相同的容器仓库中。

include:
- template: Cosign.gitlab-ci.yml

要学习关于签名容器的更多详情,请参阅 Cosign 签名容器文档

构建产物

如下示例演示如何在极狐GitLab CI 中对构建产物进行签名。您应该保存 cosign.sign 生成的 cosign.bundle 文件,用于签名验证。

要学习关于签名产物的更多详情,请参阅 Cosign 签名 Blobs 文档

build_and_sign_artifact:
  stage: build
  image: alpine:latest
  variables:
    COSIGN_YES: "true"
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
  before_script:
    - apk add --update cosign
  script:
    - echo "This is a build artifact" > artifact.txt
    - cosign sign-blob artifact.txt --bundle cosign.bundle
  artifacts:
    paths:
      - artifact.txt
      - cosign.bundle

验证

命令行参数

名称
--certificate-identity Fulcio 颁发的签名证书的 SAN。可以使用镜像/制品签名所在项目的以下信息进行构建:极狐GitLab 实例 URL + 项目路径 + // + CI 配置文件路径 + @ + 引用路径。
--certificate-oidc-issuer 镜像/制品进行签名时所使用的极狐GitLab 实例的 URL。比如,https://gitlab.com
--bundle cosign sign-blob 生成的 bundle文件。仅用于验证构建制品。

要学习关于验证签名镜像/产物的更多详情,请查阅 Cosign 验证文档

容器镜像

如下示例演示如何在极狐GitLab CI 中验证已签名的容器镜像。命令行参数在上方进行了描述。

verify_image:
  image: alpine:3.20
  stage: verify
  before_script:
    - apk add --update cosign docker
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
  script:
    - cosign verify "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" --certificate-identity "https://gitlab.com/my-group/my-project//path/to/.gitlab-ci.yml@refs/heads/main" --certificate-oidc-issuer "https://gitlab.com"

额外信息:

  • 项目路径和 .gitlab-ci.yml 路径之间的双反斜杠不是错误,而是验证成功所必需的。如果使用单斜杠,则通常会出现以下错误:Error: none of the expected identities matched what was in the certificate, got subjects,后跟已签名的 URL,该 URL 在项目路径和 .gitlab-ci.yml 路径之间有两个斜杠。
  • 如果验证发生在与签名相同的流水线中,则可以使用此路径:"${CI_PROJECT_URL}//.gitlab-ci.yml@refs/heads/${CI_COMMIT_REF_NAME}"

构建产物

以下示例演示了如何在极狐GitLab CI 中验证已签名的构建制品。验证构建制品需要构建制品本身和 cosign sign-blob 生成的 cosign.bundle 文件。命令行参数在上方进行了描述。

verify_artifact:
  stage: verify
  image: alpine:latest
  before_script:
    - apk add --update cosign
  script:
    - cosign verify-blob artifact.txt --bundle cosign.bundle --certificate-identity "https://gitlab.com/my-group/my-project//path/to/.gitlab-ci.yml@refs/heads/main" --certificate-oidc-issuer "https://gitlab.com"

额外信息:

  • 如果项目路径和 .gitlab-ci.yml 路径中的双反斜杠不是错误,而是验证成功所必需的。如果使用单斜杠,则通常会出现以下错误:Error: none of the expected identities matched what was in the certificate, got subjects,后跟已签名的 URL,该 URL 在项目路径和 .gitlab-ci.yml 路径之间有两个斜杠。
  • 如果验证发生在与签名相同的流水线中,则可以使用此路径:"${CI_PROJECT_URL}//.gitlab-ci.yml@refs/heads/${CI_COMMIT_REF_NAME}"

使用 Sigstore 和 npm 生成无密钥来源

您可以使用 Sigstore 和 npm 以及极狐GitLab CI/CD 对构建产物进行数字签名,而无需花费密钥管理的开销。

关于 npm 来源

npm CLI 允许软件包维护者向用户提供来源证明。使用 npm CLI 来源生成允许用户信任并验证他们正在下载和使用的包是否来自您和构建它的构建系统。

有关如何发布 npm 包的更多信息,请参阅极狐GitLab npm 软件包库

Sigstore

Sigstore 是一组工具,软件包管理器和安全专家可以使用它们来保护其软件供应链免受攻击。它汇集了 Fulcio、Cosign 和 Rekor 等免费使用的开源技术,可以处理数字签名、验证和来源检查,从而使开源软件的分发和使用更加安全。

相关主题

在极狐GitLab CI/CD 中生成来源

现在,Sigstore 支持如上所述的极狐GitLab OIDC,您可以将 npm 来源与极狐GitLab CI/CD 和 Sigstore 一起使用,为极狐GitLab CI/CD 流水线中的 npm 包生成并签署来源。

先决条件

  1. 将您的极狐GitLab ID 令牌 aud 设置为 sigstore
  2. 添加 --provenance 标志以让 npm 发布。

要添加到 .gitlab-ci.yml 文件的示例内容:

image: node:latest

build:
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
  script:
    - npm publish --provenance --access public

npm 极狐GitLab 模板也提供了此功能。

验证 npm 来源

npm CLI 还为最终用户提供验证包来源的功能。

npm audit signatures
audited 1 package in 0s
1 package has a verified registry signature

检查来源元数据

Rekor 透明度日志存储每个带有来源的发布的包的证书和证明。例如以下示例的条目

由 npm 生成的来源文档示例:

_type: https://in-toto.io/Statement/v0.1
subject:
  - name: pkg:npm/%40strongjz/strongcoin@0.0.13
    digest:
      sha512: >-
        924a134a0fd4fe6a7c87b4687bf0ac898b9153218ce9ad75798cc27ab2cddbeff77541f3847049bd5e3dfd74cea0a83754e7686852f34b185c3621d3932bc3c8
predicateType: https://slsa.dev/provenance/v0.2
predicate:
  buildType: https://github.com/npm/CLI/gitlab/v0alpha1
  builder:
    id: https://gitlab.com/strongjz/npm-provenance-example/-/runners/12270835
  invocation:
    configSource:
      uri: git+https://gitlab.com/strongjz/npm-provenance-example
      digest:
        sha1: 6e02e901e936bfac3d4691984dff8c505410cbc3
      entryPoint: deploy
    parameters:
      CI: 'true'
      CI_API_GRAPHQL_URL: https://gitlab.com/api/graphql
      CI_API_V4_URL: https://gitlab.com/api/v4
      CI_COMMIT_BEFORE_SHA: 7d3e913e5375f68700e0c34aa90b0be7843edf6c
      CI_COMMIT_BRANCH: main
      CI_COMMIT_REF_NAME: main
      CI_COMMIT_REF_PROTECTED: 'true'
      CI_COMMIT_REF_SLUG: main
      CI_COMMIT_SHA: 6e02e901e936bfac3d4691984dff8c505410cbc3
      CI_COMMIT_SHORT_SHA: 6e02e901
      CI_COMMIT_TIMESTAMP: '2023-05-19T10:17:12-04:00'
      CI_COMMIT_TITLE: trying to publish to gitlab reg
      CI_CONFIG_PATH: .gitlab-ci.yml
      CI_DEFAULT_BRANCH: main
      CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX: gitlab.com:443/strongjz/dependency_proxy/containers
      CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX: gitlab.com:443/strongjz/dependency_proxy/containers
      CI_DEPENDENCY_PROXY_SERVER: gitlab.com:443
      CI_DEPENDENCY_PROXY_USER: gitlab-ci-token
      CI_JOB_ID: '4316132595'
      CI_JOB_NAME: deploy
      CI_JOB_NAME_SLUG: deploy
      CI_JOB_STAGE: deploy
      CI_JOB_STARTED_AT: '2023-05-19T14:17:23Z'
      CI_JOB_URL: https://gitlab.com/strongjz/npm-provenance-example/-/jobs/4316132595
      CI_NODE_TOTAL: '1'
      CI_PAGES_DOMAIN: gitlab.io
      CI_PAGES_URL: https://strongjz.gitlab.io/npm-provenance-example
      CI_PIPELINE_CREATED_AT: '2023-05-19T14:17:21Z'
      CI_PIPELINE_ID: '872773336'
      CI_PIPELINE_IID: '40'
      CI_PIPELINE_SOURCE: push
      CI_PIPELINE_URL: https://gitlab.com/strongjz/npm-provenance-example/-/pipelines/872773336
      CI_PROJECT_CLASSIFICATION_LABEL: ''
      CI_PROJECT_DESCRIPTION: ''
      CI_PROJECT_ID: '45821955'
      CI_PROJECT_NAME: npm-provenance-example
      CI_PROJECT_NAMESPACE: strongjz
      CI_PROJECT_NAMESPACE_ID: '36018'
      CI_PROJECT_PATH: strongjz/npm-provenance-example
      CI_PROJECT_PATH_SLUG: strongjz-npm-provenance-example
      CI_PROJECT_REPOSITORY_LANGUAGES: javascript,dockerfile
      CI_PROJECT_ROOT_NAMESPACE: strongjz
      CI_PROJECT_TITLE: npm-provenance-example
      CI_PROJECT_URL: https://gitlab.com/strongjz/npm-provenance-example
      CI_PROJECT_VISIBILITY: public
      CI_REGISTRY: registry.gitlab.com
      CI_REGISTRY_IMAGE: registry.gitlab.com/strongjz/npm-provenance-example
      CI_REGISTRY_USER: gitlab-ci-token
      CI_RUNNER_DESCRIPTION: 3-blue.shared.runners-manager.gitlab.com/default
      CI_RUNNER_ID: '12270835'
      CI_RUNNER_TAGS: >-
        ["gce", "east-c", "linux", "ruby", "mysql", "postgres", "mongo",
        "git-annex", "shared", "docker", "saas-linux-small-amd64"]
      CI_SERVER_HOST: gitlab.com
      CI_SERVER_NAME: GitLab
      CI_SERVER_PORT: '443'
      CI_SERVER_PROTOCOL: https
      CI_SERVER_REVISION: 9d4873fd3c5
      CI_SERVER_SHELL_SSH_HOST: gitlab.com
      CI_SERVER_SHELL_SSH_PORT: '22'
      CI_SERVER_URL: https://gitlab.com
      CI_SERVER_VERSION: 16.1.0-pre
      CI_SERVER_VERSION_MAJOR: '16'
      CI_SERVER_VERSION_MINOR: '1'
      CI_SERVER_VERSION_PATCH: '0'
      CI_TEMPLATE_REGISTRY_HOST: registry.gitlab.com
      GITLAB_CI: 'true'
      GITLAB_FEATURES: >-
        elastic_search,ldap_group_sync,multiple_ldap_servers,seat_link,usage_quotas,zoekt_code_search,repository_size_limit,admin_audit_log,auditor_user,custom_file_templates,custom_project_templates,db_load_balancing,default_branch_protection_restriction_in_groups,extended_audit_events,external_authorization_service_api_management,geo,instance_level_scim,ldap_group_sync_filter,object_storage,pages_size_limit,project_aliases,password_complexity,enterprise_templates,git_abuse_rate_limit,required_ci_templates,runner_maintenance_note,runner_performance_insights,runner_upgrade_management,runner_jobs_statistics
      GITLAB_USER_ID: '31705'
      GITLAB_USER_LOGIN: strongjz
    environment:
      name: 3-blue.shared.runners-manager.gitlab.com/default
      architecture: linux/amd64
      server: https://gitlab.com
      project: strongjz/npm-provenance-example
      job:
        id: '4316132595'
      pipeline:
        id: '872773336'
        ref: .gitlab-ci.yml
  metadata:
    buildInvocationId: https://gitlab.com/strongjz/npm-provenance-example/-/jobs/4316132595
    completeness:
      parameters: true
      environment: true
      materials: false
    reproducible: false
  materials:
    - uri: git+https://gitlab.com/strongjz/npm-provenance-example
      digest:
        sha1: 6e02e901e936bfac3d4691984dff8c505410cbc3