在 Azure Kubernetes 服务 (AKS) 中将 Azure 标识提供者连接到 Azure Key Vault 机密存储 CSI 驱动程序

Azure Kubernetes 服务 (AKS) 上的机密存储容器存储接口 (CSI) 驱动程序提供了多种基于标识访问 Azure Key Vault 的方法。 本文概述了何时使用基于角色的访问控制 (RBAC) 或 OpenID Connect (OIDC) 安全模型来访问 Key Vault 和 AKS 群集的方法和最佳做法。

可以使用以下访问方法之一:

CSI 驱动程序的先决条件

使用 Microsoft Entra 工作负载 ID 进行访问

Microsoft Entra 工作负载 ID 是 Pod 上运行的应用程序使用的标识,可针对其他 Azure 服务(例如软件中的工作负载)进行自身身份验证。 机密存储 CSI 驱动程序与本机 Kubernetes 功能集成,可与外部标识提供者联合。

在此安全模型中,AKS 群集充当令牌颁发者。 然后,Microsoft Entra ID 会使用 OIDC 发现公共签名密钥,并在用它交换 Microsoft Entra 令牌前验证服务帐户令牌的真实性。 要使工作负载用投影到其卷的服务帐户令牌交换 Microsoft Entra 令牌,需要使用 Azure SDK 或 Microsoft 身份验证库 (MSAL) 中的 Azure 标识客户端库

注意

  • 此身份验证方法取代了 Microsoft Entra Pod 托管标识(预览版)。 Azure Kubernetes 服务中的开源 Microsoft Entra Pod 托管标识(预览版)已于 2022 年 10 月 24 日弃用。
  • Windows 和 Linux 群集都支持 Microsoft Entra 工作负载 ID。

配置工作负载标识

  1. 使用 az account set 命令设置订阅。

    export SUBSCRIPTION_ID=<subscription id>
    export RESOURCE_GROUP=<resource group name>
    export UAMI=<name for user assigned identity>
    export KEYVAULT_NAME=<existing keyvault name>
    export CLUSTER_NAME=<aks cluster name>
    
    az account set --subscription $SUBSCRIPTION_ID
    
  2. 使用 az identity create 命令创建托管标识。

    az identity create --name $UAMI --resource-group $RESOURCE_GROUP
    
    export USER_ASSIGNED_CLIENT_ID="$(az identity show -g $RESOURCE_GROUP --name $UAMI --query 'clientId' -o tsv)"
    export IDENTITY_TENANT=$(az aks show --name $CLUSTER_NAME --resource-group $RESOURCE_GROUP --query identity.tenantId -o tsv)
    
  3. 创建角色分配,以使用 az role assignment create 命令向工作负载标识授予访问密钥保管库机密、访问密钥和证书的权限。

    export KEYVAULT_SCOPE=$(az keyvault show --name $KEYVAULT_NAME --query id -o tsv)
    
    az role assignment create --role "Key Vault Administrator" --assignee $USER_ASSIGNED_CLIENT_ID --scope $KEYVAULT_SCOPE
    
  4. 使用 az aks show 命令获取 AKS 群集 OIDC 颁发者 URL。

    注意

    此步骤假定你已有启用了 OIDC 颁发者 URL 的现有 AKS 群集。 如果未启用它,请参阅使用 OIDC 颁发者更新 AKS 群集以启用它。

    export AKS_OIDC_ISSUER="$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --query "oidcIssuerProfile.issuerUrl" -o tsv)"
    echo $AKS_OIDC_ISSUER
    
  5. 在 Microsoft Entra 应用程序、服务帐户颁发者和使用者之间建立联合标识凭据。 使用以下命令获取 Microsoft Entra 应用程序的对象 ID。 确保使用 Kubernetes 服务帐户名称及其命名空间更新 serviceAccountNameserviceAccountNamespace 的值。

    export SERVICE_ACCOUNT_NAME="workload-identity-sa"  # sample name; can be changed
    export SERVICE_ACCOUNT_NAMESPACE="default" # can be changed to namespace of your workload
    
    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      annotations:
        azure.workload.identity/client-id: ${USER_ASSIGNED_CLIENT_ID}
      name: ${SERVICE_ACCOUNT_NAME}
      namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    EOF
    
  6. 使用 az identity federated-credential create 命令在托管标识、服务帐户颁发者和使用者之间创建联合标识凭据。

    export FEDERATED_IDENTITY_NAME="aksfederatedidentity" # can be changed as needed
    
    az identity federated-credential create --name $FEDERATED_IDENTITY_NAME --identity-name $UAMI --resource-group $RESOURCE_GROUP --issuer ${AKS_OIDC_ISSUER} --subject system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_NAME}
    
  7. 使用 kubectl apply 命令和以下 YAML 脚本部署 SecretProviderClass

    cat <<EOF | kubectl apply -f -
    # This is a SecretProviderClass example using workload identity to access your key vault
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: azure-kvname-wi # needs to be unique per namespace
    spec:
      provider: azure
      parameters:
        usePodIdentity: "false"
        clientID: "${USER_ASSIGNED_CLIENT_ID}" # Setting this to use workload identity
        keyvaultName: ${KEYVAULT_NAME}       # Set to the name of your key vault
        cloudName: "AzureChinaCloud"                         # [OPTIONAL for Azure] if not provided, the Azure environment defaults to AzureChinaCloud
        objects:  |
          array:
            - |
              objectName: secret1             # Set to the name of your secret
              objectType: secret              # object types: secret, key, or cert
              objectVersion: ""               # [OPTIONAL] object versions, default to latest if empty
            - |
              objectName: key1                # Set to the name of your key
              objectType: key
              objectVersion: ""
        tenantId: "${IDENTITY_TENANT}"        # The tenant ID of the key vault
    EOF
    

    注意

    如果使用 objectAlias 而不是 objectName,请更新 YAML 脚本以进行解释。

    注意

    若要使 SecretProviderClass 正常运行,在 objects 部分中引用机密、密钥或证书之前,请确保在 Azure Key Vault 中填充它们。

  8. 使用 kubectl apply 命令和以下 YAML 脚本部署示例 Pod。

    cat <<EOF | kubectl apply -f -
    # This is a sample pod definition for using SecretProviderClass and workload identity to access your key vault
    kind: Pod
    apiVersion: v1
    metadata:
      name: busybox-secrets-store-inline-wi
      labels:
        azure.workload.identity/use: "true"
    spec:
      serviceAccountName: "workload-identity-sa"
      containers:
        - name: busybox
          image: k8sgcr.azk8s.cn/e2e-test-images/busybox:1.29-4
          command:
            - "/bin/sleep"
            - "10000"
          volumeMounts:
          - name: secrets-store01-inline
            mountPath: "/mnt/secrets-store"
            readOnly: true
      volumes:
        - name: secrets-store01-inline
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "azure-kvname-wi"
    EOF
    

使用托管标识访问

Microsoft Entra 托管 ID 是管理员用来对其他 Azure 服务进行身份验证的标识。 托管标识使用 RBAC 与外部标识提供者联合。

在此安全模型中,可以向共享托管角色的团队成员或租户授予对群集资源的访问权限。 检查角色的范围是否可访问密钥保管库和其他凭据。 在 AKS 群集上启用用于机密存储 CSI 驱动程序的 Azure Key Vault 提供程序时,它会创建一个用户标识。

配置托管标识

  1. 使用 az aks show 命令和加载项创建的用户分配的托管标识访问密钥保管库。 你还应当检索标识的 clientId,以便在后续步骤中在创建 SecretProviderClass 时使用。

    az aks show -g <resource-group> -n <cluster-name> --query addonProfiles.azureKeyvaultSecretsProvider.identity.objectId -o tsv
    az aks show -g <resource-group> -n <cluster-name> --query addonProfiles.azureKeyvaultSecretsProvider.identity.clientId -o tsv
    

    或者,可以使用以下命令创建新的托管标识,并将其分配给虚拟机 (VM) 规模集或分配给可用性集内的每个 VM 实例。

    az identity create -g <resource-group> -n <identity-name>
    az vmss identity assign -g <resource-group> -n <agent-pool-vmss> --identities <identity-resource-id>
    az vm identity assign -g <resource-group> -n <agent-pool-vm> --identities <identity-resource-id>
    
    az identity show -g <resource-group> --name <identity-name> --query 'clientId' -o tsv
    
  2. 使用 az role assignment create 命令创建角色分配,授予标识访问密钥保管库机密、访问密钥和证书的权限。

    export IDENTITY_OBJECT_ID="$(az identity show -g <resource-group> --name <identity-name> --query 'principalId' -o tsv)"
    export KEYVAULT_SCOPE=$(az keyvault show --name <key-vault-name> --query id -o tsv)
    
    az role assignment create --role "Key Vault Administrator" --assignee $IDENTITY_OBJECT_ID --scope $KEYVAULT_SCOPE
    
  3. 使用以下 YAML 创建 SecretProviderClass。 确保对 userAssignedIdentityIDkeyvaultNametenantId 和要从密钥保管库中检索的对象使用自己的值。

    # This is a SecretProviderClass example using user-assigned identity to access your key vault
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: azure-kvname-user-msi
    spec:
      provider: azure
      parameters:
        usePodIdentity: "false"
        useVMManagedIdentity: "true"          # Set to true for using managed identity
        userAssignedIdentityID: <client-id>   # Set the clientID of the user-assigned managed identity to use
        keyvaultName: <key-vault-name>        # Set to the name of your key vault
        cloudName: "AzureChinaCloud"          # [OPTIONAL for Azure] if not provided, the Azure environment defaults to AzurePublicCloud
        objects:  |
          array:
            - |
              objectName: secret1
              objectType: secret              # object types: secret, key, or cert
              objectVersion: ""               # [OPTIONAL] object versions, default to latest if empty
            - |
              objectName: key1
              objectType: key
              objectVersion: ""
        tenantId: <tenant-id>                 # The tenant ID of the key vault
    

    注意

    如果使用 objectAlias 而不是 objectName,请务必更新 YAML 脚本。

    注意

    若要使 SecretProviderClass 正常运行,在 objects 部分中引用机密、密钥或证书之前,请确保在 Azure Key Vault 中填充它们。

  4. 使用 kubectl apply 命令将 SecretProviderClass 应用到群集。

    kubectl apply -f secretproviderclass.yaml
    
  5. 使用以下 YAML 创建 Pod。

    # This is a sample pod definition for using SecretProviderClass and the user-assigned identity to access your key vault
    kind: Pod
    apiVersion: v1
    metadata:
      name: busybox-secrets-store-inline-user-msi
    spec:
      containers:
          name: busybox
          image: k8sgcr.azk8s.cn/e2e-test-images/busybox:1.29-4
          command:
            - "/bin/sleep"
            - "10000"
          volumeMounts:
          - name: secrets-store01-inline
            mountPath: "/mnt/secrets-store"
            readOnly: true
      volumes:
        - name: secrets-store01-inline
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "azure-kvname-user-msi"
    
  6. 使用 kubectl apply 命令将 Pod 应用到群集。

    kubectl apply -f pod.yaml
    

使用 Key Vault 机密

Pod 启动后,在部署 YAML 中指定的卷路径上装载的内容便可用了。 使用以下命令验证机密并打印测试机密。

  1. 使用以下命令显示机密存储中保存的机密。

    kubectl exec busybox-secrets-store-inline -- ls /mnt/secrets-store/
    
  2. 使用以下命令显示存储中的机密。 此示例命令会显示测试机密 ExampleSecret

    kubectl exec busybox-secrets-store-inline -- cat /mnt/secrets-store/ExampleSecret
    

获取证书和密钥

Azure 密钥保管库设计在密钥、机密和证书之间进行了明确区分。 Key Vault 服务的证书功能旨在利用其密钥和机密功能。 创建密钥保管库证书后,它会创建具有相同名称的可寻址密钥和机密。 密钥允许身份验证操作,机密允许以机密形式检索证书值。

密钥保管库证书还包含公共 x509 证书元数据。 密钥保管库将证书的公共组成部分和私用组成部分存储在一个机密中。 可以通过在 SecretProviderClass 中指定 objectType 来获取每个单独的组成部分。 下表显示了哪些对象映射到与证书关联的各种资源:

Object 返回值 返回整个证书链
key 采用隐私增强邮件 (PEM) 格式的公钥。 不可用
cert 采用 PEM 格式的证书。
secret 采用 PEM 格式的私钥和证书。

在现有群集上禁用加载项

注意

在禁用该加载项之前,请确保没有正在使用 SecretProviderClass。 当存在 SecretProviderClass 时尝试禁用该加载项会导致出错。

  • 配合使用 az aks disable-addons 命令与 azure-keyvault-secrets-provider 加载项,在现有群集中禁用适用于机密存储 CSI 驱动程序的 Azure 密钥保管库提供程序功能。

    az aks disable-addons --addons azure-keyvault-secrets-provider -g myResourceGroup -n myAKSCluster
    

注意

在禁用加载项时,现有工作负载应该没有问题,或者不应在已装载的机密中看到任何更新。 如果 Pod 重启,或作为纵向扩展事件的一部分创建新 Pod,该 Pod 将无法启动,因为驱动程序已不再运行。

后续步骤

本文介绍了如何创建和提供用于访问 Azure 密钥保管库的标识。 如果要配置额外的配置选项或执行故障排除,请继续阅读下一篇文章。