关于 keyvault-csi-driver
keyvault-csi-driver 是一项指导技能,介绍如何通过 Secrets Store CSI Driver 将 Azure Key Vault 集成到 Kubernetes。它提供配置模式和故障排查说明,面向管理集群机密的团队。
主要功能
- 01配置 SecretProviderClass 资源
- 02通过 CSI 卷将机密挂载到 Pod
- 03将 Key Vault 值同步为 Kubernetes Secret
- 04涵盖 API 令牌、多项机密和 TLS
- 05排查权限与挂载故障
适用场景
- 01为 Kubernetes 工作负载配置 Key Vault 机密
- 02排查 403 或找不到机密的问题
- 03配置 TLS 证书挂载
- 04检查 CSI Driver 和 SecretProviderClass 状态
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ julianobarbosa/ claude-code-skills --skill keyvault-csi-driver安装后在 Claude Code 中输入/keyvault-csi-driver确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/architecture.mdexamples.mdtroubleshooting.mdscripts/create-keyvault-secret.shdiagnose-csi.shgrant-keyvault-access.sh详细介绍
Azure Key Vault CSI 驱动技能
概述
本技能介绍如何使用 Secrets Store CSI Driver 将 Azure Key Vault 集成到 Kubernetes 中。贵组织集群中的所有敏感数据都存储在 Azure Key Vault 中,并通过 CSI 驱动访问。
快速参考
环境配置
| 集群 | Key Vault | 托管身份(客户端 ID) | 租户 ID |
|---|---|---|---|
| example-app-dev | kv-example-dev-hlg | <WORKLOAD_IDENTITY_CLIENT_ID> | <TENANT_ID> |
| example-app-hub | kv-example-default | <WORKLOAD_IDENTITY_CLIENT_ID> | <TENANT_ID> |
| example-app-prd | kv-example-prd | <WORKLOAD_IDENTITY_CLIENT_ID> | <TENANT_ID> |
| example-app2-dev | kv-example2-dev | 检查集群身份 | <TENANT_ID> |
| example-app2-prd | kv-example2-prd | 检查集群身份 | <TENANT_ID> |
SecretProviderClass 模板
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: <app>-secrets
namespace: <namespace>
labels:
app.kubernetes.io/name: <app>
app.kubernetes.io/component: secrets
spec:
provider: azure
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true"
userAssignedIdentityID: "<managed-identity-client-id>"
keyvaultName: "<keyvault-name>"
cloudName: "AzurePublicCloud"
tenantId: "<tenant-id>"
objects: |
array:
- |
objectName: "<secret-name-in-keyvault>"
objectType: "secret"
objectAlias: "<ALIAS_FOR_MOUNT>"
# Optional: Sync to Kubernetes Secret
secretObjects:
- secretName: <k8s-secret-name>
type: Opaque
data:
- objectName: "<ALIAS_FOR_MOUNT>"
key: "<key-in-k8s-secret>"
Pod 卷挂载
spec:
containers:
- name: app
volumeMounts:
- name: secrets-store
mountPath: "/mnt/secrets-store"
readOnly: true
volumes:
- name: secrets-store
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "<secretproviderclass-name>"
常见模式
模式 1:简单 API 令牌(例如 Cloudflare)
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: cloudflare-api-token-kv
namespace: external-dns
spec:
provider: azure
secretObjects:
- data:
- key: cloudflare_api_token
objectName: cloudflare-api-token
secretName: cloudflare-api-token
type: Opaque
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true"
userAssignedIdentityID: "<WORKLOAD_IDENTITY_CLIENT_ID>"
keyvaultName: "kv-example-dev-hlg"
objects: |
array:
- |
objectName: cloudflare-api-token
objectType: secret
tenantId: "<TENANT_ID>"
模式 2:将多个机密映射到多个 K8s 机密
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: app-secrets
spec:
provider: azure
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true"
userAssignedIdentityID: "<identity>"
keyvaultName: "<keyvault>"
tenantId: "<tenant>"
objects: |
array:
- |
objectName: "app-db-password"
objectType: "secret"
objectAlias: "DB_PASSWORD"
- |
objectName: "app-redis-password"
objectType: "secret"
objectAlias: "REDIS_PASSWORD"
secretObjects:
- secretName: app-db-secret
type: Opaque
data:
- objectName: "DB_PASSWORD"
key: "password"
- secretName: app-redis-secret
type: Opaque
data:
- objectName: "REDIS_PASSWORD"
key: "password"
模式 3:TLS 证书
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: tls-cert-provider
spec:
provider: azure
secretObjects:
- secretName: tls-secret
type: kubernetes.io/tls
data:
- objectName: tls-cert
key: tls.crt
- objectName: tls-key
key: tls.key
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true"
userAssignedIdentityID: "<identity>"
keyvaultName: "<keyvault>"
tenantId: "<tenant>"
objects: |
array:
- |
objectName: my-certificate
objectType: cert
objectAlias: tls-cert
- |
objectName: my-certificate
objectType: secret
objectAlias: tls-key
文件位置
SecretProviderClass 文件存储在:
argo-cd-helm-values/kube-addons/<application>/<cluster>/secretproviderclass.yaml
示例:
argo-cd-helm-values/kube-addons/defectdojo/example-app-dev/secretproviderclass.yamlargo-cd-helm-values/kube-addons/external-dns/example-app-dev/secretproviderclass.yamlargo-cd-helm-values/kube-addons/cert-manager/example-app-dev/csi-cloudflare-api-key.yaml
故障排查
错误:403 Forbidden
原因: 托管身份缺少 Key Vault 权限。
解决方案:
# Get identity info from error message, then:
az keyvault set-policy \
--name "<keyvault-name>" \
--object-id "<object-id-from-error>" \
--secret-permissions get list
# Or for RBAC-enabled Key Vaults:
az role assignment create \
--role "Key Vault Secrets User" \
--assignee-object-id "<object-id>" \
--assignee-principal-type ServicePrincipal \
--scope "/subscriptions/.../Microsoft.KeyVault/vaults/<kv-name>"
错误:找不到机密
原因: 机密名称不存在或大小写不匹配。
解决方案:
# List secrets (names are case-sensitive)
az keyvault secret list --vault-name "<kv-name>" --query "[].name" -o tsv
错误:未创建 K8s 机密
原因: 尚无 Pod 挂载 CSI 卷。
解决方案: 部署一个挂载该卷的 Pod。只有至少一个 Pod 使用 SecretProviderClass 时,才会创建 K8s 机密。
错误:Pod 卡在 ContainerCreating 状态
诊断:
kubectl describe pod <pod-name> -n <namespace>
kubectl get pods -n kube-system | grep secrets-store
kubectl logs -n kube-system -l app=secrets-store-provider-azure
脚本
授予 Key Vault 权限
# Use the helper script
./scripts/grant-keyvault-permissions.sh
# Or quick manual command
az keyvault set-policy \
--name "kv-example-dev-hlg" \
--object-id "<object-id>" \
--secret-permissions get list
在 Key Vault 中创建机密
az keyvault secret set \
--vault-name "kv-example-dev-hlg" \
--name "my-app-secret" \
--value "secret-value"
列出所有 SecretProviderClass
kubectl get secretproviderclass -A
检查 CSI 驱动状态
kubectl get pods -n kube-system | grep secrets-store
重要说明
-
必须挂载 CSI 卷:即使使用
secretObjects同步到 K8s 机密,Pod 也必须挂载 CSI 卷。 -
机密名称:Key Vault 机密名称区分大小写。请使用完全匹配的名称。
-
对象别名:挂载时使用
objectAlias设置适用于文件系统的名称。 -
命名空间范围:SecretProviderClass 的作用范围为命名空间。每个需要使用它的命名空间都要创建一个。
-
RBAC 与访问策略:检查 Key Vault 授权模型:
az keyvault show --name "<kv>" --query "properties.enableRbacAuthorization"
详细参考
完整的实现示例和架构说明:
- references/architecture.md - CSI 驱动架构
- references/examples.md - 实际示例
- references/troubleshooting.md - 扩展故障排查
注意事项
- K8s Secret 要等 Pod 挂载 CSI 卷后才会创建:
secretObjects:的同步是惰性的——没有 Pod 就没有 Secret。直接使用该 Secret(而不自行挂载 CSI 卷)的应用,在首次部署时会失败,直到有同级 Pod 先完成挂载。 - 工作负载标识与 VM 托管标识走的是不同代码路径:
useVMManagedIdentity: "true"会从 IMDS 读取;工作负载标识需要设置usePodIdentity: "false"+useVMManagedIdentity: "false"+ 服务帐户注解。混用这些标志会静默回退到错误的标识。 - **
objects:中的 Key Vault 名称区分大小写,K8s 键名则不区分:**如果在 SPC 中写成mytoken,就无法找到 KV 中名为MyToken的 Secret。CSI 日志只会显示“secret not found”,不会提示大小写不匹配。 - **RBAC 与访问策略是按保管库区分的,而非按租户:**有些保管库使用访问策略(
az keyvault set-policy),另一些使用 Azure RBAC(Key Vault Secrets User角色)。请先检查properties.enableRbacAuthorization——授予错误类型的权限会返回 403,且错误文本相同。 - **TLS 证书需要两个
objectType条目:**要将证书挂载为tls.crt+tls.key,需要同时配置objectType: cert(公钥)和objectType: secret(包含私钥的完整 PEM),且两者都指向同一个 KV 证书名称。缺少其中一项会导致 Ingress TLS 静默失效。 - **CSI 轮换默认是选择启用且仅在部署时执行:**除非在驱动程序上设置
--enable-secret-rotation=true并重启 Pod,否则挂载到 Pod 中的 Secret 不会在 KV 值更改时刷新。运行时间较长的 Pod 会继续使用过期的 Secret。
评论
支持 Markdown