跳到主要内容

密钥管理与Vault实践

1. 反面案例

密钥怎么泄露的(真实案例):
- 提交 .env 到 git(grep 历史能挖到)
- 镜像 ENV DATABASE_PASSWORD=xxx(docker history 看到)
- 日志打印 token
- 客户端代码 console.log(secret)
- CI 配置 echo $SECRET 到日志
- Slack 群里贴密钥临时调试

密钥泄露后果:直接破窗 = 数据泄露 / 资金损失 / 信誉。

2. 分级管理

级别存储
公开(不敏感)API_URL、版本号git、config map
内部(不能泄露)第三方 API key、内部 endpointCI Secrets / Vault
高敏感DB 密码、签名私钥、root 凭证Vault + 审计
顶级根 CA、KMS 主密钥HSM / 离线

3. 不能做的事

  • ❌ 写 git 仓库(即使 private)
  • ❌ 写 Docker 镜像 ENV / ARG
  • ❌ 写日志 / 错误堆栈
  • ❌ 命令行参数(ps aux 能看)
  • ❌ 公共聊天工具
  • ❌ 邮件
  • ❌ 客户端代码(前端 JS 任何用户都能看)

4. 推荐做法

4.1 环境变量注入(最低标准)

# K8s
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password

容器内 process.env.DB_PASSWORD 取。

4.2 文件挂载

volumeMounts:
- name: secret
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret
secret:
secretName: db-secret

应用读 /etc/secrets/password。挂载文件能动态更新(环境变量不行)。

4.3 应用主动拉

启动时调 Vault 拉密钥到内存。永远不落盘。

5. K8s Secret

K8s Secret 默认 base64(不是加密)。增强:

5.1 etcd 加密

apiserver 配置 --encryption-provider-config

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources: [secrets]
providers:
- aescbc:
keys:
- name: key1
secret: <32 字节 base64>
- identity: {}

云托管集群(ACK / EKS / GKE)控制台一键开。

5.2 sealed-secrets

加密后可以提交 git:

echo -n 'password' | kubectl create secret generic db --dry-run=client \
--from-file=password=/dev/stdin -o yaml | \
kubeseal --controller-namespace=kube-system -o yaml > db-sealed.yaml

db-sealed.yaml 是密文,安全提交。集群内 controller 解密成 Secret。

5.3 external-secrets-operator(推荐)

把外部 Vault / AWS Secrets Manager 同步到 K8s Secret:

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault
spec:
provider:
vault:
server: "https://vault.example.com"
path: "kv"
auth:
kubernetes:
mountPath: kubernetes
role: my-app

---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-secret
spec:
refreshInterval: 1h
secretStoreRef:
name: vault
kind: SecretStore
target:
name: db-secret
data:
- secretKey: password
remoteRef:
key: prod/db
property: password

6. HashiCorp Vault

企业级密钥管理。

6.1 部署

helm install vault hashicorp/vault \
--set "server.ha.enabled=true" \
--set "server.ha.replicas=3"

6.2 启用 KV 引擎

vault secrets enable -version=2 kv
vault kv put kv/prod/db password="xxx"
vault kv get kv/prod/db

6.3 K8s 认证

vault auth enable kubernetes
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc"

vault policy write my-app - <<EOF
path "kv/data/prod/db" {
capabilities = ["read"]
}
EOF

vault write auth/kubernetes/role/my-app \
bound_service_account_names=my-app \
bound_service_account_namespaces=production \
policies=my-app \
ttl=1h

6.4 Vault Agent Injector

注解触发自动注入:

metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "my-app"
vault.hashicorp.com/agent-inject-secret-db: "kv/data/prod/db"
vault.hashicorp.com/agent-inject-template-db: |
&#125;&#125;- with secret "kv/data/prod/db" -&#125;&#125;
DB_PASSWORD=&#125;&#125; .Data.data.password &#125;&#125;
&#125;&#125;- end &#125;&#125;

容器启动时 sidecar 拉密钥写到 /vault/secrets/db

6.5 动态密钥(高级)

# 启动 database 引擎
vault secrets enable database
vault write database/config/postgres \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://&#125;&#125;username&#125;&#125;:&#125;&#125;password&#125;&#125;@db:5432/app" \
allowed_roles=readonly \
username="vault" \
password="vault-pass"

# 创建 role
vault write database/roles/readonly \
db_name=postgres \
creation_statements="CREATE USER \"&#125;&#125;name&#125;&#125;\" WITH PASSWORD '&#125;&#125;password&#125;&#125;' VALID UNTIL '&#125;&#125;expiration&#125;&#125;'; \
GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"&#125;&#125;name&#125;&#125;\";" \
default_ttl="1h" \
max_ttl="24h"

# 应用动态获取
vault read database/creds/readonly

每次 read 生成短期数据库账号,TTL 后自动撤销。杀手锏

7. 云原生方案

平台服务
AWSSecrets Manager / Parameter Store / KMS
GCPSecret Manager
AzureKey Vault
阿里KMS

集成 K8s 用 external-secrets-operator 同步。

8. Secret Rotation

定期轮换:

  • DB 密码:30-90 天
  • API key:90 天
  • 证书:自动续期(Let's Encrypt 90 天)
  • root key:年级别 + KMS 自动 rotation

应用代码必须支持热加载新密钥(不重启换密码)。

9. 审计

谁什么时候读了哪个密钥要记录:

# Vault audit log
vault audit enable file file_path=/vault/logs/audit.log

可疑访问立即告警(异常时间、异常来源)。

10. 常见反模式

  • .env 提交 git:经典灾难
  • Secret 写 Docker ARG:history 泄露
  • K8s Secret = 安全:base64 不是加密
  • 同一密钥多年不换:泄露后破坏面巨大
  • 生产 staging 共用密钥:staging 泄露 = 生产泄露
  • 应用 log 打印 token:日志聚合到 ELK 后所有人能看
  • Vault root token 长期保留:unseal 后立即吊销
  • 不审计 secret 访问:被偷不知道

11. 延伸阅读