保护 AKS 工作负载:使用批准和 Azure Policy 验证容器映像签名

容器安全性在云原生环境中至关重要,用于保护工作负荷。 为了改善安全态势,Azure 引入了 容器安全供应链(CSSC)框架,增强了容器映像整个生命周期的安全性。 CSSC 框架中定义的阶段之一是 Deploy 阶段,即在其中容器映像被部署到生产环境,例如 Azure Kubernetes 服务(AKS)群集。 确保安全生产环境涉及维护容器映像的完整性和真实性,这是通过在生成阶段对容器映像进行签名来实现的,然后在部署阶段验证它们,确保仅部署受信任的和未更改的映像。

Ratify 是由 Microsoft 支持的 CNCF 沙盒项目,是一个强大的验证引擎,专注于验证容器镜像的安全元数据,如签名,并仅允许部署符合您指定策略的镜像。

Scenarios

本部分介绍在 AKS 上实现容器映像签名验证的两个主要方案。 这些方案因管理证书进行签名和验证的方式而异:使用 Azure Key Vault (AKV) 进行传统证书管理。 选择符合当前证书管理方法和安全要求的方案。

使用 AKV 进行证书管理

映像生成者在 CI/CD 管道中生成容器映像并将其推送到 Azure 容器注册表(ACR)。 这些映像适用于映像使用者在 AKS 群集上部署和运行云原生工作负荷。 映像生成者在 CI/CD 管道中使用 Notary Project 工具(特别是 Notation)在 ACR 中对容器映像进行签名。 用于签名的密钥和证书安全地存储在 Azure Key Vault (AKV) 中。 签名完成后,公证项目签名将被创建并存储在 ACR 中,并引用相应的图像。 映像使用者在 AKS 群集上设置批准策略,以在部署期间验证映像的公证项目签名。 如果策略效果设置为拒绝效果,则签名验证失败的映像将无法部署。 这可确保仅将受信任的和未更改的映像部署到 AKS 群集。

作为映像生成者,请按照以下文档使用 AKV 在 ACR 中对容器映像进行签名:

签名验证概述

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

  1. 为 ACR 设置标识和访问控制:配置 Ratify 使用的标识,以必要的角色访问 ACR。

  2. 为 AKV 设置标识和访问控制:配置 Ratify 使用的标识,以访问 AKV,并授予其必要的角色。 如果图像使用受信任的签名,请跳过此步骤。

  3. 在 AKS 群集上设置 Ratify:使用 Helm Chart 安装,将 Ratify 设置为标准的 Kubernetes 服务。

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

执行这些步骤后,可以开始部署工作负载来观察结果。 Deny使用效果策略时,仅允许通过签名验证的映像进行部署,而未签名或由不受信任的标识签名的映像将被拒绝。 Audit借助效果策略,可以部署映像,但组件将标记为不符合审核目的。

先决条件

设置标识和访问控制

在 AKS 群集上安装批准之前,需要建立适当的标识和访问控制。 Ratify 需要访问 ACR 来拉取容器映像和签名,并且在使用 Azure Key Vault 进行证书管理时,也需要访问以获取证书用于签名验证。 本部分将指导你创建用户分配的托管标识,并配置批准所需的权限,以便在 Azure 环境中安全运行。

标识配置涉及:

  • 创建或使用现有用户分配的托管标识
  • 设置联合身份凭证以启用工作负载身份验证
  • 为 ACR 访问权限授予适当的角色分配
  • 配置 AKV 访问权限(使用 AKV 进行证书管理时)

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

如果还没有用户分配的托管标识,请按照 本文档 创建一个。 Ratify 将使用此标识来访问 Azure 资源,比如 ACR,并在适用时使用 AKV 进行证书管理。

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

设置环境变量:

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"

注释

如果不使用默认值,请更新变量 RATIFY_NAMESPACERATIFY_SA_NAME 的值。 务必在安装 Ratify Helm 图表时使用相同的值。

以下命令为托管标识创建一个联合凭据,允许它使用由 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"

配置对 ACR 的访问权限

要拉取签名和其他容器映像元数据,您的标识需要具有角色 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}

配置对 AKV 的访问

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

您的标识需要 Key Vault Secrets User 角色才能从 Azure 密钥保管库 (AKV) 获取整个证书链。 请按照以下说明来分配角色:

为 AKV 资源设置额外的环境变量:

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。 批准作为与 Azure Policy 集成的验证引擎运行,以强制实施签名验证策略。 安装过程涉及使用 Helm chart 部署 Ratify,其中包含特定配置参数,用于定义其应如何验证容器镜像签名。

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

  • 了解证书管理方法所需的 Helm 图表参数(AKV 签名)

  • 使用适当的配置安装Ratify以启用签名验证功能

配置参数因是否使用 AKV 进行证书管理而有所不同,因此请确保按照与所选方案匹配的说明进行作。

了解 Helm 图表的参数

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

配置因是否使用 AKV 进行证书管理而有所不同。

您需要进行配置:

  • 以前为访问 ACR 和 AKV 而设置的标识。
  • 存储在 AKV 中用于签名验证的证书。
  • 用于签名验证的公证项目信任策略,包括registryScopestrustStorestrustedIdentities

有关详细信息,请参阅下面的参数表:

参数 Description 价值
azureWorkloadIdentity.clientId 指定 Azure 工作负荷标识的客户端 ID “$IDENTITY_CLIENT_ID”
oras.authProviders.azureWorkloadIdentityEnabled 为 ACR 身份验证启用/禁用 Azure 工作负荷标识
azurekeyvault.enabled 启用/禁用从 AKV 获取证书
azurekeyvault.vaultURI AKV 资源的 URI “https://$AKV_NAME.vault.azure.cn”
azurekeyvault.tenantId AKV 资源的租户 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 图表,并设定所需的参数和值。

确保 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.cn/<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.cn" \
--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 Policy,该策略将在容器部署期间强制实施签名验证。 此策略充当强制机制,指示群集在允许部署之前使用批准来验证容器映像签名。

Azure Policy 提供两种强制模式:

  • 拒绝效果:阻止部署未通过签名验证的映像,确保仅在群集中运行受信任的映像
  • 审核效果:允许所有部署,但出于监视和报告目的标记合规资源

审核效果在初始设置或测试阶段非常有用,使你可以验证配置,而不会危及生产环境中的服务中断。

将新策略分配给 AKS 群集

创建自定义 Azure 策略进行签名验证。 默认情况下,策略效果设置为 Deny,这意味着失败签名验证的映像被拒绝部署。 或者,您可以将策略效果配置为 Audit,允许即使签名验证失败的映像也可以被部署,同时仍将 AKS 群集和相关工作负载标记为合规。 Audit 效果在验证签名验证配置时非常实用,可以避免因生产环境设置不当而造成的中断。

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将策略分配给 AKS 群集。

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 Policy 分配到 AKS 群集,接下来可以测试签名验证功能。 本部分演示了如何通过部署不同类型的容器映像和观察结果来实施策略。

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

  • 使用受信任证书签名的映像:应成功部署
  • 未签名图像:应阻止(具有拒绝效果)或标记为合规(具有审核效果)
  • 使用不受信任的证书签名的图像:应阻止(具有拒绝效果)或标记为合规(具有审核效果)

观察到的行为取决于在 Azure Policy 分配步骤中选择的策略效果。 此测试过程有助于确保签名验证正常运行,并确保在生产环境中仅允许受信任的镜像,从而提高您对该过程的信心。

使用拒绝策略效果

通过 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

使用审核策略的影响

在审核策略生效的情况下,允许部署未签名的映像或使用不受信任标识签名的映像。 但是,AKS 群集和相关组件被标记为 noncompliant。 有关如何查看不符合资源并了解原因的更多详细信息,请参阅 “获取 Azure 策略符合性数据”。

清理

使用以下命令卸载 Ratify 并清理 Ratify CRD:

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

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

在某些情况下,映像使用者可能无权访问用于签名验证的证书。 若要验证签名,需要下载 PEM 格式的根 CA 证书文件,并为 Ratify Helm chart 安装指定相关参数。 下面是类似于上一个安装命令的示例命令,但没有任何与 AKV 证书相关的参数。 公证项目中的信任存储指的是作为参数传入的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 自定义资源,允许你更新这些资源,而无需重新安装批准。

  • 若要更新 AKV 相关配置,请使用名为KeyManagementProvider"Ratify"的azurekeyvault自定义资源。 若要更新受信任的签名相关配置,请使用类型为KeyManagementProvider“批准inline”自定义资源。 请按照 文档操作。
  • 若要更新公证项目信任策略和存储,请使用批准 Verifier 自定义资源。 按照 文档进行操作。
  • 若要对 ACR(或其他符合 OCI 的注册表)进行身份验证和交互,请使用批准商店自定义资源。 请遵循 文档

如果未使用表示法工具对容器映像进行签名,该怎么办?

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