教程:使用构建治理数据注释容器镜像

注释提供了关于构建过程的有价值的元数据。这些信息用于审计和可追溯性。在安全事件中,详细的来源数据可以显著加快调查和补救过程。

本教程描述了如何设置极狐GitLab流水线来自动化构建、签名和使用 Cosign 注释容器镜像的过程。您可以配置您的 .gitlab-ci.yml 文件来构建、推送和签署 Docker 镜像,并将其推送到极狐GitLab容器仓库。

要注释容器镜像:

  1. 设置镜像和服务镜像
  2. 定义 CI/CD 变量
  3. 准备 OIDC 令牌
  4. 准备容器
  5. 构建和推送镜像
  6. 使用 Cosign 签署镜像
  7. 验证签名和注释

当您将所有步骤结合起来时,您的 .gitlab-ci.yml 应该类似于本教程结尾提供的示例配置

在您开始之前#

您必须具备:

  • 安装了 Cosign v2.0 或更高版本。
  • 对于极狐GitLab私有化部署,极狐GitLab容器仓库配置了元数据库以显示签名。

设置镜像和服务镜像#

.gitlab-ci.yml 文件中,使用 docker:latest 镜像并启用 Docker-in-Docker 服务以允许 Docker 命令在 CI/CD 作业中运行。

yaml
build_and_sign: stage: build image: docker:latest services: - docker:dind # Enable Docker-in-Docker service to allow Docker commands inside the container

定义 CI/CD 变量#

使用极狐GitLab CI/CD 预定义变量定义镜像标签和 URI 的变量。

yaml
variables: IMAGE_TAG: $CI_COMMIT_SHORT_SHA # Use the commit short SHA as the image tag IMAGE_URI: $CI_REGISTRY_IMAGE:$IMAGE_TAG # Construct the full image URI with the registry, project path, and tag COSIGN_YES: "true" # Automatically confirm actions in Cosign without user interaction FF_SCRIPT_SECTIONS: "true" # Enables GitLab's CI script sections for better multi-line script output

准备 OIDC 令牌#

设置一个 OIDC 令牌用于使用 Cosign 的无密钥签名。

yaml
id_tokens: SIGSTORE_ID_TOKEN: aud: sigstore # Provide an OIDC token for keyless signing with Cosign

准备容器#

.gitlab-ci.yml 文件的 before_script 部分:

  • 安装 Cosign 和 jq(用于 JSON 处理):apk add --no-cache cosign jq
  • 启用使用 CI/CD 作业令牌登录极狐GitLab容器仓库:docker login -u "gitlab-ci-token" -p "$CI_JOB_TOKEN" "$CI_REGISTRY"

流水线首先设置必要的环境。

构建和推送镜像#

.gitlab-ci.yml 文件的 script 部分,输入以下命令以构建 Docker 镜像并将其推送到极狐GitLab容器仓库。

yaml
- docker build --pull -t "$IMAGE_URI" . - docker push "$IMAGE_URI"

此命令使用当前目录的 Dockerfile 创建镜像,并将其推送到仓库。

使用 Cosign 签署镜像#

在构建并将镜像推送到极狐GitLab容器仓库后,使用 Cosign 签署它。

.gitlab-ci.yml 文件的 script 部分,输入以下命令:

yaml
1- IMAGE_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE_URI") 2- | 3 cosign sign "$IMAGE_DIGEST" \ 4 --annotations "com.gitlab.ci.user.name=$GITLAB_USER_NAME" \ 5 --annotations "com.gitlab.ci.pipeline.id=$CI_PIPELINE_ID" \ 6 # Additional annotations removed for readability 7 --annotations "tag=$IMAGE_TAG"

此步骤检索镜像摘要。然后使用 Cosign 签署镜像,并添加多个注释。

验证签名和注释#

签署镜像后,验证签名和添加的注释是至关重要的。

.gitlab-ci.yml 文件中,使用 cosign verify 命令包含一个验证步骤:

yaml
1- | 2 cosign verify \ 3 --annotations "tag=$IMAGE_TAG" \ 4 --certificate-identity "$CI_PROJECT_URL//.gitlab-ci.yml@refs/heads/$CI_COMMIT_REF_NAME" \ 5 --certificate-oidc-issuer "$CI_SERVER_URL" \ 6 "$IMAGE_URI" | jq .

验证步骤确保附加到镜像的来源数据是正确的,并且没有被篡改。cosign verify 命令验证签名并检查注释。输出显示您在签名过程中添加到镜像的所有注释。

在输出中,您可以看到之前添加的所有注释,包括:

  • 极狐GitLab用户名
  • 流水线 ID 和 URL
  • 作业 ID 和 URL
  • 提交 SHA 和参考名称
  • 项目路径
  • 镜像来源和修订

通过验证这些注释,您可以确保镜像的来源数据是完整的,并且与您根据构建过程期望的相匹配。

示例 .gitlab-ci.yml 配置#

当您遵循上述所有步骤时,您的 .gitlab-ci.yml 应该类似于:

yaml
1stages: 2 - build 3 4build_and_sign: 5 stage: build 6 image: docker:latest 7 services: 8 - docker:dind # Enable Docker-in-Docker service to allow Docker commands inside the container 9 variables: 10 IMAGE_TAG: $CI_COMMIT_SHORT_SHA # Use the commit short SHA as the image tag 11 IMAGE_URI: $CI_REGISTRY_IMAGE:$IMAGE_TAG # Construct the full image URI with the registry, project path, and tag 12 COSIGN_YES: "true" # Automatically confirm actions in Cosign without user interaction 13 FF_SCRIPT_SECTIONS: "true" # Enables GitLab's CI script sections for better multi-line script output 14 id_tokens: 15 SIGSTORE_ID_TOKEN: 16 aud: sigstore # Provide an OIDC token for keyless signing with Cosign 17 before_script: 18 - apk add --no-cache cosign jq # Install Cosign (mandatory) and jq (optional) 19 - docker login -u "gitlab-ci-token" -p "$CI_JOB_TOKEN" "$CI_REGISTRY" # Log in to the Docker registry using GitLab CI token 20 script: 21 # Build the Docker image using the specified tag and push it to the registry 22 - docker build --pull -t "$IMAGE_URI" . 23 - docker push "$IMAGE_URI" 24 25 # Retrieve the digest of the pushed image to use in the signing step 26 - IMAGE_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE_URI") 27 28 # Sign the image using Cosign with annotations that provide metadata about the build and tag annotation to allow verifying 29 # the tag->digest mapping (https://github.com/sigstore/cosign?tab=readme-ov-file#tag-signing) 30 - | 31 cosign sign "$IMAGE_DIGEST" \ 32 --annotations "com.gitlab.ci.user.name=$GITLAB_USER_NAME" \ 33 --annotations "com.gitlab.ci.pipeline.id=$CI_PIPELINE_ID" \ 34 --annotations "com.gitlab.ci.pipeline.url=$CI_PIPELINE_URL" \ 35 --annotations "com.gitlab.ci.job.id=$CI_JOB_ID" \ 36 --annotations "com.gitlab.ci.job.url=$CI_JOB_URL" \ 37 --annotations "com.gitlab.ci.commit.sha=$CI_COMMIT_SHA" \ 38 --annotations "com.gitlab.ci.commit.ref.name=$CI_COMMIT_REF_NAME" \ 39 --annotations "com.gitlab.ci.project.path=$CI_PROJECT_PATH" \ 40 --annotations "org.opencontainers.image.source=$CI_PROJECT_URL" \ 41 --annotations "org.opencontainers.image.revision=$CI_COMMIT_SHA" \ 42 --annotations "tag=$IMAGE_TAG" 43 44 # Verify the image signature using Cosign to ensure it matches the expected annotations and certificate identity 45 - | 46 cosign verify \ 47 --annotations "tag=$IMAGE_TAG" \ 48 --certificate-identity "$CI_PROJECT_URL//.gitlab-ci.yml@refs/heads/$CI_COMMIT_REF_NAME" \ 49 --certificate-oidc-issuer "$CI_SERVER_URL" \ 50 "$IMAGE_URI" | jq . # Use jq to format the verification output for easier readability