External Secrets Operator 集成 OpenBao:基于 Vault Provider 的密钥同步实战指南
External Secrets Operator 集成 OpenBao基于 Vault Provider 的密钥同步实战指南【免费下载链接】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-secretsExternal Secrets OperatorESO通过复用其 HashiCorp Vault Provider 的成熟实现为 OpenBao 与 providers/v1/openbao 下的完整源码实现系统讲解 OpenBao Provider 的架构、配置参数、四种认证方式、密钥读取与校验机制并给出可直接落地的 SecretStore / ExternalSecret 配置示例。读完本文你将掌握如何在 Kubernetes 中把 OpenBao KV 引擎中的密钥安全同步为 Kubernetes Secret并能根据源码理解其只读能力边界与底层调用链。OpenBao 与 ESO 的集成方式OpenBao 是一个由社区维护的、API 兼容 HashiCorp Vault 的开源密钥管理项目两者的 HTTP API 与认证协议高度一致。正因如此OpenBao 集成文档 明确说明External Secrets Operator 通过使用 HashiCorp Vault Provider 来与 OpenBao 集成具体实现细节请以该文档为准。集成文档还给出了经官方验证的版本组合该集成使用External Secrets Operator v0.16.1与OpenBao v2.2.0完成了测试。这意味着在选用 OpenBao 作为密钥后端时这两个版本是当前仓库明确验证过的组合。仓库内pkg/register/openbao.go展示了 OpenBao Provider 的注册方式//go:build openbao || all_providers // Register openbao provider esv1.Register(openbao.NewProvider(), openbao.ProviderSpec(), openbao.MaintenanceStatus())从中可以确认两点事实OpenBao Provider 与 Vault Provider 是仓库中独立注册的 Provider 实例通过openbao或all_providers构建标签启用openbao.MaintenanceStatus()返回esv1.MaintenanceStatusMaintained见 provider.go表明该 Provider 处于活跃维护状态。底层实现复用 Vault API 的独立 Provider从源码结构看OpenBao Provider 是providers/v1/openbao下的完整独立实现而并非简单地将SecretStore.spec.provider.vault改名复用。其核心入口在 provider.go// Provider implements the ESO Provider interface for OpenBao. type Provider struct { HTTPClientFactory httpClientFactory AuthMethodFactory auth.Factory } // Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite). func (p *Provider) Capabilities() esv1.SecretStoreCapabilities { return esv1.SecretStoreReadOnly }实现要点如下使用 OpenBao 官方 Go 客户端github.com/openbao/openbao/api/v2构建连接见 client.go这与 Vault Provider 使用hashicorp/vault/api形成对应关系——两个 Provider 各自对接各自的官方 SDK但共享 ESO 侧的统一接口抽象认证方式通过internal/auth包内的工厂接口auth.Factory创建默认工厂DefaultAuthMethodFactory直接调用 OpenBao 官方 SDK 中对应的认证方法构造器见 internal/auth/impl.goProvider 的能力为只读SecretStoreReadOnly这与下文PushSecret / DeleteSecret 未实现的限制一致。因此OpenBao 集成文档中请参阅 HashiCorp Vault Provider 文档的指引本质上指向的是使用方式与 KV 语义的互通性SecretStore/ExternalSecret 的编排模型、metadataPolicy、dataFrom等而 HTTP 层与认证层则由 OpenBao 自身的实现负责。配置 SecretStore字段与默认值OpenBao Provider 的 CRD 类型定义在 apis/externalsecrets/v1/secretstore_openbao_types.go通过spec.provider.openbao配置。核心字段如下字段类型必填说明serverstring是OpenBao 服务地址如https://openbao.example.com:8200pathstring否KV 引擎挂载路径默认kv见 client.go 的path()方法若填写了 v2 引擎特有的/data后缀客户端会自动兼容versionstring否KV 引擎版本v1或v2默认v2CRD 中通过kubebuilder:default:v2声明namespacestring否OpenBao Namespace多租户特性如ns1caBundle[]byte否PEM 编码的 CA 证书用于校验 OpenBao 服务端证书caProviderobject否从 Kubernetes Secret/ConfigMap 引用的 CA 证书authobject否认证配置见下文两个容易踩坑的约束已由 CRD 校验注解表达caBundle与caProvider通过kubebuilder:validation:AtMostOneOfcaBundle;caProvider约束为二选一version通过kubebuilder:validation:Enumv1;v2限定取值范围。一个最小可用的 SecretStore 示例apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: openbao-backend spec: provider: openbao: server: https://openbao.example.com:8200 path: secret version: v2 auth: tokenSecretRef: name: openbao-token key: token --- apiVersion: v1 kind: Secret metadata: name: openbao-token data: token: cm9vdA # root注意若使用ClusterSecretStoretokenSecretRef等引用必须显式指定namespace指明 Secret 所在的 Kubernetes 命名空间。这是 ESO 对集群级 Store 引用命名空间 Secret 的通用要求详见 HashiCorp Vault Provider 文档。认证方式四种受支持的模式OpenBaoAuth 类型通过kubebuilder:validation:ExactlyOneOfappRole;tokenSecretRef;userPass;kubernetes约束四种认证方式必须且只能选择一种。客户端装配逻辑位于 client.go 的setupAuth方法。Token 认证静态令牌最直接的认证方式从 Kubernetes Secret 中读取静态 token 并直接设置到客户端spec: provider: openbao: server: https://openbao.example.com:8200 auth: tokenSecretRef: name: openbao-token key: token对应实现resolvers.SecretKeyRef解析 Secret 后调用c.client.SetToken(token)见 client.go。AppRole 认证AppRole 认证从 Secret 中读取 secret ID并配合roleId内联或通过roleRef引用换取临时令牌。roleId与roleRef通过ExactlyOneOf约束为互斥spec: provider: openbao: server: https://openbao.example.com:8200 auth: appRole: path: approle # 默认 approle roleId: my-role-id # 或使用 roleRef 引用 Secret 中的值 secretRef: name: openbao-approle-secret key: secretid实现上AppRole 的登录由 OpenBao 官方 SDK 的approle.NewAppRoleAuth完成见 internal/auth/impl.go。值得注意的是若同时指定了roleRefRoleID 也来自 Secret源码会优先使用roleRef解析出的 RoleID见 client.go。UserPass 认证使用用户名/密码对换取访问令牌用户名直接写在 Store 中密码存放在 Kubernetes Secret 中spec: provider: openbao: server: https://openbao.example.com:8200 auth: userPass: path: userpass # 默认 userpass username: alice secretRef: name: openbao-userpass-secret key: password这是唯一被 HTTP 录制测试覆盖的认证方式详见下文测试章节因为 UserPass 交互相对静态、易于回放。Kubernetes 原生认证通过 Kubernetes ServiceAccount 令牌向 OpenBao 认证令牌来源二选一ExactlyOneOfserviceAccountRef;secretRefserviceAccountRef通过 Kubernetes TokenRequest API 请求短期令牌可自定义audiences见 client.go 的getJwt方法secretRef从 Secret 中读取已存在的 ServiceAccount JWT未指定key时默认使用token。spec: provider: openbao: server: https://openbao.example.com:8200 auth: kubernetes: path: kubernetes # 默认 kubernetes role: my-role # 必填绑定 ServiceAccount 与 OpenBao 策略 serviceAccountRef: name: my-sa audiences: [vault]从源码注释看OpenBao Kubernetes 认证不支持直接使用控制器 Pod 的 ServiceAccount 令牌Using the controller pods ServiceAccount token is not supported必须显式指定serviceAccountRef或secretRef。另外由于 Kubernetes 认证依赖 OpenBao 侧通过 TokenReview API 校验令牌需按 OpenBao 官方文档完成 ServiceAccount 的 RBAC 授权system:auth-delegator相关说明可参考 HashiCorp Vault Provider 文档 的 Kubernetes 认证一节。认证命名空间OpenBao 的 Namespace 支持在数据读取与认证两个层面分别指定spec.provider.openbao.namespace数据读取所用命名空间spec.provider.openbao.auth.namespace认证所用命名空间可不同于数据命名空间若未设置默认回退到openbao.namespace。对应实现见 client.go 与 client.go认证请求会通过authClient.WithNamespace(...)在独立命名空间上下文中执行登录成功后取得的令牌再设置回主客户端。从 OpenBao 读取密钥登录完成后Provider 通过GetSecret/GetSecretMap/GetAllSecrets三个接口向 ESO 提供数据见 client.go。单密钥读取property 与 versionKV v2 引擎的读取逻辑见 client.go不指定remoteRef.property时返回整个 secret 的 JSON 编码数据指定property时仅返回该字段的值缺失则报cannot find secret data for keyKV v2 可通过remoteRef.version指定读取特定版本内部先做strconv.Atoi转换再调用KVv2.GetVersionKV v1 不支持版本化读取指定version会直接报错OpenBao KVv1 secrets do not support versioning (use KVv2)。一个完整的 ExternalSecret 示例对应 Vault 文档中的模式apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: openbao-example spec: refreshInterval: 15s secretStoreRef: name: openbao-backend kind: SecretStore target: name: example-sync data: - secretKey: foobar remoteRef: key: foo property: my-value先通过bao kv put -mountsecret foo my-values3cr3t在 OpenBao 中写入密钥ESO 控制器即可将该值同步为名为example-sync的 Kubernetes Secret 中的foobar字段。整包读取GetSecretMap 与 dataFromGetSecretMap将整个 secret 的 JSON 数据反序列化为map[string][]byte用于dataFrom.extract场景当remoteRef.property留空时dataFrom.extract会把该路径下的全部键值对展开为多个目标字段。嵌套值可通过property指定 gjson 表达式如foo.nested.bar提取具体用法与 HashiCorp Vault Provider 文档 中的示例一致。批量查找GetAllSecrets 与 dataFrom.findGetAllSecrets实现了dataFrom.find的能力见 client.go在ref.path指定的目录下列出密钥名默认从 Provider 的path挂载点开始通过runtime/find包的正则匹配器过滤名称对匹配的每个密钥逐一调用GetSecret读取完整值。需要特别说明的限制OpenBao Provider 的GetAllSecrets尚未实现基于 tagcustom_metadata的搜索传入ref.Tags会直接返回 tag based search is not implemented 错误同时SecretExistsPushSecret 配套接口也未实现。因此基于标签的dataFrom.find目前只适用于 Vault ProviderOpenBao 用户应使用name.regexp方式按名称匹配。配置校验ValidateStore 与 ValidateESO 在两层面对 OpenBao 配置进行校验。准入校验ValidateStorevalidate.go 实现ValidateStore在 SecretStore 被写入时校验其结构合法性包括Store / Spec / Provider 非空、provider.openbao存在以及对appRole、tokenSecretRef、userPass、kubernetes四种认证下的所有 Secret/ServiceAccount 引用执行ValidateReferentSecretSelector/ValidateReferentServiceAccountSelector检查保证 ClusterSecretStore 的引用带上了 namespace 等必要条件。连通性校验Validateclient.go 的Validate方法在 Store 就绪时探测 OpenBao设置 5 秒超时上下文调用Sys().MountInfoWithContext查询挂载信息校验挂载类型必须为kv否则报expected mount type kv found %q校验 KV 引擎版本与spec.provider.openbao.version一致否则报expected kv engine version %s found version %s。一个例外当ClusterSecretStore使用 referent namespace 引用即认证引用的 namespace 未内联指定时由于Validate()阶段尚无法确定 namespace会返回ValidationResultUnknown跳过深度校验。能力边界当前为只读 Provider结合 provider.go 的Capabilities()与 client.go 中未实现的接口方法OpenBao Provider 当前的能力边界非常清晰能力状态说明读取密钥GetSecret / GetSecretMap / GetAllSecrets✅ 支持覆盖 KV v1 / v2PushSecret❌ 不支持PushSecret返回 push secret is not supported (the OpenBao provider is currently read only)DeleteSecret❌ 不支持同上返回明确的只读错误SecretExists❌ 未实现返回 not implemented因此OpenBao Provider 不能用于 PushSecret 等写入型工作流对比之下 Vault Provider 支持 PushSecret详见 HashiCorp Vault Provider 文档 的 PushSecret 章节。如果你的场景需要向 OpenBao 推送密钥需要等待该 Provider 后续迭代补齐写入能力。测试与质量保障基于真实流量的录制回放OpenBao Provider 的测试策略很有特色详细说明见 DEV.md大部分逻辑测试基于与真实 OpenBao 服务器交互录制的 HTTP 流量存储在 testdata/http 目录下既贴近真实环境又保持毫秒级执行速度。测试初始化脚本 testdata/init-bao.sh 展示了测试环境的搭建过程也侧面印证了 Provider 的能力矩阵bao kv put -mountsecret foo barbazz loremipsum # KV v2 写入 bao secrets enable -version1 -pathsecret_v1 kv # 启用 KV v1 引擎 bao auth enable --pathcustomuserpasspath userpass # 启用 UserPass 认证 bao namespace create my-namespace # 创建命名空间 bao secrets enable -version2 --namespacemy-namespace kv # 命名空间内启用 KV v2重新录制测试流量只需执行ESO_PROVIDER_OPENBAO_RERECORDtrue go test .要求bao命令在 PATH 中。录制时会清理随机值如 mount accessor与时间戳保证 git diff 可读性。测试覆盖范围从testdata/http文件名可推断包括KV v1 / KV v2 的单密钥、属性提取、版本化读取、全量列举、命名空间认证TestProvider_BaoNamespaces以及 UserPass 登录与配置校验等。总结OpenBao Provider 是 ESO 生态中对开源密钥管理后端的有力补充它通过与 Vault Provider 一致的编排模型SecretStore / ExternalSecret / ClusterSecretStore降低了学习成本同时以独立的官方 SDK 实现保证了协议兼容性。当前版本经 ESO v0.16.1 OpenBao v2.2.0 验证支持 KV v1/v2 读取、四种认证方式与命名空间多租户但能力边界为只读。接入生产环境前建议结合本文的字段说明核对 SecretStore 配置并通过Validate校验挂载类型与 KV 版本一致性尽早暴露配置错误更多与 Vault 行为一致的语义细节如metadataPolicy、gjson 嵌套提取可直接查阅 HashiCorp Vault Provider 文档 与 OpenBao Provider 源码。【免费下载链接】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),仅供参考