用于机密存储容器存储接口(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 密钥保管库。
- 如果群集具有出站类型
userDefinedRouting并使用可基于域名(如 Azure 防火墙)控制出站流量的防火墙设备,请确保 允许所需的出站网络规则和 FQDN。 - 如果将 Ingress 限制在群集中,请确保端口 9808 和 8095 处于打开状态。
角色
- Azure CLI工作流使用密钥保管库机密官员角色授予帐户创建机密的权限。 Terraform 示例使用 密钥保管库 管理员角色,并向加载项身份授予 密钥保管库 机密用户角色。
- 在
提供 Azure 密钥保管库 访问权限 的文章中,使用的标识需要密钥保管库 证书用户 权限来访问或 对象类型 ,以及密钥保管库 机密用户 权限来访问对象类型 。
创建 AKS 群集
使用 Azure 密钥保管库 提供程序,为 Secrets Store CSI 驱动程序支持创建 AKS 群集。
创建命令中用于创建 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=chinanorth3Azure 密钥保管库 名称必须全局唯一,字母数字(包括连字符)和 3-24 个字符。 密钥保管库名称将
KEYVAULT_NAME变量的值与myKeyVault变量的 10 个字符字符串连接在一起。使用
az group create命令创建 Azure 资源组。az group create --name $RESOURCE_GROUP --location $LOCATION使用
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-keysTip
如果要使用 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
}
部署配置。 在 Bash 会话中运行以下命令来部署资源:
terraform init terraform validate terraform plan terraform apply根据 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 群集。
创建命令中使用的变量。 根据需要替换值以更新现有 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使用
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 工作流在部署后从输出中设置这些变量。
验证托管标识
使用以下步骤验证是否已创建托管标识并将其分配给集群的虚拟机规模集。
使用
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。验证托管标识是否已分配给节点资源组中的虚拟机规模集。
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属性中包含用于确认该标识分配至虚拟机规模集的资源 IDazurekeyvaultsecretsprovider-myakscluster。
验证适用于机密存储 CSI 驱动程序的 Azure 密钥保管库 提供程序安装
使用
az aks get-credentials命令获取 AKS 群集凭据。az aks get-credentials \ --name $CLUSTER_NAME \ --resource-group $RESOURCE_GROUP使用
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 基于角色的访问控制提供对 密钥保管库 密钥、证书和机密的访问权限
将角色分配和机密添加到密钥保管库
az keyvault show运行以下命令来验证密钥保管库是否已启用 Azure RBAC。az keyvault show \ --name $KEYVAULT_NAME \ --resource-group $RESOURCE_GROUP \ --query properties.enableRbacAuthorization输出应为
true.使用
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使用
ExampleSecret命令在密钥保管库中创建一个纯az keyvault secret set文本机密。密钥保管库可以存储密钥、机密和证书。 该
value参数使用RANDOM_STRING变量为机密创建唯一值。az keyvault secret set \ --vault-name $KEYVAULT_NAME \ --name ExampleSecret \ --value MyAKSExampleSecret${RANDOM_STRING}使用
az keyvault secret show命令验证是否已将机密添加到密钥保管库。az keyvault secret show --vault-name $KEYVAULT_NAME --name ExampleSecret
创建新的密钥保管库
前文提供的 Terraform 示例会创建一个已启用 Azure RBAC 的新密钥保管库,为其分配所需的角色,并创建 ExampleSecret。 不需要单独的配置更新。
从 Terraform 输出中获取密钥保管库名称。
KEYVAULT_NAME=$(terraform output -raw key_vault_name)使用
az keyvault secret show命令验证ExampleSecret是否已添加到密钥保管库。az keyvault secret show \ --vault-name $KEYVAULT_NAME \ --name ExampleSecret
清理资源
如果要转到下一篇文章并需要这些资源,请忽略以下步骤。 否则,如果已完成并且不打算继续阅读下一篇文章,则应删除本文中创建的资源,以避免不必要的成本。
从本地 .kube/config 文件中删除集群凭据。
KUBE_CONTEXT=$(kubectl config current-context) kubectl config delete-context $KUBE_CONTEXT使用命令删除资源组及其中的所有资源,包括节点资源组 (
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 文档。
从本地 .kube/config 文件中删除集群凭据。
KUBE_CONTEXT=$(kubectl config current-context) kubectl config delete-context $KUBE_CONTEXT运行以下命令以删除本文中创建的资源:
terraform destroy
后续步骤
本文介绍了如何将适用于机密存储 CSI 驱动程序的 Azure 密钥保管库 提供程序用于 AKS 群集。 Terraform 示例授予加载项标识对密钥保管库中的机密的访问权限。 若要配置 SecretProviderClass 或使用其他标识,请继续阅读下一篇文章。