痛点:密钥管理是 K8s 安全最大的短板
生产集群里跑着几十个微服务,数据库密码、API Token、TLS 证书散落在各个 Namespace 的 Secret 对象里。这些 Secret 本质上只是 Base64 编码——不是加密。一旦有人 kubectl get secret -o yaml,所有凭据一览无余。
更致命的问题:
- Secret YAML 被提交到 Git,泄露面不可控
- 密钥轮换需要逐个 Deployment 重启,漏一个就是事故
- 审计困难——谁在什么时候读取了哪个密钥,没有任何记录
如果你的集群还在用 kubectl create secret generic 手动管理凭据,是时候引入集中式 Secrets 管理了。
方案:Vault 做密钥源,ESO 做同步桥梁
架构一句话概括:
HashiCorp Vault(密钥仓库)→ External Secrets Operator(同步控制器)→ Kubernetes Secret(Pod 消费)
Vault 负责存储、加密、审计、轮换;ESO 负责把 Vault 里的密钥自动同步为 K8s 原生 Secret 对象,Pod 无需任何代码改动即可消费。
核心优势:
| 维度 | 传统方式 | Vault + ESO |
|---|---|---|
| 存储安全 | Base64 明文 | AES-256-GCM 加密存储 |
| 轮换 | 手动改 YAML + 重启 | Vault 自动轮换 + ESO 定时同步 |
| 审计 | 无 | 完整 Audit Log,精确到路径和时间 |
| Git 安全 | Secret 泄露风险高 | Git 里只有 ExternalSecret CRD 引用 |
实操步骤
Step 1:部署 Vault 并启用 KV 引擎
用 Helm 部署 Vault 到 K8s:
# 添加 Helm repo
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
# 部署 Vault(开发模式仅用于测试,生产请用 HA 模式)
helm install vault hashicorp/vault \
--namespace vault \
--create-namespace \
--set "server.dev.enabled=true"
# 等待 Pod Ready
kubectl -n vault wait --for=condition=Ready pod/vault-0 --timeout=120s
启用 KV v2 引擎并写入测试密钥:
# 进入 Vault Pod 执行
kubectl -n vault exec -it vault-0 -- sh
# 启用 KV 引擎
vault secrets enable -path=kv kv-v2
# 写入一组数据库凭据
vault kv put kv/prod/db-credentials \
username="app_rw" \
password="S3cUr3!P@ssw0rd#2026" \
host="pg-primary.internal:5432"
# 验证读取
vault kv get kv/prod/db-credentials
Step 2:配置 Kubernetes Auth 并创建策略
让 ESO 能以 ServiceAccount 身份向 Vault 认证:
# 还是在 Vault Pod 内操作
# 启用 Kubernetes Auth
vault auth enable kubernetes
# 配置 K8s Auth(自动检测集群内信息)
vault write auth/kubernetes/config \
kubernetes_host="https://${KUBERNETES_PORT_443_TCP_ADDR}:443"
# 创建读取策略
vault policy write eso-reader - <<EOF
path "kv/data/prod/*" {
capabilities = ["read"]
}
path "kv/metadata/prod/*" {
capabilities = ["read", "list"]
}
EOF
# 绑定角色到 ServiceAccount
vault write auth/kubernetes/role/eso-role \
bound_service_account_names=external-secrets \
bound_service_account_namespaces=external-secrets \
policies=eso-reader \
ttl=1h
Step 3:部署 ESO 并创建 ExternalSecret
安装 External Secrets Operator:
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace \
--set installCRDs=true
创建 ClusterSecretStore(集群级密钥源配置):
# cluster-secret-store.yaml
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "http://vault.vault.svc:8200"
path: "kv"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "eso-role"
serviceAccountRef:
name: "external-secrets"
namespace: "external-secrets"
创建 ExternalSecret(这个文件可以安全提交到 Git):
# external-secret-db.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: app-prod
spec:
refreshInterval: 5m # 每 5 分钟同步一次,密钥轮换自动生效
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: db-credentials # 生成的 K8s Secret 名称
creationPolicy: Owner
data:
- secretKey: DB_USERNAME
remoteRef:
key: prod/db-credentials
property: username
- secretKey: DB_PASSWORD
remoteRef:
key: prod/db-credentials
property: password
- secretKey: DB_HOST
remoteRef:
key: prod/db-credentials
property: host
应用并验证:
kubectl apply -f cluster-secret-store.yaml
kubectl apply -f external-secret-db.yaml
# 检查同步状态
kubectl -n app-prod get externalsecret db-credentials
# 应显示 STATUS=SecretSynced
# 确认 K8s Secret 已自动生成
kubectl -n app-prod get secret db-credentials -o jsonpath='{.data.DB_PASSWORD}' | base64 -d
Pod 直接通过 envFrom 或 volumeMount 消费这个 Secret,零代码改动:
# deployment 片段
envFrom:
- secretRef:
name: db-credentials
避坑指南
1. 生产环境不要用 Dev 模式
Vault Dev 模式数据存内存,重启即丢失。生产部署必须: - 使用 Raft 或 Consul 作为存储后端 - 配置 Auto Unseal(推荐 AWS KMS / GCP Cloud KMS) - 至少 3 节点 HA
# 生产 Helm values 示例
helm install vault hashicorp/vault \
--set "server.ha.enabled=true" \
--set "server.ha.replicas=3" \
--set "server.ha.raft.enabled=true"
2. refreshInterval 不要设太短
ESO 每次同步都会调用 Vault API。如果有 500 个 ExternalSecret 且 refreshInterval=30s,意味着每分钟 1000 次请求打到 Vault。建议:
- 常规密钥:
15m~1h - 高频轮换的临时凭据(如 AWS STS):
5m - 配合 Vault 的
max_lease_ttl统一规划
3. RBAC 最小权限原则
不要给 ESO 的 ServiceAccount 赋予 Vault root policy。按 Namespace 划分路径:
kv/data/prod/* → prod 集群 ESO
kv/data/staging/* → staging 集群 ESO
每个环境的 Role 只能读自己路径下的密钥,互不越权。
总结
| 步骤 | 动作 | 耗时 |
|---|---|---|
| 1 | 部署 Vault + 启用 KV 引擎 | 10 min |
| 2 | 配置 K8s Auth + 策略 | 5 min |
| 3 | 部署 ESO + 创建 ExternalSecret | 5 min |
全程 20 分钟即可跑通。之后团队只需关注两件事:
- 密钥写到 Vault — 通过 Vault CLI/API/UI,或 Terraform vault provider 自动化
- ExternalSecret CRD 提交到 Git — 安全、可审计、可回滚
从此 Git 仓库里再也不会出现明文密钥,密钥轮换不再需要逐个重启 Pod,所有访问都有审计日志。这才是云原生环境下密钥管理该有的样子。