在 Azure Kubernetes 服务 (AKS) 群集中使用 Azure 密钥保管库 提供程序作为 Secrets Store CSI 驱动程序

用于机密存储容器存储接口(CSI)驱动程序的 Azure 密钥保管库 提供程序允许将 Azure 密钥保管库 通过CSI 卷与 Azure Kubernetes 服务(AKS)集群进行集成,作为机密存储。

功能

  • 使用 CSI 卷将机密、密钥和证书装载到 Pod。
  • 支持 CSI 内联卷。
  • 支持将多个机密存储对象装载为单个卷。
  • 支持使用SecretProviderClass 自定义资源定义(CRD)实现 Pod 的可移植性。
  • 支持 Windows 容器。
  • 与 Kubernetes Secrets 同步。
  • 支持自动旋转已挂载内容和已同步的 Kubernetes 密钥。

限制

  • 轮换机密时,将 ConfigMap 或 Secret 作为 subPath 卷装载使用的容器不会收到自动更新,这是 Kubernetes 的限制。 若要使更改生效,应用程序需要通过监视文件系统中的更改或通过重启 Pod 来重新加载更改的文件。 有关详细信息,请参阅机密存储 CSI 驱动程序已知限制。
  • 加载项将创建一个在节点资源组(azurekeyvaultsecretsprovider-xxxxx)中命名MC_的托管标识,并自动将其分配给虚拟机规模集。 可使用此托管标识或自己的托管标识访问密钥保管库。 不支持阻止创建身份标识。

先决条件

  • 如果没有 Azure 订阅,可在开始前创建一个试用帐户。
  • 检查 Azure CLI 的版本是否为 2.30.0 或更高版本。 如果它是更早的版本,请安装最新版本。
  • 如果没有 Azure 订阅,可在开始前创建一个试用帐户。

  • 版本 1.6 或更高版本的 Terraform。

  • Azure CLI已安装并登录。安装最新版本。

  • 创建 AKS 和密钥保管库资源的权限。

  • 使用以下命令在Azure CLI中设置Azure订阅。 将 <subscriptionId> 替换为订阅 ID。

    az account set --subscription <subscriptionId>
    

网络

角色

  • Azure CLI工作流使用密钥保管库机密官员角色授予帐户创建机密的权限。 Terraform 示例使用 密钥保管库 管理员角色,并向加载项身份授予 密钥保管库 机密用户角色。
  • 在提供 Azure 密钥保管库 访问权限的文章中,使用的标识需要密钥保管库 证书用户权限来访问或对象类型,以及密钥保管库 机密用户权限来访问对象类型。

创建 AKS 群集

使用 Azure 密钥保管库 提供程序,为 Secrets Store CSI 驱动程序支持创建 AKS 群集。

  1. 创建命令中用于创建 AKS 群集和 密钥保管库 的变量。

    export RANDOM_STRING=$(printf '%05d%05d' "$RANDOM" "$RANDOM")
    export KEYVAULT_NAME=myKeyVault${RANDOM_STRING}
    export RESOURCE_GROUP=myResourceGroup
    export CLUSTER_NAME=myAKSCluster
    export LOCATION=chinanorth3
    

    Azure 密钥保管库 名称必须全局唯一,字母数字(包括连字符)和 3-24 个字符。 密钥保管库名称将KEYVAULT_NAME 变量的值与myKeyVault 变量的 10 个字符字符串连接在一起。

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

    az group create --name $RESOURCE_GROUP --location $LOCATION
    
  3. 使用 az aks create 命令和 --enable-addons azure-keyvault-secrets-provider 参数创建 AKS 群集,该群集具备 Azure 密钥保管库提供程序功能以支持 Secrets Store CSI 驱动程序。

    该 --enable-addons 参数创建一个用户分配的托管标识, azurekeyvaultsecretsprovider-xxxx 该标识可用于对密钥保管库进行身份验证。 托管标识存储在节点资源组(MC_)中,并自动分配给虚拟机规模集。 可使用此托管标识或自己的托管标识访问密钥保管库。 不支持阻止创建身份标识。

    az aks create \
      --name $CLUSTER_NAME \
      --resource-group $RESOURCE_GROUP \
      --enable-addons azure-keyvault-secrets-provider \
      --generate-ssh-keys
    

    Tip

    如果要使用 Microsoft Entra 工作负荷 ID,该 az aks create 命令必须包含 --enable-oidc-issuer 和 --enable-workload-identity 参数。

创建 AKS 群集

创建一个 main.tf 文件,并将以下测试的示例配置复制到其中。 Azure Terraform GitHub存储库维护示例。 此示例创建资源组、AKS 群集、Azure 密钥保管库、示例机密和所需的角色分配。

terraform {
  required_version = ">= 1.5.0"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.116"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }
}

provider "azurerm" {
  features {
    resource_group {
      prevent_deletion_if_contains_resources = false
    }
  }
}

data "azurerm_client_config" "current" {}

resource "random_string" "suffix" {
  length  = 6
  upper   = false
  special = false
}

locals {
  location            = "chinanorth3"
  resource_group_name = "rg-aks-kv-csi-demo-${random_string.suffix.result}"
  aks_name            = "aks-kv-csi-demo"
  key_vault_name      = "kvcsidemo${random_string.suffix.result}"
  secret_name         = "ExampleSecret"
}

resource "azurerm_resource_group" "this" {
  name     = local.resource_group_name
  location = local.location
}

resource "azurerm_log_analytics_workspace" "this" {
  name                = "law-aks-kv-csi-demo"
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name
  sku                 = "PerGB2018"
  retention_in_days   = 30
}

resource "azurerm_kubernetes_cluster" "this" {
  name                      = local.aks_name
  location                  = azurerm_resource_group.this.location
  resource_group_name       = azurerm_resource_group.this.name
  dns_prefix                = "akskvcsidemo"
  kubernetes_version        = null
  oidc_issuer_enabled       = true
  workload_identity_enabled = true

  default_node_pool {
    name       = "system"
    node_count = 1
    vm_size    = "Standard_D2s_v3"

    upgrade_settings {
      drain_timeout_in_minutes      = 0
      max_surge                     = "10%"
      node_soak_duration_in_minutes = 0
    }
  }

  identity {
    type = "SystemAssigned"
  }

  key_vault_secrets_provider {
    secret_rotation_enabled = false
  }

  oms_agent {
    log_analytics_workspace_id = azurerm_log_analytics_workspace.this.id
  }

  network_profile {
    network_plugin    = "azure"
    load_balancer_sku = "standard"
  }

  tags = {
    scenario = "aks-keyvault-csi"
  }
}

resource "azurerm_key_vault" "this" {
  name                          = local.key_vault_name
  location                      = azurerm_resource_group.this.location
  resource_group_name           = azurerm_resource_group.this.name
  tenant_id                     = data.azurerm_client_config.current.tenant_id
  sku_name                      = "standard"
  purge_protection_enabled      = false
  soft_delete_retention_days    = 7
  enable_rbac_authorization     = true
  public_network_access_enabled = true
}

resource "azurerm_role_assignment" "current_user_kv_admin" {
  scope                = azurerm_key_vault.this.id
  role_definition_name = "Key Vault Administrator"
  principal_id         = data.azurerm_client_config.current.object_id
}

resource "azurerm_key_vault_secret" "example" {
  name         = local.secret_name
  value        = "HelloFromKeyVault"
  key_vault_id = azurerm_key_vault.this.id

  depends_on = [
    azurerm_role_assignment.current_user_kv_admin
  ]
}

resource "azurerm_role_assignment" "aks_csi_secrets_user" {
  scope                = azurerm_key_vault.this.id
  role_definition_name = "Key Vault Secrets User"
  # This is the managed identity automatically created by the AKS Key Vault CSI addon.
  principal_id = azurerm_kubernetes_cluster.this.key_vault_secrets_provider[0].secret_identity[0].object_id
}

output "resource_group_name" {
  value = azurerm_resource_group.this.name
}

output "aks_cluster_name" {
  value = azurerm_kubernetes_cluster.this.name
}

output "key_vault_name" {
  value = azurerm_key_vault.this.name
}

output "key_vault_secret_name" {
  value = azurerm_key_vault_secret.example.name
}

output "tenant_id" {
  value = data.azurerm_client_config.current.tenant_id
}

output "aks_csi_client_id" {
  value = azurerm_kubernetes_cluster.this.key_vault_secrets_provider[0].secret_identity[0].client_id
}

output "aks_csi_object_id" {
  value = azurerm_kubernetes_cluster.this.key_vault_secrets_provider[0].secret_identity[0].object_id
}
  1. 部署配置。 在 Bash 会话中运行以下命令来部署资源:

    terraform init
    terraform validate
    terraform plan
    terraform apply
    
  2. 根据 Terraform 的输出结果设置其余命令中使用的变量:

    export RESOURCE_GROUP=$(terraform output -raw resource_group_name)
    export CLUSTER_NAME=$(terraform output -raw aks_cluster_name)
    export KEYVAULT_NAME=$(terraform output -raw key_vault_name)
    

更新现有的 AKS 群集

为支持机密存储 CSI 驱动程序,使用 Azure 密钥保管库 提供程序更新现有的 AKS 群集。

  1. 创建命令中使用的变量。 根据需要替换值以更新现有 AKS 群集或 密钥保管库。

    例如,如果使用现有密钥保管库,请替换 KEYVAULT_NAME 变量的值而不使用该 RANDOM_STRING 变量。

    如果没有密钥保管库,Azure 密钥保管库 名称必须全局唯一,字母数字(包括连字符)和 3-24 个字符。 密钥保管库名称将KEYVAULT_NAME 变量的值与myKeyVault 变量的 10 个字符字符串连接在一起。 可以稍后在本文中创建密钥保管库。

    export RANDOM_STRING=$(printf '%05d%05d' "$RANDOM" "$RANDOM")
    export KEYVAULT_NAME=myKeyVault${RANDOM_STRING}
    export RESOURCE_GROUP=myResourceGroup
    export CLUSTER_NAME=myAKSCluster
    export LOCATION=chinanorth3
    
  2. 使用 az aks enable-addons 命令更新现有的 AKS 群集以启用 Secrets Store CSI 驱动程序的 Azure 密钥保管库 提供程序功能,并启用 azure-keyvault-secrets-provider 加载项。 加载项会创建用户分配的托管标识,你可以使用该标识向密钥保管库进行身份验证。

    az aks enable-addons \
      --addons azure-keyvault-secrets-provider \
      --name $CLUSTER_NAME \
      --resource-group $RESOURCE_GROUP
    

    启用 Azure 密钥保管库 机密提供程序后,AKS 会创建一个托管 azurekeyvaultsecretsprovider-xxxx 标识,该标识可用于向密钥保管库进行身份验证。 托管标识存储在节点资源组(MC_)中,并自动分配给虚拟机规模集。 可使用此托管标识或自己的托管标识访问密钥保管库。 不支持阻止创建身份标识。

验证托管标识和密钥保管库提供程序安装

以下命令使用 $CLUSTER_NAME 和 $RESOURCE_GROUP 变量。 Azure CLI工作流之前定义这些变量,Terraform 工作流在部署后从输出中设置这些变量。

验证托管标识

使用以下步骤验证是否已创建托管标识并将其分配给集群的虚拟机规模集。

  1. 使用 az aks show 命令验证是否已创建并分配给群集的托管标识。

    az aks show \
      --name $CLUSTER_NAME \
      --resource-group $RESOURCE_GROUP \
      --query addonProfiles
    
    {
      "azureKeyvaultSecretsProvider": {
        "config": {
          "enableSecretRotation": "false",
          "rotationPollInterval": "2m"
        },
        "enabled": true,
        "identity": {
          "clientId": "00001111-aaaa-2222-bbbb-3333cccc4444",
          "objectId": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
          "resourceId": "/subscriptions/<subscriptionID>/resourcegroups/MC_myResourceGroup_myAKSCluster_eastus2/providers/Microsoft.ManagedIdentity/userAssignedIdentities/azurekeyvaultsecretsprovider-myakscluster"
        }
      }
    }
    

    该 resourceId 属性显示资源组和标识的名称 azurekeyvaultsecretsprovider-myakscluster。

  2. 验证托管标识是否已分配给节点资源组中的虚拟机规模集。

    NODE_RG=$(az aks show \
      --name $CLUSTER_NAME \
      --resource-group $RESOURCE_GROUP \
      --query nodeResourceGroup --output tsv)
    
    VMSS_NAME=$(az vmss list \
      --resource-group $NODE_RG \
      --query [].name --output tsv)
    
    az vmss show --name $VMSS_NAME --resource-group $NODE_RG --query '[id, identity]'
    

    输出显示了虚拟机规模集 Microsoft.Compute/virtualMachineScaleSets 的资源 ID,以及 userAssignedIdentities 属性中包含用于确认该标识分配至虚拟机规模集的资源 ID azurekeyvaultsecretsprovider-myakscluster。

验证适用于机密存储 CSI 驱动程序的 Azure 密钥保管库 提供程序安装

  1. 使用 az aks get-credentials 命令获取 AKS 群集凭据。

    az aks get-credentials \
      --name $CLUSTER_NAME \
      --resource-group $RESOURCE_GROUP
    
  2. 使用kubectl get pods命令验证安装是否完成,该命令会列出命名空间secrets-store-csi-driver中具有secrets-store-provider-azure和kube-system标签的所有 Pods。

    kubectl get pods -n kube-system -l 'app in (secrets-store-csi-driver,secrets-store-provider-azure)' -o wide
    

    该 -o wide 标志会在输出中包括每个 Pod 运行的节点。

    输出应类似于以下示例输出:

    NAME                                     READY   STATUS    RESTARTS   AGE    NODE
    aks-secrets-store-csi-driver-4vpkj       3/3     Running   2          4m25s  aks-nodepool1-12345678-vmss000002
    aks-secrets-store-csi-driver-ctjq6       3/3     Running   2          4m21s  aks-nodepool1-12345678-vmss000001
    aks-secrets-store-csi-driver-tlvlq       3/3     Running   2          4m24s  aks-nodepool1-12345678-vmss000000
    aks-secrets-store-provider-azure-5p4nb   1/1     Running   0          4m21s  aks-nodepool1-12345678-vmss000000
    aks-secrets-store-provider-azure-6pqmv   1/1     Running   0          4m24s  aks-nodepool1-12345678-vmss000001
    aks-secrets-store-provider-azure-f5qlm   1/1     Running   0          4m25s  aks-nodepool1-12345678-vmss000002
    

创建新的密钥保管库

az keyvault create运行该命令,创建启用了 Azure RBAC 的新密钥保管库。

az keyvault create \
  --name $KEYVAULT_NAME \
  --resource-group $RESOURCE_GROUP \
  --location $LOCATION \
  --enable-rbac-authorization

创建新的密钥保管库时,即使不包含 --enable-rbac-authorization 该参数,Azure RBAC 也默认启用。

有关 密钥保管库 权限模型和 Azure RBAC 的详细信息,请参阅使用 Azure 基于角色的访问控制对 密钥保管库 密钥、证书和保密信息的访问。

更新现有密钥保管库

运行 az keyvault update 命令来使用 Azure 基于角色的访问控制(Azure RBAC)更新现有的密钥保管库。 更新已禁用 Azure RBAC 的现有密钥保管库时,需要使用 --enable-rbac-authorization 参数来启用 Azure RBAC。

az keyvault update \
  --name $KEYVAULT_NAME \
  --resource-group $RESOURCE_GROUP \
  --enable-rbac-authorization

有关 密钥保管库 权限模型和 Azure RBAC 的详细信息,请参阅 使用 Azure 基于角色的访问控制提供对 密钥保管库 密钥、证书和机密的访问权限

将角色分配和机密添加到密钥保管库

  1. az keyvault show运行以下命令来验证密钥保管库是否已启用 Azure RBAC。

    az keyvault show \
      --name $KEYVAULT_NAME \
      --resource-group $RESOURCE_GROUP \
      --query properties.enableRbacAuthorization
    

    输出应为 true.

  2. 使用 az role assignment create 命令将用户帐户的角色分配添加到密钥保管库范围,以便在下一步中添加密钥保管库机密。

    添加了具有唯一标识符的 b86a8fe4-44ce-4948-aee5-eccb2c155cd7角色,可以使用名称或唯一标识符。 使用角色的唯一标识符是防止角色名称更改时出现问题的最佳做法。

    KEYVAULT_ID=$(az keyvault show \
      --name $KEYVAULT_NAME \
      --resource-group $RESOURCE_GROUP \
      --query id -o tsv)
    
    MYID=$(az ad signed-in-user show --query id --output tsv)
    
    az role assignment create \
      --assignee-object-id $MYID \
      --role "b86a8fe4-44ce-4948-aee5-eccb2c155cd7" \
      --scope $KEYVAULT_ID \
      --assignee-principal-type User
    

    角色分配可能需要几分钟才能生效。 可以使用以下命令验证角色分配是否已创建:

    az role assignment list \
      --assignee-object-id $MYID \
      --scope $KEYVAULT_ID \
      --query '[].{Role:roleDefinitionName, Scope:scope}' \
      --output table
    
  3. 使用ExampleSecret命令在密钥保管库中创建一个纯az keyvault secret set文本机密。

    密钥保管库可以存储密钥、机密和证书。 该 value 参数使用 RANDOM_STRING 变量为机密创建唯一值。

    az keyvault secret set \
      --vault-name $KEYVAULT_NAME \
      --name ExampleSecret \
      --value MyAKSExampleSecret${RANDOM_STRING}
    
  4. 使用 az keyvault secret show 命令验证是否已将机密添加到密钥保管库。

    az keyvault secret show --vault-name $KEYVAULT_NAME --name ExampleSecret
    

创建新的密钥保管库

前文提供的 Terraform 示例会创建一个已启用 Azure RBAC 的新密钥保管库,为其分配所需的角色,并创建 ExampleSecret。 不需要单独的配置更新。

  1. 从 Terraform 输出中获取密钥保管库名称。

    KEYVAULT_NAME=$(terraform output -raw key_vault_name)
    
  2. 使用az keyvault secret show命令验证ExampleSecret是否已添加到密钥保管库。

    az keyvault secret show \
     --vault-name $KEYVAULT_NAME \
     --name ExampleSecret
    

清理资源

如果要转到下一篇文章并需要这些资源,请忽略以下步骤。 否则,如果已完成并且不打算继续阅读下一篇文章,则应删除本文中创建的资源,以避免不必要的成本。

  1. 从本地 .kube/config 文件中删除集群凭据。

    KUBE_CONTEXT=$(kubectl config current-context)
    kubectl config delete-context $KUBE_CONTEXT
    
  2. 使用命令删除资源组及其中的所有资源,包括节点资源组 (MC_) az group delete 中的资源。

    az group delete --name $RESOURCE_GROUP --yes --no-wait
    

该 terraform destroy 命令删除当前 Terraform 配置和状态文件中定义的所有资源。 仅从本文使用的工作目录中运行此命令。

Warning

如果使用的是现有资源或生产资源,请在运行之前仔细查看执行计划:

terraform plan -destroy

避免针对共享或导入的基础结构运行 terraform destroy ,除非确定可以安全删除。 有关详细信息,请参阅 Terraform destroy 命令 的 Terraform 文档。

  1. 从本地 .kube/config 文件中删除集群凭据。

    KUBE_CONTEXT=$(kubectl config current-context)
    kubectl config delete-context $KUBE_CONTEXT
    
  2. 运行以下命令以删除本文中创建的资源:

    terraform destroy
    

后续步骤

本文介绍了如何将适用于机密存储 CSI 驱动程序的 Azure 密钥保管库 提供程序用于 AKS 群集。 Terraform 示例授予加载项标识对密钥保管库中的机密的访问权限。 若要配置 SecretProviderClass 或使用其他标识,请继续阅读下一篇文章。