mcpField
K
keyvault-csi-driver

keyvault-csi-driver

github.com/julianobarbosa云与 DevOps14101 次安装2 天前更新
代码仓库

关于 keyvault-csi-driver

keyvault-csi-driver 是一项指导技能,介绍如何通过 Secrets Store CSI Driver 将 Azure Key Vault 集成到 Kubernetes。它提供配置模式和故障排查说明,面向管理集群机密的团队。

主要功能

  1. 01配置 SecretProviderClass 资源
  2. 02通过 CSI 卷将机密挂载到 Pod
  3. 03将 Key Vault 值同步为 Kubernetes Secret
  4. 04涵盖 API 令牌、多项机密和 TLS
  5. 05排查权限与挂载故障

适用场景

  1. 01为 Kubernetes 工作负载配置 Key Vault 机密
  2. 02排查 403 或找不到机密的问题
  3. 03配置 TLS 证书挂载
  4. 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.md
examples.md
troubleshooting.md
scripts/
create-keyvault-secret.sh
diagnose-csi.sh
grant-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-devkv-example-dev-hlg<WORKLOAD_IDENTITY_CLIENT_ID><TENANT_ID>
example-app-hubkv-example-default<WORKLOAD_IDENTITY_CLIENT_ID><TENANT_ID>
example-app-prdkv-example-prd<WORKLOAD_IDENTITY_CLIENT_ID><TENANT_ID>
example-app2-devkv-example2-dev检查集群身份<TENANT_ID>
example-app2-prdkv-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.yaml
  • argo-cd-helm-values/kube-addons/external-dns/example-app-dev/secretproviderclass.yaml
  • argo-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

重要说明

  1. 必须挂载 CSI 卷:即使使用 secretObjects 同步到 K8s 机密,Pod 也必须挂载 CSI 卷。

  2. 机密名称:Key Vault 机密名称区分大小写。请使用完全匹配的名称。

  3. 对象别名:挂载时使用 objectAlias 设置适用于文件系统的名称。

  4. 命名空间范围:SecretProviderClass 的作用范围为命名空间。每个需要使用它的命名空间都要创建一个。

  5. RBAC 与访问策略:检查 Key Vault 授权模型:

    az keyvault show --name "<kv>" --query "properties.enableRbacAuthorization"
    

详细参考

完整的实现示例和架构说明:


注意事项

  • 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