External Secrets Operator 集成 Passbolt:SecretStore 配置、ExternalSecret 同步与自定义字段全指南
2026/9/17 20:45:02 网站建设 项目流程

External Secrets Operator 集成 Passbolt:SecretStore 配置、ExternalSecret 同步与自定义字段全指南

【免费下载链接】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)官方提供的 Passbolt Provider,讲解如何将 Passbolt 密码库中的凭据通过SecretStoreExternalSecret两个 CRD 同步为 Kubernetes Secret。读完本文,你将掌握 Passbolt SecretStore 的认证与自定义 CA 配置、按 ID 拉取与按名称查找(dataFrom.find)两种取值方式、property精确选取与custom_fields.<name>自定义字段语法,并能结合源码理解每条配置背后的实际行为。

一、Passbolt Provider 概述与集成原理

Passbolt 是一个开源的团队密码管理器,数据采用端到端加密存储。ESO 通过官方 go-passbolt 的包注释中写明:该 Provider 通过 Passbolt REST API 获取存储在其中的秘密(secrets)。

从源码看,整个集成有以下关键行为(providers/v1/passbolt/passbolt.go):

  • 只读能力Capabilities()返回SecretStoreReadOnly(第 70–72 行),PushSecretDeleteSecretGetSecretMapSecretExists均返回 "not implemented",因此本 Provider 不支持PushSecret等写入场景,只用于单向同步;
  • 立即登录与会话复用NewClient在创建客户端后立即调用client.Login(ctx)(第 111–113 行),后续每次取值前通过assureLoggedIn检查会话,失效时自动重新登录(第 335–340 行);
  • V5 元数据预取缓存:登录后调用PreFetchCaches预取会话密钥与元数据密钥,以加速 V5 资源的解密;Close时还会调用SavePendingSessionKeys保存会话密钥(第 115–119、216–221 行);
  • 注册方式:Provider 通过NewProvider()ProviderSpec()MaintenanceStatus()注册(第 387–401 行),当前维护状态为MaintenanceStatusMaintained(已维护)。

二、创建 Passbolt SecretStore:认证与 Host 配置

ESO 通过Kind=SecretStore定义与 Passbolt 的连接。官方要求passboltProvider 出现在spec.provider下,并且必须配置authhost。Passbolt API 的认证需要两样东西:用户密码(password)私钥(private key),二者都存放在一个 Kubernetes Secret 中,通过passwordSecretRefprivateKeySecretRef引用。

完整配置见 docs/snippets/passbolt-secret-store.yaml:

apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: passbolt spec: provider: passbolt: host: https://passbolt.passbolt.svc.cluster.local auth: passwordSecretRef: key: password name: passbolt-credentials privateKeySecretRef: key: privateKey name: passbolt-credentials

字段说明:

  • spec.provider.passbolt.host:Passbolt 服务地址,必须是 HTTPS 协议ValidateStore会解析该 URL 并强制校验host.Scheme == "https",否则报错host Url has to be https scheme(providers/v1/passbolt/passbolt.go);
  • spec.provider.passbolt.auth.passwordSecretRef:引用存放 Passbolt 用户密码的 Secret,需指定namekey
  • spec.provider.passbolt.auth.privateKeySecretRef:引用存放用户私钥的 Secret(即 Passbolt 中该用户用于解密数据的私钥)。

字段类型定义位于 apis/externalsecrets/v1/secretsstore_passbolt_types.go:PassboltAuthPasswordSecretRefPrivateKeySecretRef两个SecretKeySelector组成,PassboltProviderAuthHost、可选CABundle与可选CAProvider组成。

2.1 SecretStore 校验规则(ValidateStore)

ValidateStore(providers/v1/passbolt/passbolt.go)按顺序校验以下内容,任一不满足都会拒绝该 SecretStore:

校验项错误信息
spec.provider.passbolt存在missing: spec.provider.passbolt
auth存在missing: spec.provider.passbolt.auth
auth.passwordSecretRef的 name/key 非空missing: spec.provider.passbolt.auth.passwordSecretRef
auth.privateKeySecretRef的 name/key 非空missing: spec.provider.passbolt.auth.privateKeySecretRef
host非空missing: spec.provider.passbolt.host
host可被解析且 scheme 为httpshost Url has to be https scheme

2.2 凭据解析逻辑

NewClient中通过resolvers.SecretKeyRef在 SecretStore 所在命名空间内解析passwordprivateKey两个引用(providers/v1/passbolt/passbolt.go),随后构造 go-passbolt 客户端。实际认证时,client.Login(ctx)会先向 Passbolt 发起登录请求,之后所有 API 调用均依赖该会话。

三、自定义 CA 证书配置

如果 Passbolt 实例使用了私有 CA 或自定义 CA 签发的证书,需要让 ESO 信任该 CA。两种方式任选其一,见 docs/snippets/passbolt-secret-store-ca.yaml:

apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: passbolt-with-custom-ca spec: provider: passbolt: host: https://passbolt.example.com # Reference a ConfigMap or Secret containing the CA bundle that signed # the Passbolt server certificate. caProvider: type: ConfigMap name: passbolt-ca-bundle key: ca.crt auth: passwordSecretRef: key: password name: passbolt-credentials privateKeySecretRef: key: privateKey name: passbolt-credentials
  • caBundle(内联 PEM):直接把 PEM 编码的 CA 证书内容写在spec.provider.passbolt.caBundle中;
  • caProvider(引用外部来源):通过typeConfigMapSecret)、namekey引用一个 ConfigMap 或 Secret 中存放的 CA bundle。

如果两者都未设置,ESO 使用系统根证书校验 TLS 连接。底层实现在buildHTTPClient(providers/v1/passbolt/passbolt.go):

  • caBundlecaProvider均为空时返回nil,让 go-passbolt SDK 使用默认 HTTP 客户端与系统根 CA;
  • 否则通过esutils.FetchCACertFromSource拉取 CA 内容,追加到x509.NewCertPool();若 PEM 解析失败返回failed to parse CA certificate for Passbolt provider
  • 克隆默认http.Transport并仅覆盖TLSClientConfig.RootCAs,同时强制MinVersion = TLS 1.2,保留原有的代理、拨号器、HTTP/2 与空闲连接设置。

四、创建 ExternalSecret 同步 Passbolt 秘密

要把 Passbolt 秘密同步为 Kubernetes Secret,需要创建Kind=ExternalSecret并引用上一步的 SecretStore。默认情况下,同步出的秘密包含nameusernameuripassworddescription五个标准属性;如需只取其中某一个属性,可以在remoteRef.property中指定。

示例见 docs/snippets/passbolt-external-secret-example.yaml:

apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: passbolt-example-simple spec: refreshInterval: "1h0m0s" secretStoreRef: name: passbolt kind: SecretStore target: name: passbolt-example data: - secretKey: full_secret remoteRef: key: e22487a8-feb8-4591-95aa-14b193930cb4 # Replace with ID of exising Passbolt secret - secretKey: password_only remoteRef: key: e22487a8-feb8-4591-95aa-14b193930cb4 # Replace with ID of exising Passbolt secret property: password # You can limit the secret to only display one property

要点说明:

  • remoteRef.keyPassbolt 资源的 UUID(而非名称),需替换为实际存在的 Passbolt 秘密 ID;
  • remoteRef.property可选,取值范围为nameusernameuripassworddescriptioncustom_fields.<name>(详见第六节)。源码GetProp中若传入其他值,会返回property must be one of name, username, uri, password, description, or custom_fields.<name>(providers/v1/passbolt/passbolt.go);
  • 不指定property时,GetSecretesutils.JSONMarshal(secret)分支,返回整个秘密的 JSON 对象(providers/v1/passbolt/passbolt.go)。

4.1 同步结果示例

上述 ExternalSecret 将生成如下形态的 Kubernetes Secret(见 docs/snippets/passbolt-secret-example.yaml):

apiVersion: v1 kind: Secret metadata: name: passbolt-example data: full_secret: '{"name":"passbolt-secret","username":"some-username","password":"supersecretpassword","uri":"passbolt.com","description":"some description"}' password_only: supersecretpassword type: Opaque

可以看到:full_secret键保存的是完整 JSON 对象,password_only键则只保存password属性的原始字符串值。

4.2 取值流程(GetSecret 调用链)

GetSecret(providers/v1/passbolt/passbolt.go)的调用链为:

  1. assureLoggedIn确保会话有效;
  2. getPassboltSecret(ctx, ref.Key)调用client.GetResource(id)拉取资源;
  3. secretFromResource获取资源类型(GetResourceType)与秘密数据(GetSecret),再通过helper.GetResourceFieldMaps解出元数据字段与机密字段,组装成Secret结构体(providers/v1/passbolt/passbolt.go);
  4. property为空返回 JSON 序列化结果,否则调用GetProp取单个属性。

Secret结构体的 JSON 字段名为nameusernamepassworduridescription与可选的custom_fields(providers/v1/passbolt/passbolt.go),这也正是上面同步结果 JSON 键名的来源。

五、按名称查找秘密(dataFrom.find)

除了按 ID 精确拉取,还可以用dataFrom配合find按名称(正则)搜索 Passbolt 中的秘密。示例见 docs/snippets/passbolt-external-secret-findbyname.yaml:

apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: passbolt-example spec: refreshInterval: "1h0m0s" secretStoreRef: name: passbolt kind: SecretStore target: name: passbolt-example dataFrom: - find: name: regexp: ".*"

底层由GetAllSecrets实现(providers/v1/passbolt/passbolt.go),其行为要点:

  • 必须提供find.name.regexp:若ref.Name为空或RegExp为空,直接返回missing: find.name.regexp错误;
  • 先取全部资源再本地过滤:调用client.GetResources拉取所有资源,再用编译后的正则nameRegexp.MatchString(secret.Name)解密后的名称过滤,结果以资源ID -> JSON的 map 形式返回;
  • V5 加密元数据的性能提示:源码注释明确指出,由于 V5 资源的元数据(含名称)是加密的,每个资源都必须先解密才能过滤,即使不匹配也会被解密,在秘密数量较大时可能影响性能;
  • 正则会作用于解密后的名称,因此regexp: ".*"匹配所有秘密,每个匹配项会以 Passbolt 资源 ID 为键写入目标 Secret。

六、自定义字段(Custom Fields)

Passbolt 资源除了标准属性外,还可以携带任意自定义字段。ESO 通过custom_fields.<name>属性语法暴露这些字段,其中<name>是字段在 Passbolt 中配置的显示名称。

示例见 docs/snippets/passbolt-external-secret-custom-fields.yaml:

apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: passbolt-custom-fields-example spec: refreshInterval: "1h0m0s" secretStoreRef: name: passbolt kind: SecretStore target: name: passbolt-custom-fields data: # Fetch a single custom field by its display name (metadata_key). # The property value is the literal prefix "custom_fields." followed by # the name of the field as configured in Passbolt. - secretKey: api_token remoteRef: key: e22487a8-feb8-4591-95aa-14b193930cb4 # Replace with the ID of an existing Passbolt secret property: custom_fields.api-token - secretKey: deploy_key remoteRef: key: e22487a8-feb8-4591-95aa-14b193930cb4 property: custom_fields.deploy-key # Omitting property returns the full secret as JSON, with custom_fields # included as a nested object keyed by the field display name. - secretKey: full_secret remoteRef: key: e22487a8-feb8-4591-95aa-14b193930cb4

其产生的 Kubernetes Secret 形态如下(见 docs/snippets/passbolt-secret-custom-fields-example.yaml):

apiVersion: v1 kind: Secret metadata: name: passbolt-custom-fields data: api_token: my-api-token-value deploy_key: ssh-ed25519-AAAA... full_secret: '{"name":"my-service","username":"deploy","password":"supersecretpassword","uri":"https://example.com","description":"","custom_fields":{"api-token":"my-api-token-value","deploy-key":"ssh-ed25519-AAAA..."}}' type: Opaque

关于自定义字段的官方说明与源码印证:

  • 不指定property时返回完整 JSONcustom_fields键只要资源至少包含一个命名自定义字段就会出现在该 JSON 对象中,键名为字段显示名称;
  • 名称与值均可加密存储:Passbolt 会按字段配置把名称或值存放在明文元数据侧或加密机密侧,ESO 在读取前对两侧都进行解密,因此无论哪种存储方式,字段都可以用显示名称寻址。这在Secret结构体注释中有明确说明(providers/v1/passbolt/passbolt.go);
  • 属性解析逻辑GetProp中通过strings.CutPrefix(key, "custom_fields.")识别自定义字段前缀,若前缀匹配但字段不存在则返回custom field not found: <name>,否则返回字段值(providers/v1/passbolt/passbolt.go);
  • 字段组装:自定义字段最终由helper.ParseCustomFields(metaFields, secretFields).Map()填充到Secret.CustomFields中(providers/v1/passbolt/passbolt.go)。

七、实战小结与注意事项

场景使用的 CRD 字段说明
建立连接SecretStore.spec.provider.passbolt.host/authhost 必须 HTTPS;凭据存于 K8s Secret
信任私有 CAcaBundlecaProvider不配置则使用系统根证书
按 ID 同步ExternalSecret.spec.data[].remoteRef.keykey 为 Passbolt 资源 UUID
只取单属性remoteRef.property支持五个标准属性与custom_fields.<name>
按名称查找ExternalSecret.spec.dataFrom[].find.name.regexp正则匹配解密后的资源名称
读取自定义字段property: custom_fields.<name>name 为 Passbolt 中的字段显示名称

需要记住的限制:

  • Passbolt Provider 是只读的,PushSecretDeleteSecret等写入操作未实现,相关用法请参考仓库中其他支持读写的 Provider(如 docs/provider/aws-secrets-manager.md);
  • find.name.regexp为必填项,缺失会直接报错;
  • 按名称查找会对所有资源解密后再过滤,V5 资源较多时需评估性能开销;
  • 更完整的 SecretStore / ExternalSecret 通用字段说明可参阅 docs/api/secretstore.md 与 docs/api/externalsecret.md。

本文所有配置示例均可在仓库 docs/snippets 目录中找到对应 YAML,Provider 实现与测试代码位于 providers/v1/passbolt,类型定义位于 apis/externalsecrets/v1/secretsstore_passbolt_types.go,便于读者进一步深入验证。

【免费下载链接】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),仅供参考

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

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

立即咨询