Kubernetes 的基础设施 provisioning 已经高度自动化——ArgoCD、Terraform、Crossplane 把集群和命名空间像流水线一样产出。但秘密管理却常常卡在手动复制和 YAML 粘贴的阶段。当组织把开发、预发布、生产拆到不同集群、命名空间甚至云账户之后,秘密的"蔓延"问题就彻底失控了:同一个数据库密码被复制到十几个 namespace,轮换时漏掉一个就等于没轮换。
原生 Secret 的天花板
Kubernetes 原生 Secret 只是一个 base64 编码的键值对,没有轮换机制,没有审计日志,也没有跨集群同步能力。单集群里勉强够用,一旦跨账户、跨集群部署,你面对的是:
- 每个集群独立存储一份相同的秘密,轮换时需要逐个更新
- 没有统一的权限边界——谁能读、谁能写散落在各集群的 RBAC 里
- GitOps 仓库里出现 base64 编码的"秘密",安全团队直接报警
External Secrets Operator 的核心思路
External Secrets Operator(ESO)的逻辑很简单:Kubernetes 不应该是秘密的源头,它只是秘密的消费者。真正的源头是 AWS Secrets Manager、HashiCorp Vault、GCP Secret Manager 这些专业服务。ESO 定期从外部源拉取秘密,写入本地 Secret 对象,让应用像往常一样挂载使用。
关键组件:
- SecretStore:定义外部秘密源的连接方式和认证信息(每个 namespace 或集群一份)
- ExternalSecret:声明"我要从外部源取哪个秘密,映射到本地 Secret 的哪个 key"
- ClusterSecretStore:集群级别的 SecretStore,跨 namespace 共用
实战:多账户场景下的 ESO 配置
下面是一个典型的多账户 AWS 场景:生产账户的 Secrets Manager 存着真实凭据,开发集群通过 ESO 跨账户拉取(或拉取开发版本的凭据)。
1. 安装 ESO
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets \\n external-secrets/external-secrets \\n -n external-secrets-system \\n --create-namespace
2. 配置跨账户 SecretStore
假设生产集群在 AWS 账户 A,需要读取账户 B 的 Secrets Manager,或者反过来——开发集群需要读取生产账户的只读凭据。关键是 IAM 跨账户角色信任:
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: aws-prod-secretstore
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
role: arn:aws:iam::PROD_ACCOUNT_ID:role/cross-account-eso-role
auth:
jwt:
serviceAccountRef:
name: eso-sa
namespace: external-secrets-system
对应的 IAM 角色需要信任开发集群的 OIDC provider:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::DEV_ACCOUNT_ID:role/eso-dev-cluster-role"
},
"Action": "sts:AssumeRole"
}
]
}
3. 声明 ExternalSecret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: my-app
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-prod-secretstore
kind: ClusterSecretStore
target:
name: db-credentials-secret
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: prod/db/credentials
property: username
- secretKey: password
remoteRef:
key: prod/db/credentials
property: password
应用 Pod 的 Deployment 完全不用改——它仍然挂载 db-credentials-secret 这个原生 Secret。ESO 在背后定期刷新,密码轮换后最多 1 小时自动同步到集群。
4. 多集群同步策略
如果你有 ArgoCD 或 Flux 管理多集群,ExternalSecret 和 SecretStore 可以作为 GitOps 仓库的一部分统一分发:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: external-secrets-config
namespace: argocd
spec:
source:
repoURL: https://github.com/your-org/k8s-secrets-config
path: external-secrets
destination:
server: https://kubernetes.default.svc
namespace: external-secrets-system
syncPolicy:
automated:
prune: true
selfHeal: true
每个集群的 SecretStore 用不同的 role ARN 指向对应账户,ExternalSecret 声明保持一致——同一个 Git 仓库,不同集群自动拿到正确账户的秘密。
落地前的取舍清单
| 决策点 | 建议 |
|---|---|
| 外部秘密源选什么 | AWS 环境优先 Secrets Manager;混合云或多云选 Vault |
| refreshInterval 多长 | 生产环境 15m–1h;开发环境可以 5m 方便调试 |
| 用 ClusterSecretStore 还是 namespace 级别 | 跨 namespace 共用选 Cluster;严格隔离选 namespace 级别 |
| 删除 ExternalSecret 后本地 Secret 会不会残留 | creationPolicy: Owner 时 ESO 会自动清理;Orphan 则保留 |
| 和 GitOps 冲突吗 | 不冲突——SecretStore 和 ExternalSecret 是 CRD,可以 GitOps 管理;同步出的原生 Secret 不要放进 Git |
ESO 不是银弹。它解决的是"秘密从哪来、怎么同步"的问题,但不解决"谁有权访问外部源"的 IAM 治理。跨账户角色信任链需要和云安全团队一起设计。另外,如果你的 Vault 或 Secrets Manager 本身就配置混乱,ESO 只会把混乱更快地搬运到 Kubernetes 里——先治理源头,再接入 ESO。