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

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

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

  • 具有工作负载 ID 的服务连接器
  • 工作负载 ID
  • 用户分配的托管标识

了解如何使用服务连接器在 Azure Kubernetes 服务 (AKS) 群集中通过 CSI 驱动程序连接到 Azure 密钥保管库。 在本文中,你将完成以下任务:

  • 创建 AKS 群集和 Azure Key Vault。
  • 使用服务连接器在 AKS 群集与 Azure Key Vault 之间创建连接。
  • 创建一个 SecretProviderClass CRD 和一个使用 CSI 提供程序的 Pod 来测试连接。
  • 清理资源。

重要

AKS 预览功能是可选择启用的自助功能。 预览功能是“按现状”和“按可用”提供的,不包括在服务级别协议和有限保证中。 AKS 预览功能是由客户支持尽最大努力部分覆盖。 因此,这些功能并不适合用于生产。 有关详细信息,请参阅以下支持文章:

先决条件

初始设置

  1. 如果是第一次使用服务连接器,请首先运行命令 az provider register 来注册服务连接器和 Kubernetes 配置资源提供程序。

    az provider register -n Microsoft.ServiceLinker
    
    az provider register -n Microsoft.KubernetesConfiguration
    

    提示

    你可以通过运行命令 az provider show -n "Microsoft.ServiceLinker" --query registrationStateaz provider show -n "Microsoft.KubernetesConfiguration" --query registrationState 来检查是否已注册这些资源提供程序。

  2. (可选)使用 Azure CLI 命令获取 AKS 群集支持的目标服务列表。

    az aks connection list-support-types --output table
    

创建 Azure 资源

  1. 使用 az group create 命令创建资源组。

    az group create \
        --name <resource-group-name> \
        --location <location>
    
  2. 使用 az aks create 命令创建 AKS 群集。 以下示例创建了一个启用了托管标识的单节点 AKS 群集。

    az aks create \
        --resource-group <resource-group-name> \
        --name <cluster-name> \
        --enable-managed-identity \
        --node-count 1
    
  3. 使用 az aks get-credentials 命令连接到群集。

    az aks get-credentials \
        --resource-group <resource-group-name> \
        --name <cluster-name>
    
  4. 使用 az keyvault create 命令创建 Azure 密钥保管库。

    az keyvault create \
        --resource-group <resource-group-name> \  
        --name <key-vault-name> \
        --location <location>
    
  5. 使用 az keyvault secret set 命令在密钥保管库中创建一个机密。

    az keyvault secret set \
        --vault-name <key-vault-name> \
        --name <secret-name> \
        --value <secret-value>
    

使用服务连接器在 AKS 中创建服务连接(预览版)

可以通过 Azure 门户或 Azure CLI 创建到 Azure 密钥保管库的服务连接。

  1. 在 Azure 门户中导航到你的 AKS 群集资源。

  2. 在服务菜单中的“设置”下,选择“服务连接器(预览版)”>“创建”

  3. 在“创建连接”页面上的“基本信息”选项卡中,配置以下设置

    • Kubernetes 命名空间:选择“默认”
    • 服务类型:选择“密钥保管库”,然后选中该复选框以启用 Azure 密钥保管库 CSI 提供程序
    • 连接名称:输入此连接的名称
    • 订阅:选择包含密钥保管库的订阅
    • 密钥保管库:选择之前创建的密钥保管库
    • 客户端类型:选择“无”
  4. 选择“查看 + 创建”,然后选择“创建”以创建连接

测试连接

克隆示例存储库并部署清单文件

  1. 使用 git clone 命令克隆示例存储库。

    git clone https://github.com/Azure-Samples/serviceconnector-aks-samples.git
    
  2. 将目录更改为 Azure 密钥保管库 CSI 提供程序示例的目录。

    cd serviceconnector-aks-samples/azure-keyvault-csi-provider
    
  3. secret_provider_class.yaml 文件中,将以下占位符替换为 Azure 密钥保管库信息:

    • <AZURE_KEYVAULT_NAME> 替换为创建和连接的密钥保管库的名称。
    • <AZURE_KEYVAULT_TENANTID> 替换为密钥保管库的租户 ID。
    • <AZURE_KEYVAULT_CLIENTID> 替换为 azureKeyvaultSecretsProvider 加载项的标识客户端 ID。
    • <KEYVAULT_SECRET_NAME> 替换为创建的密钥保管库机密。 例如,ExampleSecret
  4. 使用 kubectl apply 命令部署 SecretProviderClass CRD。

    kubectl apply -f secret_provider_class.yaml
    
  5. 使用 kubectl apply 命令部署 Pod 清单文件。

    该命令在 AKS 群集的默认命名空间中创建名为 sc-demo-keyvault-csi 的 Pod。

    kubectl apply -f pod.yaml
    

验证连接

  1. 使用 kubectl get 命令验证是否已成功创建 Pod。

    kubectl get pod/sc-demo-keyvault-csi
    

    Pod 启动后,在部署 YAML 中指定的卷路径上装载的内容便可用了。

  2. 使用 kubectl exec 命令显示机密存储中保存的机密。

    kubectl exec sc-demo-keyvault-csi -- ls /mnt/secrets-store/
    
  3. 使用 kubectl exec 命令显示一个机密。

    此示例命令显示名为 ExampleSecret 的测试机密。

    kubectl exec sc-demo-keyvault-csi -- cat /mnt/secrets-store/ExampleSecret
    

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 命令创建托管标识。

    注意

    此步骤假定你拥有已启用工作负荷标识的现有 AKS 群集。 如果未启用它,请参阅在现有 AKS 群集上启用工作负荷标识来启用。

    az identity create --name $UAMI --resource-group $RESOURCE_GROUP
    
    export USER_ASSIGNED_CLIENT_ID="$(az identity show --resource-group $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 命令向工作负载标识授予访问密钥保管库机密、访问密钥和证书的权限。

    重要

    • 如果密钥保管库已使用 --enable-rbac-authorization 进行设置,并且你使用 keycertificate 类型,则分配 Key Vault Certificate User 角色以授予权限。
    • 如果密钥保管库已使用 --enable-rbac-authorization 进行设置,并且你使用 secret 类型,则分配 Key Vault Secrets User 角色。
    • 如果密钥保管库未使用 --enable-rbac-authorization 进行设置,则可将 az keyvault set-policy 命令与 --key-permissions get--certificate-permissions get--secret-permissions get 参数一起使用,以创建密钥保管库策略来授予对密钥、证书或机密的访问权限。 例如:
    az keyvault set-policy --name $KEYVAULT_NAME --key-permissions get --object-id $IDENTITY_OBJECT_ID
    
    export KEYVAULT_SCOPE=$(az keyvault show --name $KEYVAULT_NAME --query id -o tsv)
    
    # Example command for key vault with RBAC enabled using `key` type
    az role assignment create --role "Key Vault Certificate User" --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
    

CSI 驱动程序的先决条件

使用托管标识访问

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

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

配置托管标识

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

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

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

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

    重要

    • 如果密钥保管库已使用 --enable-rbac-authorization 进行设置,并且你使用 keycertificate 类型,则分配 Key Vault Certificate User 角色。
    • 如果密钥保管库已使用 --enable-rbac-authorization 进行设置,并且你使用 secret 类型,则分配 Key Vault Secrets User 角色。
    • 如果密钥保管库未使用 --enable-rbac-authorization 进行设置,则可将 az keyvault set-policy 命令与 --key-permissions get--certificate-permissions get--secret-permissions get 参数一起使用,以创建密钥保管库策略来授予对密钥、证书或机密的访问权限。 例如:
    az keyvault set-policy --name $KEYVAULT_NAME --key-permissions get --object-id $IDENTITY_OBJECT_ID
    
    export IDENTITY_OBJECT_ID="$(az identity show --resource-group <resource-group> --name <identity-name> --query 'principalId' -o tsv)"
    export KEYVAULT_SCOPE=$(az keyvault show --name <key-vault-name> --query id -o tsv)
    
    # Example command for key vault with RBAC enabled using `key` type
    az role assignment create --role "Key Vault Certificate User" --assignee $USER_ASSIGNED_CLIENT_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-user-msi -- ls /mnt/secrets-store/
    
  2. 使用以下命令显示存储中的机密。 此示例命令会显示测试机密 ExampleSecret

    kubectl exec busybox-secrets-store-inline-user-msi -- 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 --resource-group myResourceGroup --name myAKSCluster
    

注意

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

后续步骤

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

如果你要配置额外的配置选项或执行故障排除,请参阅适用于 AKS 中机密存储 CSI 驱动程序的 Azure 密钥保管库提供程序的配置选项和故障排除资源