使用外部Secrets Manager管理Kubernetes密钥
Overview
背景
密钥的管理对于使用 GitOps 方式做持续发布是一个挑战,特别是当目标部署平台是 Kubernetes 的时候。 K8S 使用声明式配置管理最终状态,而K8S中的密钥仅仅是将密钥内容做了base64格式的编码。 在基于 Flux 的 GitOps 实战介绍了使用Bitnami Sealed Secrets加密密钥内容, 可以安全的将加密后的Kubernetes Manifest文件提交到Git代码仓库,由Sealed Secrets发现这些SealedSecret的密码, 并解密后动态的创建K8S原生Secrets对象。
SealedSecret 解决了如何在 Git 代码仓库中安全的保存密钥的痛点,但是该方式仍然需要系统管理员自行的妥善保存 SealedSecret 使用的私钥,以及如何从灾难中恢复的场景。此外,整个密钥的生命周期管理在K8S集群内部, 无法让集群外的工作负载安全有效的使用这些密钥,例如,云厂商上托管的 RDS 类型数据库。
使用外部密钥服务管理K8S密钥
在 CNCF 基金会在2021年做的一份关于密钥管理的技术雷达报告上指出, AWS Secrets Manager, HashiCorp Vault 被列为成熟的密钥管理服务或方案。 如果可以在 Kubernetes 中使用这些成熟的密钥服务或方案来管理密钥将可以同时获得密钥服务安全功能强大和 Kubernetes 任务编排的多重收益。
External Secrets Operator(ESO)
针对以上不足之处,接下来介绍的 External Secrets Operator 将按这个思路解决这些问题。
External Secrets Operator 是一个 Kubernetes Operator,它集成了外部密钥管理系统, 例如 AWS Secrets Manager、HashiCorp Vault、Google Secrets Manager、Azure Key Vault 等等。 他使用外部密钥管理服务的 API 读取信息并自动将值注入 Kubernetes Secret。
以上是 External Secrets Operator 的简介,看了以后是不是觉得特别眼熟。他跟同时 CNCF 下另一个 DNS 解析服务External DNS非常的类似,为 Kubernetes 内的域名解析注册提供统一的实现体验, 同其他众多第三方成熟的 DNS 解析集成。
下面将介绍如何在使用 FluxCD 管理 External Secrets Operator,以及在 EKS 中使用 AWS Secrets Manager 管理的密钥。
FluxCD 部署 External Secrets Operator
External Secrets Operator 支持使用 Helm 安装,Flux 部署 ESO 同安装其他 Helm Chart 类似。
- 加入 ESO 的 Helm 仓库
1apiVersion: source.toolkit.fluxcd.io/v1beta1
2kind: HelmRepository
3metadata:
4 name: external-secrets
5spec:
6 interval: 10m
7 url: https://charts.external-secrets.io
- 通过 HelmRelease 部署 ESO
1apiVersion: helm.toolkit.fluxcd.io/v2beta1
2kind: HelmRelease
3metadata:
4 name: external-secrets
5spec:
6 # Override Release name to avoid the pattern Namespace-Release
7 # Ref: https://fluxcd.io/docs/components/helm/api/#helm.toolkit.fluxcd.io/v2beta1.HelmRelease
8 releaseName: external-secrets
9 targetNamespace: kube-system
10 interval: 10m
11 chart:
12 spec:
13 chart: external-secrets
14 sourceRef:
15 kind: HelmRepository
16 name: external-secrets
17 namespace: kube-system
18 values:
19 installCRDs: true
20 serviceAccountName: helm-controller
21 timeout: 5m
22 test:
23 enable: true
24 ignoreFailures: true
25 install:
26 crds: CreateReplace
27 remediation:
28 retries: 3
29 upgrade:
30 crds: CreateReplace
31 remediation:
32 remediateLastFailure: false
为 ESO 创建配置 IRSA
EKS 通过IRSA将 K8S 内 RBAC 的 ServiceAccount 同 IAM role 统一在一起, 可以让K8S内的工作负载通过原生的 ServiceAccount 绑定 IAM Role,无需显示的指定 AccessKey/Secret 来访问 AWS API。
因为 ESO 必须通过 AWS API 访问读取保存在 AWS Secrets Manager 中的密钥。所以需要为 ESO 配置 AWS 访问密钥或使用 IRSA 支持。
- 根据 ESO 文档建议的 AWS Secrets Manager 权限创建 IAM Policy
1{
2 "Version": "2012-10-17",
3 "Statement": [
4 {
5 "Effect": "Allow",
6 "Action": [
7 "secretsmanager:GetResourcePolicy",
8 "secretsmanager:GetSecretValue",
9 "secretsmanager:DescribeSecret",
10 "secretsmanager:ListSecretVersionIds"
11 ],
12 "Resource": [
13 "arn:aws:secretsmanager:us-west-2:111122223333:secret:dev/*" # 替换 region, accountid, 密钥的名称前缀
14 ]
15 }
16 ]
17}
- 使用
eksctl
工具为 EKS 集群创建ESO需要的 Role 及绑定 ESO 需要的权限,例如,
1eksctl create iamserviceaccount --cluster=gitops-cluster --name=external-secrets \
2--role-only --role-name=gitops-cluster-dev-external-secrets-role --region ap-southeast-1 \
3--namespace=kube-system --attach-policy-arn=arn:aws:iam::123456789012:policy/gitops-dev-external-secrets-sm \
4--approve
namespace
需要跟ESO部署的命令空间保持一致
name
需要跟部署 ESO Chart 指定的 ServiceAccount 名称一致,默认为external-secrets
- 使用 Kustomization patch 为 ESO Chart 创建的 ServiceAccount 指定 IAM role
1apiVersion: kustomize.config.k8s.io/v1beta1
2kind: Kustomization
3resources:
4 - ../../base
5 - ./secrets.yaml
6patches:
7 - patch: |
8 - op: add
9 path: /spec/patches/-
10 value:
11 patch: |
12 - op: add
13 path: /spec/values/serviceAccount/annotations/eks.amazonaws.com~1role-arn
14 value: arn:aws:iam::845861764576:role/gitops-cluster-dev-external-secrets-role
15 target:
16 kind: HelmRelease
17 name: external-secrets
18 target:
19 group: kustomize.toolkit.fluxcd.io
20 version: v1beta2
21 kind: Kustomization
22 name: external-secrets
创建 SecretStore
或 ClusterSecretStore
配置访问 AWS Secrets Manager
1apiVersion: external-secrets.io/v1beta1
2kind: ClusterSecretStore
3metadata:
4 name: secretstore
5 namespace: kube-system
6spec:
7 provider:
8 aws:
9 service: SecretsManager
10 region: ap-southeast-1
11 auth:
12 jwt:
13 serviceAccountRef:
14 name: external-secrets
15 namespace: kube-system
上面的配置使用了 ServiceAccount 的短时间有效期 JWT token 访问 AWS API,避免了在集群内管理保存 AWS 访问凭证。
创建 ExternalSecret
对象从 Secrets Manager 获取密钥并配置到 K8S 的 Secret 对象
1apiVersion: external-secrets.io/v1beta1
2kind: ExternalSecret
3metadata:
4 name: slack-url
5 namespace: kube-system
6spec:
7 refreshInterval: 1h
8 secretStoreRef:
9 name: secretstore
10 kind: ClusterSecretStore
11 target:
12 name: slack-url
13 creationPolicy: Owner
14 deletionPolicy: Delete
15 data:
16 - secretKey: address
17 remoteRef:
18 key: dev/slackurl
如上的 ExternalSecret
对象声明了在 kube-system
命令空间创建名为 slack-url
的密钥。ESO会通过名为 secretstore
的 ClusterSecretStore
对象获取 AWS Secrets Manager 访问凭证,将名为 dev/slackurl
的 AWS Secrets Manager 密钥内容设置到
K8S Secret slack-url
的 address
键值。
确保 FluxCD 创建 ESO 资源的顺序
如上部署通过 Helm 部署了 ESO,通过 ESO 自定义资源创建了 ClusterSecretStore
和 ExternalSecret
创建密钥。
这些资源通过不同的 Flux 控制器(Kustomization或Helm)所创建,这些资源可用的顺序没有办法保证先后顺序。但是 ESO
的自定义资源对象声明(如ClusterSecretStore
)依赖 ESO 完整的部署创建自定义资源声明。这里通过嵌套的 Flux Kustomization
对象来管理不同对象间的依赖。示例实现如下,
1apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
2kind: Kustomization
3metadata:
4 name: secrets
5 namespace: flux-system
6spec:
7 interval: 10m0s
8 path: ./infrastructure/overlays/development/secrets
9 prune: true
10 dependsOn:
11 - name: sealed-secrets
12 - name: external-secrets
13 sourceRef:
14 kind: GitRepository
15 name: flux-system
ESO Examples 文档也详细解释了 FluxCD 中的这个问题,并且示例了解决方法。
小结
本文介绍了 External Secrets Operator 将成熟且经过验证的密钥管理服务(如 AWS Secrets Manager)引入到 Kubernetes 原生生态。 用户可以保留使用这些密钥服务的最佳实践和经验,同时让 K8S 编排的任务也无需改动仍然使用云原生的方式访问密钥。 整个方案兼容了安全成熟的密钥管理同 K8S 内程序访问密钥的需求。
随后简短的示例了如何在 EKS 环境最佳实践的管理 ESO 部署,同时示例了如何使用 FluxCD GitOps 方式同时管理 ESO 部署和外部密钥。 完整的代码示例可以这个仓库获取。
如果用户有需求通过文件访问 AWS Secrets Manager 的密钥,可以使用 AWS 开源的 AWS Secrets Manager and Config Provider for Secret Store CSI Driver, 这个项目将 Secrets Manager/Parameter Store 通过 CSI Driver 挂载到容器,提供文件系统的访问。