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

使用 Ratify 和 Azure Policy 验证容器映像签名

容器安全性在云原生环境中至关重要,可帮助保护工作负荷。 为了在整个容器映像生命周期内增强安全性,Microsoft引入了 容器安全供应链(CSSC)框架。 在框架的“部署”阶段,容器映像将部署到生产环境,例如 Azure Kubernetes 服务(AKS)群集。

确保安全生产环境涉及维护容器映像的完整性和真实性。 在生成阶段对容器映像进行签名,然后在“部署”阶段验证它们有助于确保仅部署受信任的和未更改的映像。

RatifyCloud Native Computing Foundation (CNCF) 支持的 Microsoft 沙盒项目。 它是一个可靠的验证引擎,用于验证容器映像(例如签名)的安全元数据。 它仅允许部署满足指定策略的映像。

Scenarios

本文介绍在 AKS 上使用 Ratify 实现容器镜像签名验证的两个主要场景。 这些方案因如何管理证书进行签名和验证而有所不同:使用 Azure Key Vault 进行传统证书管理,或使用 Microsoft 受信任的签名服务进行零接触证书生命周期管理。 选择符合当前证书管理方法和安全要求的方案。

使用 Key Vault 进行证书管理

映像生成者在持续集成和持续交付(CI/CD)管道中生成容器映像并将其推送到 Azure 容器注册表。 这些映像适用于映像使用者在 AKS 群集上部署和运行云原生工作负荷。

映像生成者使用 Notary Project 工具(尤其是 Notation),在 CI/CD 管道中对容器注册表中的容器映像进行签名。 用于签名的密钥和证书安全地存储在 Key Vault 中。

Notary Project 签名在容器注册表中创建并存储,在那里引用相关的映像。 映像使用者在 AKS 群集上设置 Ratify 以及策略,以在部署期间验证映像的 Notary 项目签名。 如果策略效果设置为 Deny,则无法通过签名验证的映像将被拒绝进行部署。 此配置有助于确保仅将受信任的和未更改的映像部署到 AKS 群集。

作为映像生成者,可以使用 Key Vault 按照以下文章在容器注册表中对容器映像进行签名:

使用受信任的签名进行证书管理

在此方案中,映像生成者使用受信任的签名而不是 Key Vault 管理的证书对容器注册表中的容器映像进行签名。 由于受信任的签名提供零接触证书生命周期管理,因此生成者不再需要处理证书颁发、轮换或过期。

在使用者端,受信任的签名生成生存期较短的证书。 映像使用者在验证期间配置时间戳,以在证书过期后保持信任。

此外,在 AKS 上配置批准策略和群集策略,以在部署时验证签名。 如果策略效果设置为 Deny,则阻止验证失败的任何映像。 阻止有助于确保仅部署受信任的和未更改的映像。

作为镜像生产者,您可以参考以下文章,使用 Trusted Signing 对容器镜像进行签名:

本文将指导您作为映像消费者,通过在 AKS 群集上使用 Ratify 和 Azure Policy 验证容器映像签名的过程。

重要

如果更喜欢使用托管体验而不是直接使用开源批准,则可以改为选择 AKS 映像完整性策略(预览版), 以帮助确保 AKS 群集上的映像完整性。

签名验证概述

下面是签名验证的高级步骤:

  1. 为容器注册表设置标识和访问控制:配置 Ratify 用于访问具有必要角色的容器注册表的身份。

  2. 为 Key Vault 设置标识和访问控制:配置 Ratify 使用的标识,以访问 Key Vault 并具有必要的角色。 如果镜像经过受信任的签名验证,请跳过此步骤。

  3. 在你的 AKS 群集上安装 Ratify:通过 Helm chart 将 Ratify 安装为标准的 Kubernetes 服务。

  4. 设置自定义 Azure 策略:创建并分配具有所需策略效果的自定义 Azure 策略: DenyAudit

执行以下步骤后,可以开始部署工作负荷来观察结果:

  • 通过 Deny 策略生效后,仅允许通过签名验证的镜像进行部署。 未签名或由不受信任的标识签名的镜像被拒绝。
  • 借助Audit策略效果,可以部署映像,但您的组件已被标记为不符合审核要求。

先决条件

设置标识和访问控制

在 AKS 群集上安装批准之前,需要建立适当的标识和访问控制。 Ratify需要访问容器注册表以提取容器镜像和签名。 使用 Key Vault 进行证书管理时,Ratify 还需要访问 Key Vault 以检索证书来进行签名验证。

标识配置涉及:

  • 创建用户分配的托管标识或使用现有标识。
  • 设置联合标识凭据以启用工作负荷标识身份验证。
  • 为容器注册表访问权限授予适当的角色分配。
  • 如果使用 Key Vault 进行证书管理,请配置 Key Vault 访问权限。

创建或使用用户分配的托管标识

如果还没有用户分配的托管标识,请参阅 创建用户分配的托管标识 以创建一个托管标识。 Ratify 使用此身份来访问 Azure 资源,例如容器注册表和(适用时)用于证书管理的密钥保管库。

为您的身份创建联合身份凭据

使用以下代码设置环境变量。 如果不使用默认值,请更新变量 RATIFY_NAMESPACERATIFY_SA_NAME 的值。 请务必在安装 Ratify Helm chart 时确保使用相同的值。

export AKS_RG=<aks-resource-group-name>
export AKS_NAME=<aks-name>
export AKS_OIDC_ISSUER=$(az aks show -n $AKS_NAME -g $AKS_RG --query "oidcIssuerProfile.issuerUrl" -otsv)

export IDENTITY_RG=<identity-resource-group-name>
export IDENTITY_NAME=<identity-name>
export IDENTITY_CLIENT_ID=$(az identity show --name  $IDENTITY_NAME --resource-group $IDENTITY_RG --query 'clientId' -o tsv)
export IDENTITY_OBJECT_ID=$(az identity show --name $IDENTITY_NAME --resource-group $IDENTITY_RG --query 'principalId' -otsv)

export RATIFY_NAMESPACE="gatekeeper-system"
export RATIFY_SA_NAME="ratify-admin"

以下命令为您的托管标识创建一个联合凭据。 该凭据允许托管标识使用 OIDC 证书颁发者颁发的令牌进行身份验证,特别是针对命名空间 RATIFY_NAMESPACE 中的 Kubernetes 服务帐户 RATIFY_SA_NAME

az identity federated-credential create \
--name ratify-federated-credential \
--identity-name "$IDENTITY_NAME" \
--resource-group "$IDENTITY_RG" \
--issuer "$AKS_OIDC_ISSUER" \
--subject system:serviceaccount:"$RATIFY_NAMESPACE":"$RATIFY_SA_NAME"

配置对容器注册表的访问

标识需要具备 AcrPull 角色,才能拉取容器映像的签名和其他元数据。 使用以下代码分配角色:

export ACR_SUB=<acr-subscription-id>
export ACR_RG=<acr-resource-group>
export ACR_NAME=<acr-name>

az role assignment create \
--role acrpull \
--assignee-object-id ${IDENTITY_OBJECT_ID} \
--scope subscriptions/${ACR_SUB}/resourceGroups/${ACR_RG}/providers/Microsoft.ContainerRegistry/registries/${ACR_NAME}

配置对 Key Vault 的访问权限

如果使用受信任的签名进行证书管理,请跳过此步骤。

标识需要具备 Key Vault Secrets User 角色,才能从密钥保管库中提取整个证书链。 使用以下代码分配角色:

export AKV_SUB=<acr-subscription-id>
export AKV_RG=<acr-resource-group>
export AKV_NAME=<acr-name>

az role assignment create \
--role "Key Vault Secrets User" \
--assignee ${IDENTITY_OBJECT_ID} \
--scope "/subscriptions/${AKV_SUB}/resourceGroups/${AKV_RG}/providers/Microsoft.KeyVault/vaults/${AKV_NAME}"

在启用了 Azure Policy 的 AKS 集群上部署 Ratify

正确配置身份和访问控制后,现在可以在 AKS 群集上安装 Ratify。 Ratify工具与 Azure Policy 集成,以强制实施签名验证策略。 安装过程涉及使用 Helm 图表来部署 Ratify,其中包含特定的配置参数,用于定义它如何验证容器镜像签名。

以下部分介绍批准设置的两个关键方面:

  • 了解证书管理方法(密钥保管库或受信任签名)所需的 Helm 图表参数
  • 使用适当的配置安装Ratify以启用签名验证功能

配置参数因是使用 Key Vault 还是受信任的签名进行证书管理而有所不同。 请务必按照与所选方案匹配的说明进行操作。

了解 Helm 图表参数

在为 Ratify 安装 Helm 图表时,需要使用 --set 标志或者提供自定义的值文件将值传递给参数。 用于配置 Ratify 以进行签名验证的值。 有关参数的综合列表,请参阅Ratify Helm 图表文档

您需要进行配置:

  • 之前为访问容器注册表和密钥库而配置的标识。
  • 存储在 Key Vault 中用于签名验证的证书。
  • 用于签名验证的公证项目信任策略,包括registryScopestrustStorestrustedIdentities

此表提供有关参数的详细信息:

参数 Description 价值
azureWorkloadIdentity.clientId Azure 工作负荷标识的客户端 ID "$IDENTITY_CLIENT_ID"
oras.authProviders.azureWorkloadIdentityEnabled 用于容器注册表身份验证的 Azure 工作负荷标识(启用或禁用) true
azurekeyvault.enabled 从 Key Vault 提取证书(启用或禁用) true
azurekeyvault.vaultURI Key Vault 资源的 URI "https://$AKV_NAME.vault.azure.net"
azurekeyvault.tenantId Key Vault 资源的租户 ID "$AKV_TENANT_ID"
azurekeyvault.certificates[0].name 证书的名称 "$CERT_NAME"
notation.trustPolicies[0].registryScopes[0] 策略应用到的存储库 URI "$REPO_URI"
notation.trustPolicies[0].trustStores[0] 信任存储用于存放类型为catsa的证书 ca:azurekeyvault
notation.trustPolicies[0].trustedIdentities[0] 签名证书的主题字段,前缀 x509.subject: 表示您所信任的内容 "x509.subject: $SUBJECT"

通过对图像进行时间戳记,可以确保在证书过期前签名的图像仍然可以成功验证。 为时间戳颁发机构(TSA)配置添加以下参数:

参数 Description 价值
notationCerts[0] PEM 格式的 TSA 根证书文件的文件路径 "$TSA_ROOT_CERT_FILEPATH"
notation.trustPolicies[0].trustStores[1] 存储 TSA 根证书的另一个信任存储区 tsa:notationCerts[0]

如果有多个证书用于签名验证,请指定额外的参数:

参数 Description 价值
azurekeyvault.certificates[1].name 证书的名称 "$CERT_NAME_2"
notation.trustPolicies[0].trustedIdentities[1] 签名证书的另一个主题字段,指示您信任的内容 "x509.subject: $SUBJECT_2"

安装具有所需参数和值的 Ratify Helm chart

确保 Ratify Helm 图表的版本至少为 1.15.0,这将安装 Ratify 版本 1.4.0 或更高版本。 以下示例使用 Helm 图表版本 1.15.0

设置用于安装的其他环境变量:

export CHART_VER="1.15.0"
export REPO_URI="$ACR_NAME.azurecr.io/<namespace>/<repo>"
export SUBJECT="<Subject-of-signing-certificate>"
export AKV_TENANT_ID="$(az account show --query tenantId --output tsv)"

helm repo add ratify https://notaryproject.github.io/ratify
helm repo update

helm install ratify ratify/ratify --atomic --namespace $RATIFY_NAMESPACE --create-namespace --version $CHART_VER --set provider.enableMutation=false --set featureFlags.RATIFY_CERT_ROTATION=true \
--set azureWorkloadIdentity.clientId=$IDENTITY_CLIENT_ID \
--set oras.authProviders.azureWorkloadIdentityEnabled=true \
--set azurekeyvault.enabled=true \
--set azurekeyvault.vaultURI="https://$AKV_NAME.vault.azure.net" \
--set azurekeyvault.certificates[0].name="$CERT_NAME" \
--set azurekeyvault.tenantId="$AKV_TENANT_ID" \  
--set notation.trustPolicies[0].registryScopes[0]="$REPO_URI" \
--set notation.trustPolicies[0].trustStores[0]="ca:azurekeyvault" \
--set notation.trustPolicies[0].trustedIdentities[0]="x509.subject: $SUBJECT"

对于时间戳支持,需要指定其他参数: --set-file notationCerts[0]="$TSA_ROOT_CERT_FILE"--set notation.trustPolicies[0].trustStores[1]="ca:azurekeyvault"

重要

对于未链接到信任策略的图像,签名验证失败。 例如,如果映像不在存储库 $REPO_URI中,则这些映像的签名验证会失败。 可以通过指定其他参数来添加多个存储库。 例如,若要为信任策略 notation.trustPolicies[0]添加另一个存储库,请包含参数 --set notation.trustPolicies[0].registryScopes[1]="$REPO_URI_1"

设置自定义 Azure 策略

在 AKS 群集上成功安装和配置批准后,最后一步是创建和分配一个 Azure 策略,该策略将在容器部署期间强制实施签名验证。 此策略充当强制机制,指示群集在允许部署之前使用批准来验证容器映像签名。

Azure Policy 提供两种强制模式:

  • Deny:阻止对签名验证失败的映像进行部署,以便仅在群集中运行受信任的映像。
  • Audit:允许所有部署,但标记合规的资源用于监控和报告。

在初始设置或测试阶段,效果 Audit 非常有用。 你可以使用它来验证配置,而不会在生产环境中造成服务中断(由于设置不正确)。

将新策略分配给 AKS 群集

创建自定义 Azure 策略进行签名验证:

export CUSTOM_POLICY=$(curl -L https://raw.githubusercontent.com/notaryproject/ratify/refs/tags/v1.4.0/library/default/customazurepolicy.json)
export DEFINITION_NAME="ratify-default-custom-policy"
export DEFINITION_ID=$(az policy definition create --name "$DEFINITION_NAME" --rules "$(echo "$CUSTOM_POLICY" | jq .policyRule)" --params "$(echo "$CUSTOM_POLICY" | jq .parameters)" --mode "Microsoft.Kubernetes.Data" --query id -o tsv)

默认情况下,策略效果设置为 Deny。 通过此策略效果,无法进行签名验证的映像被拒绝部署。

或者,可以将策略效果设置为 Audit。 此策略效果允许部署那些未通过签名验证的映像,同时标记 AKS 群集和相关工作负载为合规状态。

将策略分配给 AKS 群集,其默认效果为 Deny

export POLICY_SCOPE=$(az aks show -g "$AKS_RG" -n "$AKS_NAME" --query id -o tsv)
az policy assignment create --policy "$DEFINITION_ID" --name "$DEFINITION_NAME" --scope "$POLICY_SCOPE"

若要将策略效果 Audit更改为,可以将另一个参数传递给 az policy assignment create 命令。 例如:

az policy assignment create --policy "$DEFINITION_ID" --name "$DEFINITION_NAME" --scope "$POLICY_SCOPE" -p "{\"effect\": {\"value\":\"Audit\"}}"

注释

完成作业大约需要 15 分钟。

使用以下命令检查自定义策略状态:

kubectl get constraintTemplate ratifyverification

下面是成功策略分配的输出示例:

NAME                 AGE
ratifyverification   11m

若要对现有分配进行更改,首先需要删除现有分配,进行更改,最后创建新的分配。

部署映像并检查策略效果

现已成功配置“批准”并将 Azure 策略分配给 AKS 群集,接下来可以测试签名验证功能。 以下部分演示了如何通过部署不同类型的容器映像并观察结果来实践策略强制实施。

测试三种方案来验证设置:

  • 使用受信任的证书签名的映像:应成功部署。
  • 未签名的图像:应阻止(效果 Deny )或标记为合规(效果 Audit )。
  • 使用不受信任证书签名的图像:应以Deny方式阻止,或以Audit效果标记为合规。

观察到的行为取决于你在分配 Azure 策略时选择的策略效果。 此测试过程有助于确保签名验证正常工作,并且能确保在生产环境中仅允许受信任的映像,从而增强信心。

使用拒绝策略效果

通过 Deny 策略效果,仅允许使用受信任标识签名的映像进行部署。 可以开始部署工作负荷来观察效果。 本文介绍如何使用 kubectl 命令部署 Pod。 同样,可以使用 Helm 图表或任何触发 Helm 安装的模板来部署工作负荷。

设置环境变量:

export IMAGE_SIGNED=<signed-image-reference>
export IMAGE_UNSIGNED=<unsigned-image-reference>
export IMAGE_SIGNED_UNTRUSTED=<signed-untrusted-image-reference>

运行以下命令。 $IMAGE_SIGNED 引用了由受信任的标识签名并在 Ratify 中配置的映像,因此允许进行部署。

kubectl run demo-signed --image=$IMAGE_SIGNED

下面是成功部署的输出示例:

pod/demo-signed created

$IMAGE_UNSIGNED 变量引用未签名的图像。 该 $IMAGE_SIGNED_UNTRUSTED 变量引用的图像是通过您不信任的其他证书签名的。 因此,这两个镜像被拒绝部署。 例如,运行以下命令:

kubectl run demo-unsigned --image=$IMAGE_UNSIGNED

这里是一个被拒绝的部署的输出示例:

Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: [azurepolicy-ratifyverification-077bac5b63d37da0bc4a] Subject failed verification: $IMAGE_UNSIGNED

可以使用以下命令查看批准日志。 然后,可以使用文本 verification response for subject $IMAGE_UNSIGNED搜索日志。 检查errorReason字段以了解被拒绝的部署的原因。

kubectl logs <ratify-pod> -n $RATIFY_NAMESPACE

使用审核策略效果

通过 Audit 策略效果,允许使用不受信任的标识签名的未签名映像或映像进行部署。 但是,AKS 群集和相关组件被标记为 noncompliant。 有关如何查看不符合资源并了解原因的详细信息,请参阅 获取 Azure 资源的符合性数据

清理

使用以下命令卸载 Ratify 并清理其自定义资源定义(CRDs):

helm delete ratify --namespace $RATIFY_NAMESPACE
kubectl delete crd stores.config.ratify.deislabs.io verifiers.config.ratify.deislabs.io certificatestores.config.ratify.deislabs.io policies.config.ratify.deislabs.io keymanagementproviders.config.ratify.deislabs.io namespacedkeymanagementproviders.config.ratify.deislabs.io namespacedpolicies.config.ratify.deislabs.io namespacedstores.config.ratify.deislabs.io namespacedverifiers.config.ratify.deislabs.io

使用以下命令删除策略分配和定义:

az policy assignment delete --name "$DEFINITION_NAME" --scope "$POLICY_SCOPE"
az policy definition delete --name "$DEFINITION_NAME"

FAQ

如果无权访问 Key Vault,如何设置用于签名验证的证书?

在某些情况下,映像使用者可能无权访问用于签名验证的证书。 若要验证签名,需要下载 PEM 格式的根 CA 证书文件,并指定用于安装批准 Helm 图表的相关参数。

以下示例命令类似于以前的安装命令,但没有任何与 Key Vault 证书相关的参数。 公证项目信任存储是指在参数 notationCerts[0]中传递的证书文件。

helm install ratify ratify/ratify --atomic --namespace $RATIFY_NAMESPACE --create-namespace --version $CHART_VER --set provider.enableMutation=false --set featureFlags.RATIFY_CERT_ROTATION=true \
--set azureWorkloadIdentity.clientId=$IDENTITY_CLIENT_ID \
--set oras.authProviders.azureWorkloadIdentityEnabled=true \
--set-file notationCerts[0]="<root-ca-certifice-filepath>"
--set notation.trustPolicies[0].registryScopes[0]="$REPO_URI" \
--set notation.trustPolicies[0].trustStores[0]="ca:notationCerts[0]" \
--set notation.trustPolicies[0].trustedIdentities[0]="x509.subject: $SUBJECT"

由于 notationCerts[0] 用于根 CA 证书,因此,如果有额外的证书文件用于时间戳,请确保使用正确的索引。 例如,如果 notationCerts[1] 用于 TSA 根证书文件,请将信任存储 notation.trustPolicies[0].trustStores[1]" 与值 "tsa:notationCerts[1]"一起使用。

如果在 AKS 群集中禁用 Azure Policy,应采取哪些步骤?

如果在 AKS 群集中禁用 Azure Policy,则必须在安装批准之前将 OPA Gatekeeper 安装为策略控制器。

注释

Azure Policy 应保持禁用状态,因为 Gatekeeper 与 AKS 群集上的 Azure Policy 加载项冲突。 若要稍后启用 Azure Policy,需要卸载 Gatekeeper 和 Ratify,然后按照本文中的步骤,使用已启用 Azure Policy 的环境来设置 Ratify。

helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts

helm install gatekeeper/gatekeeper  \
--name-template=gatekeeper \
--namespace gatekeeper-system --create-namespace \
--set enableExternalData=true \
--set validatingWebhookTimeoutSeconds=5 \
--set mutatingWebhookTimeoutSeconds=2 \
--set externaldataProviderResponseCacheTTL=10s

然后,按照前面的步骤中所述安装Ratify。 安装后,使用以下命令强制实施策略。 默认情况下,策略效果设置为 Deny。 可以参考 Gatekeeper 冲突文档 来更新 constraint.yaml 文件以获取不同的策略效果。

kubectl apply -f https://notaryproject.github.io/ratify/library/default/template.yaml
kubectl apply -f https://notaryproject.github.io/ratify/library/default/samples/constraint.yaml

安装 Ratify 后,我该如何更新其配置?

批准配置是 Kubernetes 自定义资源。 可以在不重新安装Ratify的情况下更新这些资源。

  • 若要更新密钥保管库相关配置,使用类型为 azurekeyvault 的 Ratify KeyManagementProvider 自定义资源。 要更新可信签名相关配置,请使用 Ratify KeyManagementProvider 自定义资源,这种资源的类型为 inline。 请按照 文档操作。
  • 若要更新公证项目信任策略和存储,请使用批准 Verifier 自定义资源。 请按照 文档操作。
  • 若要对容器注册表(或其他符合 OCI 的注册表)进行身份验证和交互,请使用批准存储自定义资源。 请按照 文档操作。

如果未通过Notation工具对容器镜像进行签名,我该怎么办?

本文适用于在可生成与公证项目兼容的签名的任何工具上独立验证公证项目签名。 Ratify还支持验证其他类型的签名。 有关详细信息,请参阅 Ratify 用户指南