External Secrets Operator 多租户部署模式详解:三种隔离架构与 RBAC 落地实践
2026/9/17 4:47:03 网站建设 项目流程

External Secrets Operator 多租户部署模式详解:三种隔离架构与 RBAC 落地实践

【免费下载链接】external-secretsExternal Secrets Operator reads information from a third-party service like AWS Secrets Manager and automatically injects the values as Kubernetes Secrets.项目地址: https://gitcode.com/GitHub_Trending/ex/external-secrets

External Secrets Operator(ESO)通过SecretStore/ClusterSecretStore两类资源抽象了对外部密钥服务的访问,天然支持多种多租户形态。本篇指南基于 docs/guides/multi-tenancy.md 展开,系统讲解共享 ClusterSecretStore、每命名空间托管 SecretStore、ESO as a Service 三种部署模式的架构、适用场景与权衡,并深入仓库源码与配套示例,说明conditionscontrollerClass、Helm 开关、Kyverno 准入校验等机制如何配合实现租户隔离。读完本文,你将能根据组织角色结构与外部 API 的访问控制粒度,选型并落地一套适合自己团队的多租户方案。

一、多租户设计:先分析组织,再选择模式

ESO 提供多种运行模式以满足不同的组织需求。在动手部署前,建议先审视你的组织结构,明确三个问题:

  1. 组织中存在哪些角色,例如Application Developers(应用开发者)Cluster Admins(集群管理员)Secret Administrator(密钥管理员)
  2. 这些角色各自承担什么职责,例如谁负责创建命名空间、谁负责管理外部密钥、谁只负责引用密钥;
  3. 这些职责如何映射到 Kubernetes RBAC 角色,即谁有权限创建ClusterSecretStoreSecretStoreExternalSecret等资源。

同时,还需要审视外部 API 提供商对密钥的访问控制能力:

  • 能否按密钥名做细粒度限制(例如仅允许db/dev/*前缀)?
  • 还是只能在桶(bucket)级别控制访问?
  • 需要注意,并非所有外部 API 都提供密钥级的细粒度访问管理。

从源码结构看,这两类资源在设计上就对应了不同的职责边界:secretstore_types.go 中SecretStore是命名空间级资源(scope=Namespaced,shortName 为ss),而ClusterSecretStore是集群级资源(scope=Cluster,shortName 为css),二者共享同一个SecretStoreSpecExternalSecret通过secretStoreRef引用它们,其中kind字段默认值为SecretStore,可显式指定为ClusterSecretStore,参见 externalsecret_types.go。

注意:下面介绍的示例不应被视为"最佳实践"的唯一答案,它们更多是展示如何组合不同的机制与技术来实现租户隔离。实际选型应以你的组织结构和安全要求为准。

二、模式一:共享 ClusterSecretStore(Shared ClusterSecretStore)

工作方式

集群管理员部署一个ClusterSecretStore(CSS),并统一管理对外部 API 的访问。该 CSS 被集群内所有租户共享;应用开发者在自己的命名空间里创建ExternalSecret并引用它,但不能自行创建ClusterSecretStoreSecretStore。此时,所有应用开发者都能访问 CSS 背后的全部密钥。

一个典型的共享 CSS 示例(AWS Secrets Manager):

apiVersion: external-secrets.io/v1 kind: ClusterSecretStore metadata: name: global-aws-store spec: provider: aws: service: SecretsManager region: eu-central-1 auth: secretRef: accessKeyIDSecretRef: name: aws-secret key: access-key namespace: eso-system secretAccessKeySecretRef: name: aws-secret key: secret-access-key namespace: eso-system --- # 租户侧只需引用即可,无需也无法创建 Store apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: app-secret namespace: team-a spec: secretStoreRef: name: global-aws-store kind: ClusterSecretStore target: name: app-secret data: - secretKey: db_password remoteRef: key: team-a/db_password

关键约束

  • 没有按命名空间限制密钥的能力:ESO 本身不提供"按命名空间限制可访问的密钥"的机制。这意味着一旦某个命名空间能引用该 CSS,它就能读取其背后的全部密钥。如果你希望限制不同租户只能读取特定 key 或特定前缀,必须引入 Admission Webhook 做更高级的校验,例如使用 Kyverno 或 Open Policy Agent(见下文第五节)。
  • 权限集中在外部 API 侧:访问控制实际取决于 CSS 中配置的凭证在外部系统(如 AWS IAM)中的权限范围。

适用场景与取舍

  • 适合:你有一个包含所有密钥的中央存储桶,且希望由集群管理员统一管理对其的访问。
  • 优点:部署极简,租户接入成本低。
  • 缺点:扩展性差——所有租户共享同一份凭证与权限面,一旦需要细化隔离,只能依赖外部准入策略。

三、模式二:每命名空间托管 SecretStore(Managed SecretStore per Namespace)

工作方式

集群管理员为每个命名空间(或每组命名空间)管理一个或多个SecretStore。每个SecretStore使用独立的 role(角色),该角色在外部 API 侧被限制为只能访问一小部分密钥。此方案的独特之处在于:访问控制实际上由外部 API 提供的角色来管理,集群管理员只做"接线"(wiring)工作——把正确的角色凭证接入对应命名空间的SecretStore

apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: team-a-store namespace: team-a spec: provider: aws: service: SecretsManager region: eu-central-1 auth: secretRef: accessKeyIDSecretRef: name: aws-secret key: access-key namespace: team-a secretAccessKeySecretRef: name: aws-secret key: secret-access-key namespace: team-a --- apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: app-secret namespace: team-a spec: secretStoreRef: name: team-a-store kind: SecretStore target: name: app-secret dataFrom: - extract: key: team-a/*

职责边界

  • Secret Administrator(密钥管理员):管理密钥的访问与生命周期——这是一个外部实体,负责在外部 API 侧维护角色权限、创建/轮换密钥。
  • Cluster Administrator(集群管理员):只负责把对应的角色凭证与命名空间绑定(创建SecretStore)。
  • Application Developer(应用开发者):在命名空间内创建ExternalSecret引用本命名空间的SecretStore

适用场景与取舍

  • 适合:组织内存在独立的密钥管理团队,希望由"外部实体"负责密钥的访问与生命周期,Kubernetes 侧仅做绑定。
  • 优点:租户间隔离粒度细,隔离责任由外部 API 的角色体系承担,Kubernetes 侧只需保证 RBAC 不允许开发者越权创建或修改SecretStore
  • 缺点:运维成本高于模式一——每个命名空间都需要单独的凭证/角色管理;命名空间数量增长时,Store 的数量也随之线性增长。

四、模式三:ESO as a Service(自服务模式)

工作方式

每个命名空间完全自包含。应用开发者自行管理SecretStoreExternalSecret以及外部密钥基础设施;集群管理员提供 External Secrets Operator 这一"公共服务"(as a Service),不再介入具体的 Store 与密钥配置。

# 由应用开发者创建于自己的命名空间 apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: my-store namespace: team-a spec: provider: vault: server: "https://vault.example.com" path: "secret/data/team-a" version: "v2" auth: kubernetes: mountPath: "kubernetes" role: "team-a-reader" --- apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: my-app-secret namespace: team-a spec: secretStoreRef: name: my-store kind: SecretStore target: name: my-app-secret data: - secretKey: api_token remoteRef: key: team-a/api-token

适用场景与取舍

  • 适合:应用开发者应完全自治(自行决定密钥来源、命名、轮换策略),而由中央团队提供通用平台服务(本例中即 ESO 本身)。
  • 优点:平台团队负担最轻,租户自主性最高;隔离边界清晰——每个命名空间的 Store 只影响自己。
  • 缺点:对外部 API 凭证的管理分散在各租户手中,若缺乏治理,可能出现凭证泄露、密钥命名混乱等风险;需要靠 RBAC 与准入策略兜底。

五、三种模式的组合与深化机制

多租户落地往往不是单选一种模式,而是组合多种机制。以下是仓库中可直接使用的配套能力。

5.1 用 Helm 开关裁剪集群级功能

如果采用"ESO as a Service"或"每命名空间托管"模式,你可能希望彻底禁用ClusterSecretStoreClusterExternalSecretClusterPushSecret等集群级资源,避免租户间共享或误用。参考 docs/guides/disable-cluster-features.md,需要通过 Helm 同时配置crds.*process*两组值(它们相互关联):

helm install external-secrets external-secrets/external-secrets \ --set crds.createClusterExternalSecret=false \ --set crds.createClusterSecretStore=false \ --set crds.createClusterPushSecret=false \ --set processClusterExternalSecret=false \ --set processClusterStore=false \ --set processClusterPushSecret=false

这样,集群中不再安装这些集群级 CRD,operator 也不会处理它们——从根上杜绝了跨命名空间共享 Store 的可能,让"每命名空间自服务"的边界更清晰。

5.2 用 ClusterSecretStore conditions 限定可见命名空间

即便保留ClusterSecretStore,也可以利用其spec.conditions字段(见 secretstore_types.go 与 ClusterSecretStoreCondition 定义)将集群级 Store 的效力约束到指定命名空间。该字段支持三种选择方式:

  • namespaceSelector:按 label selector 匹配命名空间;
  • namespaces:按名称精确列出命名空间;
  • namespaceRegexes:按正则匹配命名空间名称。

示例——仅让打了tenant: alpha标签的命名空间可用该 CSS:

apiVersion: external-secrets.io/v1 kind: ClusterSecretStore metadata: name: alpha-only-store spec: conditions: - namespaceSelector: matchLabels: tenant: alpha provider: fake: data: - key: alpha/db_password value: "supersecret"

注意:conditionsClusterSecretStore特有的能力,只对集群级 Store 生效。

5.3 用 controllerClass 实现多控制器隔离

如果希望多个 ESO 控制器实例分管不同的 Store/工作负载组,可以借助实验性的Controller Class机制,详见 docs/guides/controller-class.md。它通过spec.controller属性将SecretStore归属到特定控制器:

helm install custom-external-secrets external-secrets/external-secrets --set controllerClass=custom
apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: custom-store namespace: team-a spec: controller: custom provider: fake: data: - key: foo value: bar

此后,绑定到该 Store 的ExternalSecret只会被controllerClass=custom的 operator 处理。需要留意的是:

  • 未设置spec.controller的 Store 会被任意controller 视为有效(无论其 controllerClass 是什么);
  • 该特性目前标注为 experimental,尚未经过高强度的测试。

5.4 用 Admission Webhook 补齐密钥级隔离

如前所述,ESO 不提供"按命名空间限制密钥前缀"的内置能力,这类高级校验应交给 Admission Webhook。仓库 docs/snippets/kyverno-policy-secretstore.yaml 提供了一个现成的 KyvernoClusterPolicy示例,强制所有SecretStore/ClusterSecretStore只能使用 AWS Secrets Manager 提供商:

apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: require-secretstore-aws-provider spec: validationFailureAction: Enforce rules: - name: require-secretstore-aws-provider match: any: - resources: kinds: - SecretStore - ClusterSecretStore validate: message: "You must only use AWS SecretsManager" pattern: spec: provider: aws: service: SecretsManager

以此类推,你可以编写更细粒度的策略:例如校验remoteRef.key必须匹配team-<namespace>/*前缀、校验spec.provider中引用的凭证 Secret 必须位于固定命名空间等,从而在"共享 CSS"模式下实现前缀级租户隔离。使用 OPA(Gatekeeper)也能达到同样目的。

六、模式对比与选型建议

维度共享 ClusterSecretStore每命名空间托管 SecretStoreESO as a Service
Store 归属集群管理员(集群级)集群管理员(命名空间级)应用开发者(命名空间级)
访问控制主体外部 API 凭证 + 准入策略外部 API 的角色体系租户自管理凭证
隔离粒度粗(需 Webhook 补强)细(按角色限 key)细(命名空间自包含)
集群管理员负担
租户自主性
适合组织单一中央密钥桶有独立密钥管理员平台 + 自治租户

选型时请重点回答两个问题:

  1. 谁负责密钥的访问与生命周期?如果是中央团队,倾向模式一或二;如果是租户自身,选模式三。
  2. 外部 API 的访问控制粒度如何?支持密钥前缀级角色(如 Vault 的 policy、AWS IAM 的 resource 限制)时,模式二能获得真正的细粒度隔离;仅桶级访问时,模式一配合 Kyverno/OPA 做准入校验是更务实的组合。

七、安全要点小结

  • 最小权限:无论是 CSS 的共享凭证还是每命名空间的 role,都应在外部 API 侧收紧到"只读所需密钥"。
  • RBAC 兜底:通过 Kubernetes RBAC 限制ClusterSecretStore/SecretStore的 create/update 权限,只授予集群管理员;应用开发者仅能创建ExternalSecret
  • 准入策略:用 Kyverno / OPA 补齐 ESO 不提供的"按命名空间限制密钥前缀"能力,参考 kyverno-policy-secretstore.yaml 的模式扩展。
  • 裁剪攻击面:不需要集群级功能时,用 5.1 节的 Helm 参数关闭对应 CRD 与处理逻辑。
  • 组合而非单选:三种模式不是互斥的,你可以"共享 CSS + conditions 限定命名空间 + Webhook 前缀校验"组合使用,也可以为高敏租户单独走"每命名空间托管",为普通租户开放自服务。

参考与延伸阅读

  • 关联文档:docs/guides/multi-tenancy.md
  • API 类型定义:apis/externalsecrets/v1/secretstore_types.go(SecretStore/ClusterSecretStore/conditions)、apis/externalsecrets/v1/externalsecret_types.go(secretStoreRef
  • 裁剪集群级功能:docs/guides/disable-cluster-features.md
  • 控制器隔离:docs/guides/controller-class.md
  • 准入策略示例:docs/snippets/kyverno-policy-secretstore.yaml
  • 跨命名空间分发(ClusterExternalSecret + Kubernetes Provider):docs/snippets/cluster-external-secret-fanout.yaml
  • 完整配置样例:docs/snippets/full-secret-store.yaml、docs/snippets/full-cluster-secret-store.yaml

【免费下载链接】external-secretsExternal Secrets Operator reads information from a third-party service like AWS Secrets Manager and automatically injects the values as Kubernetes Secrets.项目地址: https://gitcode.com/GitHub_Trending/ex/external-secrets

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询