你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

在 GitHub 工作流中使用 Notation 和可信签名对容器镜像进行签名

本文是一系列关于确保容器映像和其他开放容器计划(OCI)项目的完整性和真实性的一部分。 对于完整情况,请从概述开始,该 概述解释了签名为何重要并概述了各种方案。

本文介绍如何创建 GitHub Actions 工作流以执行以下作:

  • 生成映像并将其推送到 Azure 容器注册表。
  • 使用 Notation GitHub 操作和受信任签名对映像进行签名。
  • 在容器注册表中自动存储生成的签名。

先决条件

注释

目前,受信任的签名仅适用于在美国和加拿大拥有三年或以上可验证历史记录的组织。

从 Azure 到 GitHub 进行身份验证

根据 使用 GitHub Actions 连接到 Azure,在运行 Azure CLI 或 Azure PowerShell 命令之前,必须在工作流中使用 Azure 登录操作进行 Azure 身份验证。 Azure 登录作支持多种身份验证方法。

在本指南中,您将使用 OpenID Connect (OIDC) 进行登录,采用用户分配的托管标识,并遵循 使用 OpenID Connect 的 Azure 登录操作中的步骤。

  1. 创建用户分配的托管标识。 如果你有现有的托管标识,请跳过此步骤。

    az login
    az identity create -g <identity-resource-group> -n <identity-name>
    

  1. 获取托管标识的客户端 ID:

    CLIENT_ID=$(az identity show -g <identity-resource-group> -n <identity-name> --query clientId -o tsv)
    

  1. 将角色分配给用于访问 Azure 容器注册表的托管标识。

    对于未启用基于属性的访问控制(ABAC)的注册表,请分配AcrPushAcrPull角色。

    ACR_SCOPE=/subscriptions/<subscription-id>/resourceGroups/<acr-resource-group>
    az role assignment create --assignee $CLIENT_ID --scope $ACR_SCOPE --role "acrpush" --role "acrpull"
    

    对于已启用 ABAC 的注册表,请分配 Container Registry Repository ReaderContainer Registry Repository Writer 角色:

    ACR_SCOPE=/subscriptions/<subscription-id>/resourceGroups/<acr-resource-group>
    az role assignment create --assignee $CLIENT_ID --scope $ACR_SCOPE --role "Container Registry Repository Reader" --role "Container Registry Repository Writer"
    

  1. 为访问受信任签名的托管标识分配 Trusted Signing Certificate Profile Signer 角色:

    TS_SCOPE=/subscriptions/<subscription-id>/resourceGroups/<ts-account-resource-group>/providers/Microsoft.CodeSigning/codeSigningAccounts/<ts-account>/certificateProfiles/<ts-cert-profile>
    az role assignment create --assignee $CLIENT_ID --scope $TS_SCOPE --role "Trusted Signing Certificate Profile Signer"
    

  1. 配置 GitHub 以信任你的身份。 请按照 配置用户分配的托管标识以信任外部标识提供者 的步骤进行操作。

  2. 按照 为存储库创建机密 来创建 GitHub 机密。

    将托管标识值映射到这些机密:

    GitHub 机密 托管标识值
    AZURE_CLIENT_ID 客户 ID
    AZURE_SUBSCRIPTION_ID 订阅编号
    AZURE_TENANT_ID 目录(租户)ID

存储 TSA 根证书

时间戳(RFC 3161)将签名的信任延长至签名证书的有效期之外。 受信任的签名使用生存期较短的证书,因此时间戳至关重要。 可前往 http://timestamp.acs.microsoft.com/ 获取时间戳颁发机构 (TSA) 的服务器 URL,如要了解建议,请参阅时间戳副署

  1. 下载 TSA 根证书:

    curl -o msft-tsa-root-certificate-authority-2020.crt "http://www.microsoft.com/pkiops/certs/microsoft%20identity%20verification%20root%20certificate%20authority%202020.crt"
    

  1. 将根证书存储在存储库中;例如, .github/certs/msft-identity-verification-root-cert-authority-2020.crt. 你将在工作流中使用文件路径。

创建 GitHub Actions 工作流

  1. 在存储库中创建.github/workflows目录(如果不存在)。

  2. 创建新的工作流文件;例如, .github/workflows/sign-with-trusted-signing.yml.

  3. 将以下签名工作流模板复制到文件中。

展开以查看签名工作流模板。
# Build and push an image to Azure Container Registry, set up notation, and sign the image
name: notation-github-actions-sign-with-trusted-signing-template

on:
  push:

env:
  ACR_LOGIN_SERVER: <registry-login-server>             # example: myregistry.azurecr.io
  ACR_REPO_NAME: <repository-name>                      # example: myrepo
  IMAGE_TAG: <image-tag>                                # example: v1
  PLUGIN_NAME: azure-trustedsigning                     # name of Notation Trusted Signing plug-in; do not change
  PLUGIN_DOWNLOAD_URL: <plugin-download-url>            # example: "https://github.com/Azure/trustedsigning-notation-plugin/releases/download/v1.0.0-beta.1/notation-azure-trustedsigning_1.0.0-beta.1_linux_amd64.tar.gz"
  PLUGIN_CHECKSUM: <plugin-package-checksum>            # example: 538b497be0f0b4c6ced99eceb2be16f1c4b8e3d7c451357a52aeeca6751ccb44
  TSA_URL: "http://timestamp.acs.microsoft.com/"        # timestamping server URL
  TSA_ROOT_CERT: <root-cert-file-path>                  # example: .github/certs/msft-identity-verification-root-cert-authority-2020.crt
  TS_ACCOUNT_NAME: <trusted-signing-account-name>       # Trusted Signing account name
  TS_CERT_PROFILE: <trusted-signing-cert-profile-name>  # Trusted Signing certificate profile name 
  TS_ACCOUNT_URI: <trusted-signing-account-uri>         # Trusted Signing account URI; for example, "https://eus.codesigning.azure.net/"

jobs:
  notation-sign:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
     # packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: prepare
        id: prepare
        run: |
          echo "target_artifact_reference=${{ env.ACR_LOGIN_SERVER }}/${{ env.ACR_REPO_NAME }}:${{ env.IMAGE_TAG }}" >> "$GITHUB_ENV"
      
      # Log in to Azure with your service principal secret
      # - name: Azure login
      #  uses: Azure/login@v1
      #  with:
      #    creds: ${{ secrets.AZURE_CREDENTIALS }}
      # If you're using OIDC and federated credentials, make sure to replace the preceding step with the following:
      - name: Azure login
        uses: Azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      # Log in to your container registry
      - name: ACR login
        run: |
            az acr login --name ${{ env.ACR_LOGIN_SERVER }}
      # Build and push an image to the registry
      # Use `Dockerfile` as an example to build an image
      - name: Build and push
        id: push
        uses: docker/build-push-action@v4
        with:
          push: true
          tags: ${{ env.target_artifact_reference }}
          build-args: |
            IMAGE_TAG={{ env.IMAGE_TAG }}
      # Get the manifest digest of the OCI artifact
      - name: Retrieve digest
        run: |
          echo "target_artifact_reference=${{ env.ACR_LOGIN_SERVER }}/${{ env.ACR_REPO_NAME }}@${{ steps.push.outputs.digest }}" >> "$GITHUB_ENV" 
      # Set up the Notation CLI
      - name: setup notation
        uses: notaryproject/notation-action/setup@v1.2.2
      # Sign your container images and OCI artifacts by using a private key stored in Azure Key Vault
      - name: sign OCI artifacts with Trusted Signing
        uses: notaryproject/notation-action/sign@v1
        with:
          timestamp_url: ${{ env.TSA_URL}}
          timestamp_root_cert: ${{env.TSA_ROOT_CERT }}
          plugin_name: ${{ env.PLUGIN_NAME }}
          plugin_url: ${{ env.PLUGIN_DOWNLOAD_URL }}
          plugin_checksum: ${{ env.PLUGIN_CHECKSUM }}
          key_id: ${{ env.TS_CERT_PROFILE }}
          target_artifact_reference: ${{ env.target_artifact_reference }}
          signature_format: cose
          plugin_config: |-
            accountName=${{ env.TS_ACCOUNT_NAME }}
            baseUrl=${{ env.TS_ACCOUNT_URI }}
            certProfile=${{ env.TS_CERT_PROFILE }}
          force_referrers_tag: 'false'

有关环境变量的说明:

  • PLUGIN_NAME:始终使用 azure-trustedsigning
  • PLUGIN_DOWNLOAD_URL:从 受信任签名插件发布页获取 URL。
  • PLUGIN_CHECKSUM:使用发布页上的校验和文件;例如,notation-azure-trustedsigning_<version>_checksums.txt
  • TS_ACCOUNT_URI:使用特定于其区域的受信任签名帐户的终结点;例如, https://eus.codesigning.azure.net/.

触发 GitHub Actions 工作流

语法 on:push 会触发示例工作流。 提交更改将启动工作流。 在 GitHub 存储库名称下,选择 操作 以查看工作流日志。

成功后,工作流将生成映像,将其推送到 Azure 容器注册表,并使用受信任的签名对其进行签名。 可以查看工作流日志,确认 azure-trustedsigning 插件已安装且映像已成功签名。

此外,还可以在 Azure 门户中打开容器注册表。 进入 存储库,进入您的镜像,然后选择 引用者。 确认列出了类型为 application/vnd.cncf.notary.signature 的工件(签名)。