使用 Azure Key Vault 机密提供程序扩展将机密提取到已启用 Azure Arc 的 Kubernetes 群集

使用适用于机密存储 CSI 驱动程序的 Azure 密钥保管库提供程序,可以通过 CSI 卷将 Azure 密钥保管库作为机密存储与 Kubernetes 群集集成。 对于已启用 Azure Arc 的 Kubernetes 群集,可安装 Azure Key Vault 机密提供程序扩展以提取机密。

Azure Key Vault 机密提供程序扩展的功能包括:

  • 使用 CSI 内联卷将机密/密钥/证书装载到 pod
  • 支持使用 SecretProviderClass CRD 完成 Pod 移植
  • 支持 Linux 和 Windows 容器
  • 支持与 Kubernetes 机密同步
  • 支持机密自动轮换
  • 扩展组件部署到可用性区域,使其成为区域冗余组件

先决条件

  • 具有受支持的 Kubernetes 发行版的群集已连接到 Azure Arc。对于此方案,目前支持以下 Kubernetes 发行版:

    • Cluster API Azure
    • Azure Stack HCI 上的 Azure Kubernetes 服务 (AKS) 群集
    • Azure Arc 启用的 AKS
    • Google Kubernetes Engine
    • OpenShift Kubernetes Distribution
    • Canonical Kubernetes Distribution
    • 弹性 Kubernetes 服务
    • Tanzu Kubernetes 网格
    • Azure Red Hat OpenShift
  • 以下终结点的出站连接:

    • linuxgeneva-microsoft.azurecr.cn
    • upstreamarc.azurecr.cn
    • *.blob.core.chinacloudapi.cn
  • 请确保满足一般群集扩展先决条件。 必须使用 k8s-extension Azure CLI 扩展的 0.4.0 或更新版本。

在已启用 Arc 的 Kubernetes 群集上安装 Azure Key Vault 机密提供程序扩展

可以使用 Azure CLI 或通过部署 ARM 模板在 Azure 门户中将 Azure 密钥保管库机密提供程序扩展安装在连接的群集上。

每个已启用 Azure Arc 的 Kubernetes 群集上只能部署一个扩展实例。

提示

如果群集位于出站代理服务器后面,请确保在安装扩展之前使用“代理配置”选项将其连接到 Azure Arc。

Azure 门户

  1. Azure 门户中,导航到“Kubernetes - Azure Arc”并选择你的群集。

  2. 选择“设置”下的“扩展”,然后选择“+ 添加”。

    屏幕截图显示 Azure 门户中已启用 Arc 的 Kubernetes 群集的“扩展”窗格。

  3. 从可用扩展列表中,选择“Azure Key Vault 机密提供程序”以部署最新版本的扩展。

    屏幕截图显示 Azure 门户中的 Azure Key Vault 机密提供程序扩展。

  4. 按照提示部署扩展。 如果需要,请通过更改“配置”选项卡上的默认选项来自定义安装。

Azure CLI

  1. 设置环境变量:

    export CLUSTER_NAME=<arc-cluster-name>
    export RESOURCE_GROUP=<resource-group-name>
    
  2. 通过运行以下命令安装机密存储 CSI 驱动程序和 Azure Key Vault 机密提供程序扩展:

    az k8s-extension create --cluster-name $CLUSTER_NAME --resource-group $RESOURCE_GROUP --cluster-type connectedClusters --extension-type Microsoft.AzureKeyVaultSecretsProvider --name akvsecretsprovider
    

此时会看到与下例类似的输出。 可能需要几分钟时间才能将机密提供程序 Helm 图表部署到群集。

{
  "aksAssignedIdentity": null,
  "autoUpgradeMinorVersion": true,
  "configurationProtectedSettings": {},
  "configurationSettings": {},
  "customLocationSettings": null,
  "errorInfo": null,
  "extensionType": "microsoft.azurekeyvaultsecretsprovider",
  "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Kubernetes/connectedClusters/$CLUSTER_NAME/providers/Microsoft.KubernetesConfiguration/extensions/akvsecretsprovider",
  "identity": {
    "principalId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "tenantId": null,
    "type": "SystemAssigned"
  },
  "location": null,
  "name": "akvsecretsprovider",
  "packageUri": null,
  "provisioningState": "Succeeded",
  "releaseTrain": "Stable",
  "resourceGroup": "$RESOURCE_GROUP",
  "scope": {
    "cluster": {
      "releaseNamespace": "kube-system"
    },
    "namespace": null
  },
  "statuses": [],
  "systemData": {
    "createdAt": "2022-05-12T18:35:56.552889+00:00",
    "createdBy": null,
    "createdByType": null,
    "lastModifiedAt": "2022-05-12T18:35:56.552889+00:00",
    "lastModifiedBy": null,
    "lastModifiedByType": null
  },
  "type": "Microsoft.KubernetesConfiguration/extensions",
  "version": "1.1.3"
}

ARM 模板

  1. 使用以下格式创建 .json 文件。 请务必更新 cluster-name 值以引用群集<>。

    {
        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
            "ConnectedClusterName": {
                "defaultValue": "<cluster-name>",
                "type": "String",
                "metadata": {
                    "description": "The Connected Cluster name."
                }
            },
            "ExtensionInstanceName": {
                "defaultValue": "akvsecretsprovider",
                "type": "String",
                "metadata": {
                    "description": "The extension instance name."
                }
            },
            "ExtensionVersion": {
                "defaultValue": "",
                "type": "String",
                "metadata": {
                    "description": "The version of the extension type."
                }
            },
            "ExtensionType": {
                "defaultValue": "Microsoft.AzureKeyVaultSecretsProvider",
                "type": "String",
                "metadata": {
                    "description": "The extension type."
                }
            },
            "ReleaseTrain": {
                "defaultValue": "stable",
                "type": "String",
                "metadata": {
                    "description": "The release train."
                }
            }
        },
        "functions": [],
        "resources": [
            {
                "type": "Microsoft.KubernetesConfiguration/extensions",
                "apiVersion": "2021-09-01",
                "name": "[parameters('ExtensionInstanceName')]",
                "identity": {
                 "type": "SystemAssigned"
                },
                "properties": {
                    "extensionType": "[parameters('ExtensionType')]",
                    "releaseTrain": "[parameters('ReleaseTrain')]",
                    "version": "[parameters('ExtensionVersion')]"
                },
                "scope": "[concat('Microsoft.Kubernetes/connectedClusters/', parameters('ConnectedClusterName'))]"
            }
        ]
    }
    
  2. 现在使用以下 Azure CLI 命令设置环境变量:

    export TEMPLATE_FILE_NAME=<template-file-path>
    export DEPLOYMENT_NAME=<desired-deployment-name>
    
  3. 最后,运行此 Azure CLI 命令以安装 Azure Key Vault 机密提供程序扩展:

    az deployment group create --name $DEPLOYMENT_NAME --resource-group $RESOURCE_GROUP --template-file $TEMPLATE_FILE_NAME
    

现在,应该可以查看密钥提供程序资源并在群集中使用该扩展。

验证扩展安装

若要确认成功安装 Azure Key Vault 机密提供程序扩展,请运行以下命令。

az k8s-extension show --cluster-type connectedClusters --cluster-name $CLUSTER_NAME --resource-group $RESOURCE_GROUP --name akvsecretsprovider

此时会看到与下例类似的输出。

{
  "aksAssignedIdentity": null,
  "autoUpgradeMinorVersion": true,
  "configurationProtectedSettings": {},
  "configurationSettings": {},
  "customLocationSettings": null,
  "errorInfo": null,
  "extensionType": "microsoft.azurekeyvaultsecretsprovider",
  "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Kubernetes/connectedClusters/$CLUSTER_NAME/providers/Microsoft.KubernetesConfiguration/extensions/akvsecretsprovider",
  "identity": {
    "principalId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "tenantId": null,
    "type": "SystemAssigned"
  },
  "location": null,
  "name": "akvsecretsprovider",
  "packageUri": null,
  "provisioningState": "Succeeded",
  "releaseTrain": "Stable",
  "resourceGroup": "$RESOURCE_GROUP",
  "scope": {
    "cluster": {
      "releaseNamespace": "kube-system"
    },
    "namespace": null
  },
  "statuses": [],
  "systemData": {
    "createdAt": "2022-05-12T18:35:56.552889+00:00",
    "createdBy": null,
    "createdByType": null,
    "lastModifiedAt": "2022-05-12T18:35:56.552889+00:00",
    "lastModifiedBy": null,
    "lastModifiedByType": null
  },
  "type": "Microsoft.KubernetesConfiguration/extensions",
  "version": "1.1.3"
}

创建或选择 Azure Key Vault

接下来,指定要与连接的群集一起使用的 Azure Key Vault。 如果还没有 Key Vault,请使用以下命令创建一个新的 Key Vault。 请记住,密钥保管库的名称必须是全局唯一的。

设置以下环境变量:

export AKV_RESOURCE_GROUP=<resource-group-name>
export AZUREKEYVAULT_NAME=<AKV-name>
export AZUREKEYVAULT_LOCATION=<AKV-location>

接下来,运行以下命令:

az keyvault create -n $AZUREKEYVAULT_NAME -g $AKV_RESOURCE_GROUP -l $AZUREKEYVAULT_LOCATION

Azure Key Vault 可以存储密钥、机密和证书。 对于此示例,可以使用以下命令设置名为 DemoSecret 的纯文本机密:

az keyvault secret set --vault-name $AZUREKEYVAULT_NAME -n DemoSecret --value MyExampleSecret

在继续下一部分之前,请注意以下属性:

  • Key Vault 中机密对象的名称
  • 对象类型(机密、密钥或证书)
  • Key Vault 资源的名称
  • Key Vault 所属订阅的 Azure 租户 ID

提供标识以访问 Azure Key Vault

目前,可以通过服务主体访问已启用 Arc 的群集上的机密存储 CSI 驱动程序。 请按照以下步骤提供可访问 Key Vault 的标识。

  1. 按照步骤在 Azure 中创建服务主体。 请记下此步骤中生成的客户端 ID 和客户端机密。

  2. 接下来,确保 Azure 密钥保管库对创建的服务主体具有 GET 权限

  3. 使用第一步中记下的客户端 ID 和客户端密码在已连接的群集上创建 Kubernetes 机密:

    kubectl create secret generic secrets-store-creds --from-literal clientid="<client-id>" --from-literal clientsecret="<client-secret>"
    
  4. 标记创建的机密:

    kubectl label secret secrets-store-creds secrets-store.csi.k8s.io/used=true
    
  5. 使用以下 YAML 创建 SecretProviderClass,填写要从 AKV 实例检索的密钥保管库名称、租户 ID 和对象的值:

    # This is a SecretProviderClass example using service principal to access Keyvault
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: akvprovider-demo
    spec:
      provider: azure
      parameters:
        usePodIdentity: "false"
        keyvaultName: <key-vault-name>
        cloudName: AzureChinaCloud           # Defaults to AzureCloud
        objects:  |
          array:
            - |
              objectName: DemoSecret
              objectType: secret             # object types: secret, key or cert
              objectVersion: ""              # [OPTIONAL] object versions, default to latest if empty
        tenantId: <tenant-Id>                # The tenant ID of the Azure Key Vault instance
    

    若要与国家/地区云一起使用,请将世纪互联运营的 Azure 的 cloudName 更改为 AzureChinaCloud

  6. 将 SecretProviderClass 应用到群集:

    kubectl apply -f secretproviderclass.yaml
    
  7. 使用以下 YAML 创建一个 Pod,并填入你的标识名称:

    # This is a sample pod definition for using SecretProviderClass and service principal to access Keyvault
    kind: Pod
    apiVersion: v1
    metadata:
      name: busybox-secrets-store-inline
    spec:
      containers:
        - name: busybox
          image: k8s.gcr.io/e2e-test-images/busybox:1.29
          command:
            - "/bin/sleep"
            - "10000"
          volumeMounts:
          - name: secrets-store-inline
            mountPath: "/mnt/secrets-store"
            readOnly: true
      volumes:
        - name: secrets-store-inline
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "akvprovider-demo"
            nodePublishSecretRef:                       
              name: secrets-store-creds
    
  8. 将 Pod 应用到群集:

    kubectl apply -f pod.yaml
    

验证机密

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

## show secrets held in secrets-store
kubectl exec busybox-secrets-store-inline -- ls /mnt/secrets-store/

## print a test secret 'DemoSecret' held in secrets-store
kubectl exec busybox-secrets-store-inline -- cat /mnt/secrets-store/DemoSecret

其他配置选项

Azure Key Vault 机密提供程序扩展支持 Helm 图表配置

以下配置设置经常与 Azure Key Vault 机密提供程序扩展一起使用:

配置设置 默认 说明
enableSecretRotation false 布尔类型。 如果为 true,则使用外部机密存储中的最新内容定期更新 Pod 装载和 Kubernetes 机密
rotationPollInterval 2 m 如果 enableSecretRotationtrue,则此设置指定机密轮换轮询间隔持续时间。 可以根据将所有 Pod 和 Kubernetes 机密的装载内容重新同步到最新值的频率调整此持续时间。
syncSecret.enabled false 布尔输入。 在某些情况下,你可能想要创建一个 Kubernetes 机密来镜像装载的内容。 如果为 true,则 SecretProviderClass 允许 secretObjects 字段定义已同步的 Kubernetes 机密对象的所需状态。

可以在使用 az k8s-extension create 命令安装扩展时指定这些设置:

az k8s-extension create --cluster-name $CLUSTER_NAME --resource-group $RESOURCE_GROUP --cluster-type connectedClusters --extension-type Microsoft.AzureKeyVaultSecretsProvider --name akvsecretsprovider --configuration-settings secrets-store-csi-driver.enableSecretRotation=true secrets-store-csi-driver.rotationPollInterval=3m secrets-store-csi-driver.syncSecret.enabled=true

还可以在安装后使用 az k8s-extension update 命令更改这些设置:

az k8s-extension update --cluster-name $CLUSTER_NAME --resource-group $RESOURCE_GROUP --cluster-type connectedClusters --name akvsecretsprovider --configuration-settings secrets-store-csi-driver.enableSecretRotation=true secrets-store-csi-driver.rotationPollInterval=3m secrets-store-csi-driver.syncSecret.enabled=true

可以根据需要使用其他配置设置进行部署。 例如,若要在创建群集时更改 kubelet 根目录,请修改 az k8s-extension create 命令:

az k8s-extension create --cluster-name $CLUSTER_NAME --resource-group $RESOURCE_GROUP --cluster-type connectedClusters --extension-type Microsoft.AzureKeyVaultSecretsProvider --name akvsecretsprovider --configuration-settings linux.kubeletRootDir=/path/to/kubelet secrets-store-csi-driver.linux.kubeletRootDir=/path/to/kubelet

卸载 Azure Key Vault 机密提供程序扩展

若要卸载扩展,请运行以下命令:

az k8s-extension delete --cluster-type connectedClusters --cluster-name $CLUSTER_NAME --resource-group $RESOURCE_GROUP --name akvsecretsprovider

注意

卸载扩展不会删除安装扩展时创建的自定义资源定义 (CRD)。

若要确认扩展实例已删除,请运行以下命令:

az k8s-extension list --cluster-type connectedClusters --cluster-name $CLUSTER_NAME --resource-group $RESOURCE_GROUP

如果扩展已成功移除,输出中将不会列出 Azure Key Vault 机密提供程序扩展。 如果群集上未安装任何其他扩展,你将看到一个空数组。

如果不再需要它,请确保通过运行以下命令删除与服务主体关联的 Kubernetes 机密:

kubectl delete secret secrets-store-creds

复原和故障排除

Azure Key Vault 机密提供程序扩展可自我修复。 如果有人试图更改或删除在安装扩展时部署的扩展组件,该组件将复原至其原始状态。 唯一的例外是自定义资源定义 (CRD)。 如果删除了 CRD,它们不会复原。 若要恢复已删除的 CRD,请使用现有的扩展实例名称再次运行 az k8s-extension create 命令。

有关解决常见问题的详细信息,请参阅适用于机密存储 CSI 驱动程序的 Azure Key Vault 提供程序机密存储 CSI 驱动程序的开源故障排除指南。

后续步骤