这篇文章演示如何在 AKS中启用 Azure Key Vault Secrets Provider add-on,把 Azure Key Vault 中的证书挂载到 Pod,并同步成原生的 Kubernetes TLS Secret。
Secrets Store CSI Driver 支持将 Key Vault 中的
secret、key、certificate挂载到 Pod,也支持同步成 Kubernetes Secret,并支持自动轮换。
本文使用的方式是:
AKS add-on 创建的托管标识 + SecretProviderClass + Pod 挂载触发同步
一、前置条件
开始前请确保具备以下条件:
- 已安装 Azure CLI ,版本建议为 2.30.0 或以上。
- 已安装 kubectl ,本地可使用
az aks install-cli安装。 - 已有一个可用的 AKS 集群。
- 已有一个 Azure Key Vault ,并且其中已存在要读取的证书对象。若你要把证书同步成
kubernetes.io/tlsSecret,建议先将证书导入到 Key Vault。
安装 kubectl:
bash
az aks install-cli
连接到 AKS 集群:
bash
az aks get-credentials --resource-group <resource-group> --name <cluster-name>
二、启用 Azure Key Vault Secrets Provider add-on
在现有 AKS 集群上启用 add-on:
bash
az aks enable-addons \
--addons azure-keyvault-secrets-provider \
--name <cluster-name> \
--resource-group <resource-group>
启用后,AKS 会自动部署 Secrets Store CSI Driver 和 Azure Key Vault Provider ,并在 节点资源组 中创建一个名为 azurekeyvaultsecretsprovider-xxxxx 的用户分配托管标识,再自动绑定到 VMSS。这个托管标识后续可用于访问 Key Vault。
你可以先确认 add-on identity 信息:
bash
az aks show \
--resource-group <resource-group> \
--name <cluster-name> \
--query addonProfiles.azureKeyvaultSecretsProvider.identity
三、为托管标识授予 Key Vault 访问权限
这里要区分两个概念:
clientId:写入SecretProviderClass.parameters.userAssignedIdentityIDobjectId:用于给这条托管标识授予 Key Vault 访问权限
获取 clientId,后面会在 SecretProviderClass 中使用:
bash
export IDENTITY_CLIENT_ID="$(az aks show \
--resource-group <resource-group> \
--name <cluster-name> \
--query addonProfiles.azureKeyvaultSecretsProvider.identity.clientId \
-o tsv)"
获取 objectId,用于在 Azure 侧授权:
bash
export IDENTITY_OBJECT_ID="$(az aks show \
--resource-group <resource-group> \
--name <cluster-name> \
--query addonProfiles.azureKeyvaultSecretsProvider.identity.objectId \
-o tsv)"
获取 Key Vault 的资源 ID:
bash
export KEYVAULT_SCOPE="$(az keyvault show \
--name <key-vault-name> \
--query id -o tsv)"
如果你的Key Vault 使用的是RBAC权限模型,则应授予 Key Vault Secrets User。
bash
az role assignment create \
--assignee-object-id $IDENTITY_OBJECT_ID \
--assignee-principal-type ServicePrincipal \
--role "Key Vault Secrets User" \
--scope $KEYVAULT_SCOPE
如果你的 Key Vault 使用的是Access Policy 权限模型,则可以改用以下方式授权:
bash
az keyvault set-policy \
--name <key-vault-name> \
--object-id $IDENTITY_OBJECT_ID \
--secret-permissions get
到这里,Azure 侧的身份和权限已经准备完成。接下来回到 Kubernetes 侧,创建 SecretProviderClass,告诉 CSI Driver:去哪个 Key Vault、用哪个身份、读取哪个对象,以及是否同步成原生 Kubernetes Secret。
四、创建 SecretProviderClass
SecretProviderClass 是 CSI Driver 的核心配置对象,用来定义:
- 访问哪个 Key Vault
- 使用哪个身份
- 拉取哪些对象
- 是否同步为 Kubernetes Secret
下面创建一个 SecretProviderClass,把 Key Vault 中的证书同步成一个名为 web-tls 的原生 Kubernetes TLS Secret:
yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: azure-tls
namespace: argocd
spec:
provider: azure
secretObjects:
- secretName: web-tls
type: kubernetes.io/tls
data:
- objectName: mycert
key: tls.key
- objectName: mycert
key: tls.crt
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true"
userAssignedIdentityID: "<client-id>"
keyvaultName: "<key-vault-name>"
tenantId: "<tenant-id>"
objects: |
array:
- |
objectName: mycert
objectType: secret
这里有两个关键点:
secretObjects定义了要同步出来的 Kubernetes Secret,名称是web-tls,类型是kubernetes.io/tls。objectType: secret是本文最关键的地方。对于 TLS Secret 场景,这样才能从 Key Vault 中取到证书和私钥,再映射到tls.crt和tls.key。
其中,userAssignedIdentityID 这里填的就是上一步获取到的 IDENTITY_CLIENT_ID。
应用资源:
bash
kubectl apply -f secretproviderclass.yaml
不过需要注意,SecretProviderClass 只是定义好了"怎么取",它本身不会自动触发同步。真正的拉取和同步动作,要在 Pod 或 Deployment 通过 CSI volume 引用它之后才会发生。
五、通过 Pod 挂载触发同步
为了让 CSI Driver 真正去 Key Vault 拉取对象并同步 Secret,需要有一个 Pod 或 Deployment 显式引用上一步创建的 SecretProviderClass。
下面是一个最小可用的测试 Pod:
yaml
apiVersion: v1
kind: Pod
metadata:
name: kv-sync
namespace: argocd
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh", "-c", "sleep 3600"]
volumeMounts:
- name: kv
mountPath: "/mnt/secrets"
readOnly: true
volumes:
- name: kv
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "azure-tls"
应用资源:
bash
kubectl apply -f pod.yaml
当这个 Pod 启动后,CSI Driver 会根据 SecretProviderClass 中的定义:
- 使用托管标识访问 Azure Key Vault
- 拉取对象
mycert - 将内容挂载到容器内的
/mnt/secrets - 按
secretObjects的定义同步生成web-tls这个 Kubernetes Secret
也就是说,只有 Pod 真正运行并消费了这个 CSI volume 后,web-tls 才会被同步出来。
六、验证是否同步成功
既然同步动作是在 Pod 启动后触发的,那么验证时也应该按这个顺序来:先看 Pod,再看挂载内容,最后看同步出来的 Kubernetes Secret。
先确认 Pod 是否已正常创建:
bash
kubectl get pod kv-sync -n argocd
再查看容器内挂载目录是否已有内容:
bash
kubectl exec -n argocd kv-sync -- ls /mnt/secrets
最后检查同步出来的 Kubernetes Secret:
bash
kubectl get secret web-tls -n argocd
kubectl describe secret web-tls -n argocd
如果 web-tls 成功生成,且类型为 kubernetes.io/tls,说明整条链路已经打通:
AKS add-on identity 已被授权访问 Key Vault,SecretProviderClass 已正确定义对象和同步目标,Pod 也成功触发了挂载与同步。
七、需要注意的几个坑
1. SecretProviderClass 创建了,不代表 Secret 会立刻出现
只有当 Pod / Deployment 真正引用了它,CSI Driver 才会触发拉取和同步。否则你会看到 SecretProviderClass 已存在,但 web-tls 一直没有生成。
2. userAssignedIdentityID 如果填错,Pod 往往会挂载失败
这里应填写 add-on identity 的 clientId。如果误填成 objectId 或其他资源 ID,CSI Driver 无法按预期使用这条托管标识访问 Key Vault。
3. TLS Secret 场景下,如果把 objectType 写成 cert,通常拿不到 tls.key
因为 kubernetes.io/tls Secret 需要同时包含证书和私钥,而官方 TLS 示例建议使用 objectType: secret 来获取这两个内容。
4. 自动轮换后,Kubernetes Secret 更新了,不代表应用一定自动生效
如果应用通过环境变量读取 Secret,通常仍需要重启 Pod;如果是文件挂载方式,则应用需要自己支持监听文件变化或热加载。
5. 使用 subPath 挂载时,轮换后的内容不会自动刷新
这是 Kubernetes 已知限制,不是 Key Vault Provider 本身的问题。