使用自签名证书,通过 Notation 和 Azure Key Vault 对容器映像进行签名

为容器映像签名是一个确保其真实性和完整性的过程。 这是通过将数字签名添加到容器映像来实现的,该签名可在部署期间进行验证。 签名有助于验证映像是否来自受信任的发布者,并且尚未修改。 Notation 是由 Notary Project 社区开发并由 Microsoft 提供技术支持的一个开源供应链安全工具,它支持对容器映像及其他项目进行签名和验证。 Azure Key Vault (AKV) 用于存储证书,其签名密钥可供支持 Notation AKV 插件 (azure-kv) 的 Notation 用来对容器映像及其他项目进行签名和验证。 Azure 容器注册表 (ACR) 允许将签名附加到容器映像和其他项目,以及查看这些签名。

本教程的内容:

  • 安装 Notation CLI 和 AKV 插件
  • 在 AKV 中创建自签名证书
  • 使用 ACR 任务生成并推送容器映像
  • 使用 Notation CLI 和 AKV 插件为容器映像签名
  • 使用 Notation CLI 根据签名验证容器映像
  • 时间戳

先决条件

安装 Notation CLI 和 AKV 插件

  1. 在 Linux amd64 环境中安装 Notation v1.3.0。 按照 Notation 安装指南下载适用于其他环境的包。

    # Download, extract and install
    curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.3.0/notation_1.3.0_linux_amd64.tar.gz
    tar xvzf notation.tar.gz
    
    # Copy the Notation binary to the desired bin directory in your $PATH, for example
    cp ./notation /usr/local/bin
    
  2. 在 Linux amd64 环境中安装 Notation Azure Key Vault 插件 azure-kv v1.2.1。

    备注

    可以在插件的发布页上找到 Notation Azure Key Vault 插件的 URL 和 SHA256 校验值。

    notation plugin install --url https://github.com/Azure/notation-azure-kv/releases/download/v1.2.1/notation-azure-kv_1.2.1_linux_amd64.tar.gz --sha256sum 67c5ccaaf28dd44d2b6572684d84e344a02c2258af1d65ead3910b3156d3eaf5
    
  3. 列出可用的插件,并确认版本为 azure-kv1.2.1 插件是否已包含在列表中。

    notation plugin ls
    

配置环境变量

备注

为便于执行教程中的命令,请提供 Azure 资源的值来匹配现有的 ACR 和 AKV 资源。

  1. 配置 AKV 资源名称。

    AKV_SUB_ID=myAkvSubscriptionId
    AKV_RG=myAkvResourceGroup
    # Name of the existing AKV used to store the signing keys
    AKV_NAME=myakv
    # Name of the certificate created in AKV
    CERT_NAME=wabbit-networks-io
    CERT_SUBJECT="CN=wabbit-networks.io,O=Notation,L=Seattle,ST=WA,C=US"
    CERT_PATH=./${CERT_NAME}.pem
    
  2. 配置 ACR 和映像资源名称。

    ACR_SUB_ID=myAcrSubscriptionId
    ACR_RG=myAcrResourceGroup
    # Name of the existing registry example: myregistry.azurecr.cn
    ACR_NAME=myregistry
    # Existing full domain of the ACR
    REGISTRY=$ACR_NAME.azurecr.cn
    # Container name inside ACR where image will be stored
    REPO=net-monitor
    TAG=v1
    IMAGE=$REGISTRY/${REPO}:$TAG
    # Source code directory containing Dockerfile to build
    IMAGE_SOURCE=https://github.com/wabbit-networks/net-monitor.git#main
    

使用 Azure CLI 登录

az cloud set -n AzureChinaCloud
az login
# az cloud set -n AzureCloud   //means return to Public Azure.

若要详细了解 Azure CLI 及其登录方式,请参阅使用 Azure CLI 登录

对 ACR 和 AKV 的安全访问权限

使用 ACR 和 AKV 时,必须授予适当的权限以确保安全性和受控访问。 可以根据特定方案为不同的实体(例如用户主体、服务主体或托管标识)授予访问权限。 在本教程中,访问权限授予给已登录的 Azure 用户。

授予对 ACR 的访问权限

对于启用了 Microsoft Entra 属性访问控制 (ABAC) 的注册表,在 ACR 中生成和签署容器映像需要 Container Registry Repository ReaderContainer Registry Repository Writer 角色。

对于未启用 ABAC 的注册表,需要 AcrPullAcrPush 角色。

有关 Microsoft Entra ABAC 的详细信息,请参阅 Microsoft Entra 存储库权限

  1. 设置包含 ACR 资源的订阅

    az account set --subscription $ACR_SUB_ID
    
  2. 分配角色。 在角色分配中使用的正确角色取决于注册表是否已启用 ABAC

    USER_ID=$(az ad signed-in-user show --query id -o tsv)
    ROLE1="Container Registry Repository Reader" # For ABAC-enabled registries. Otherwise, use "AcrPull" for non-ABAC-enabled registries.
    ROLE2="Container Registry Repository Writer" # For ABAC-enabled registries. Otherwise, use "AcrPush" for non-ABAC-enabled registries.
    az role assignment create --role "$ROLE1" --role "$ROLE2" --assignee $USER_ID --scope "/subscriptions/$ACR_SUB_ID/resourceGroups/$ACR_RG/providers/Microsoft.ContainerRegistry/registries/$ACR_NAME"
    

授予对 AKV 的访问权限

在本部分中,我们将探讨用于授与 AKV 访问权限的两个选项。

使用自签名证书进行签名需要以下角色:

  • Key Vault Certificates Officer,用于创建和读取证书
  • Key Vault Certificates User,用于读取现有证书
  • Key Vault Crypto User,用于签署操作

若要了解有关使用 Azure RBAC 进行 Key Vault 访问的详细信息,请参阅使用 Azure RBAC 管理访问权限

  1. 设置包含 AKV 资源的订阅

    az account set --subscription $AKV_SUB_ID
    
  2. 分配角色

    USER_ID=$(az ad signed-in-user show --query id -o tsv)
    az role assignment create --role "Key Vault Certificates Officer" --role "Key Vault Crypto User" --assignee $USER_ID --scope "/subscriptions/$AKV_SUB_ID/resourceGroups/$AKV_RG/providers/Microsoft.KeyVault/vaults/$AKV_NAME"
    

在 AKV 中分配访问策略(旧版)

标识需要以下权限:

  • Create 权限,用于创建证书
  • Get 用于读取现有证书的权限
  • 拥有 Sign 权限,可执行签名操作

若要详细了解如何将策略分配给主体,请参阅分配访问策略

  1. 设置包含 AKV 资源的订阅:

    az account set --subscription $AKV_SUB_ID
    
  2. 在 AKV 中设置访问策略:

    USER_ID=$(az ad signed-in-user show --query id -o tsv)
    az keyvault set-policy -n $AKV_NAME --certificate-permissions create get --key-permissions sign --object-id $USER_ID
    

重要

此示例显示了创建证书和对容器映像进行签名所需的最低权限。 根据要求,可能需要授予其他权限。

在 AKV (Azure CLI) 中创建自签名证书

以下步骤演示如何为测试创建自签名证书。

  1. 创建证书策略文件。

    如下所示,执行证书策略文件后,它会创建与 AKV 中的 Notary Project 证书要求兼容的有效证书。 ekus 的值用于代码签名,但表示法不需要使用它来为工件签名。 主题稍后用作用户在验证期间信任的信任标识。

    cat <<EOF > ./my_policy.json
    {
        "issuerParameters": {
        "certificateTransparency": null,
        "name": "Self"
        },
        "keyProperties": {
          "exportable": false,
          "keySize": 2048,
          "keyType": "RSA",
          "reuseKey": true
        },
        "secretProperties": {
          "contentType": "application/x-pem-file"
        },
        "x509CertificateProperties": {
        "ekus": [
            "1.3.6.1.5.5.7.3.3"
        ],
        "keyUsage": [
            "digitalSignature"
        ],
        "subject": "$CERT_SUBJECT",
        "validityInMonths": 12
        }
    }
    EOF
    
  2. 创建证书。

    az keyvault certificate create -n $CERT_NAME --vault-name $AKV_NAME -p @my_policy.json
    

使用 Notation CLI 和 AKV 插件为容器映像签名

  1. 使用个人 Azure 标识向 ACR 进行身份验证。

    az acr login --name $ACR_NAME
    

重要

如果已在系统上安装 Docker 并使用 az acr logindocker login 向 ACR 进行身份验证,则凭据已存储并可用于表示法。 在这种情况下,无需再次运行 notation login 即可向 ACR 进行身份验证。 若要详细了解表示法的身份验证选项,请参阅使用符合 OCI 的注册表进行身份验证

  1. 使用 ACR 任务构建并推送新映像。 始终使用摘要值来标识用于签名的映像,因为标记是可变的,可以覆盖。

    DIGEST=$(az acr build -r $ACR_NAME -t $REGISTRY/${REPO}:$TAG $IMAGE_SOURCE --no-logs --query "outputImages[0].digest" -o tsv)
    IMAGE=$REGISTRY/${REPO}@$DIGEST
    

    在本教程中,如果映像已生成并存储在注册表中,为方便起见,标记将用作该映像的标识符。

    IMAGE=$REGISTRY/${REPO}:$TAG
    
  2. 获取签名密钥的密钥 ID。 AKV 中的证书可以有多个版本,以下命令获取最新版本的密钥 ID。

    KEY_ID=$(az keyvault certificate show -n $CERT_NAME --vault-name $AKV_NAME --query 'kid' -o tsv)
    
  3. 使用签名密钥,以 COSE 签名格式为容器映像签名。 若要使用自签名证书进行签名,需要设置插件配置值 self_signed=true

    notation sign --signature-format cose --id $KEY_ID --plugin azure-kv --plugin-config self_signed=true $IMAGE
    

    若要使用 AKV 进行身份验证,默认将按顺序尝试以下凭据类型(如果已启用):

    如果你要指定凭据类型,请使用名为 credential_type 的附加插件配置。 例如,可以将 credential_type 显式设置为 azurecli 以使用 Azure CLI 凭据,如下所示:

    notation sign --signature-format cose --id $KEY_ID --plugin azure-kv --plugin-config self_signed=true --plugin-config credential_type=azurecli $IMAGE
    

    请参阅下表了解各种凭据类型的 credential_type 值。

    凭据类型 credential_type 的值
    环境认证 environment
    工作负载标识凭据 workloadid
    托管标识凭据 managedid
    Azure CLI 凭据 azurecli
  4. 查看已签名映像和相关签名的图。

    notation ls $IMAGE
    

使用 Notation CLI 为容器映像签名

若要验证容器映像,请将对叶证书进行签名的根证书添加到信任存储区,并创建用于验证的信任策略。 对于本教程中使用的自签名证书,根证书是自签名证书本身。

  1. 下载公共证书。

    az keyvault certificate download --name $CERT_NAME --vault-name $AKV_NAME --file $CERT_PATH
    
  2. 将下载的公共证书添加到命名信任存储,以便进行签名验证。

    STORE_TYPE="ca"
    STORE_NAME="wabbit-networks.io"
    notation cert add --type $STORE_TYPE --store $STORE_NAME $CERT_PATH
    
  3. 列出要确认的证书。

    notation cert ls
    
  4. 在验证之前先配置信任策略。

    信任策略允许用户指定经过微调的验证策略。 以下示例配置名为 wabbit-networks-images 的信任策略,该策略适用于 $REGISTRY/$REPO 中的所有项目,并使用 $STORE_NAME 类型的具名信任存储 $STORE_TYPE。 它还假设用户信任特定身份,其 X.509 主题为 $CERT_SUBJECT。 有关详细信息,请参阅信任存储和信任策略规范

    cat <<EOF > ./trustpolicy.json
    {
        "version": "1.0",
        "trustPolicies": [
            {
                "name": "wabbit-networks-images",
                "registryScopes": [ "$REGISTRY/$REPO" ],
                "signatureVerification": {
                    "level" : "strict" 
                },
                "trustStores": [ "$STORE_TYPE:$STORE_NAME" ],
                "trustedIdentities": [
                    "x509.subject: $CERT_SUBJECT"
                ]
            }
        ]
    }
    EOF
    
  5. 使用 notation policy 从我们之前创建的 JSON 文件导入信任策略配置。

    notation policy import ./trustpolicy.json
    notation policy show
    
  6. 使用 notation verify 验证容器映像自生成时以来未发生更改。

    notation verify $IMAGE
    

    使用信任策略成功验证映像后,将在成功输出消息中返回已验证映像的 sha256 摘要。

时间戳

自 Notation v1.2.0 发布以来,Notation 支持符合 RFC 3161 的时间戳。 此增强功能通过信任时间戳颁发机构 (TSA) 来延长对证书有效期内创建的签名的信任,即使在证书过期后也能成功进行签名验证。 作为映像签名者,应确保使用受信任的 TSA 生成的时间戳对容器映像进行签名。 作为映像验证者,若要验证时间戳,应确保信任映像签名者和关联的 TSA,并通过信任存储和信任策略来建立信任。 通过避免因证书过期需要定期重新签名映像,使用时间戳会降低成本,这在使用短期证书时特别关键。 有关如何使用时间戳进行签名和验证的详细说明,请参阅 公证项目时间戳指南

后续步骤

表示法在 Azure Pipelines 和 GitHub Actions 上提供 CI/CD 解决方案:

为了确保仅在 Azure Kubernetes 服务(AKS)上部署受信任的容器映像: