使用 External Secrets Operator 对接 Segura® DevOps Secret Manager(DSM)同步 Kubernetes Secrets
【免费下载链接】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)中通过 senhasegura provider 对接 Segura® 的 DevOps Secret Manager(DSM)模块,将 DSM 中由应用授权(Authorization)关联的密钥自动同步为 Kubernetes Secret。你将掌握 DSM 应用的 OAuth2 认证配置、SecretStore 与 ClusterSecretStore 两种存储的定义方式,以及按 Secret Identifier 精确同步、按 key/value 自动展开等多种 ExternalSecret 同步模式,并了解其底层 API 调用与校验逻辑。
一、整体原理:ESO 与 Segura® DSM 的对接方式
External Secrets Operator 支持通过 provider 插件读取第三方密钥管理系统的数据,并将数据以 Kubernetes Secret 的形式注入集群。senhasegura provider 是其官方维护的 provider 之一(维护状态见 providers/v1/senhasegura/provider.go 中的MaintenanceStatus(),返回MaintenanceStatusMaintained),专门用于同步 Segura® DevOps Secret Manager(DSM)模块中的密钥。
对接链路的核心流程如下:
- ESO 控制器读取
SecretStore/ClusterSecretStore中spec.provider.senhasegura的配置; - 控制器通过
NewClient创建 senhasegura 客户端(providers/v1/senhasegura/provider.go):先用auth.Authenticate完成 OAuth2 认证拿到访问令牌,再根据provider.Module判断目标模块;当module: DSM时返回dsm.New(isoSession)创建的 DSM 客户端; ExternalSecret控制器调用客户端的GetSecret(单键同步)或GetSecretMap(多键同步)拉取 DSM 数据并写入目标 Kubernetes Secret。
需要说明的是,从 dsm.go 的源码结构看,senhasegura provider 当前只实现了只读能力:Capabilities()返回SecretStoreReadOnly(provider.go),PushSecret、DeleteSecret、SecretExists等写操作接口均返回errNotImplemented,因此它目前只用于“拉取并同步”,不支持 PushSecret 回写 DSM。
二、认证:在 Segura® DSM 中为应用配置授权
Segura® DSM 采用“应用授权”机制:管理员在 DSM 中创建应用(Application),为应用配置授权(Authorization)并把需要分发的密钥关联到该授权上。ESO 侧通过 DSM 应用授权模式完成认证。在 Segura® 侧创建授权与密钥的具体步骤请参考 Segura® 官方的 DSM 授权管理文档。
ESO 侧需要准备一个 Kubernetes Secret 存放认证参数(即 DSM 应用的 Client Secret),示例见 docs/snippets/senhasegura-dsm-secret.yaml:
apiVersion: v1 kind: Secret metadata: name: senhasegura-dsm-auth stringData: CLIENT_SECRET: "CHANGEME"将该 Secret 创建到集群后,把CHANGEME替换为 DSM 应用中真实的 Client Secret 值。
从认证实现看,providers/v1/senhasegura/auth/iso.go 中的GetIsoToken会向https://<senhasegura-url>/iso/oauth2/token发送POST请求,携带grant_type=client_credentials、client_id和client_secret参数(application/x-www-form-urlencoded),返回 JSON 中的access_token即作为后续访问 DSM API 的 Bearer Token。认证参数clientId直接写在 Store 中,而clientSecretSecretRef引用上文创建的 Kubernetes Secret,密钥不会明文暴露在 Store 资源里。
三、配置 SecretStore / ClusterSecretStore
要开始同步密钥,需要定义一个SecretStore(命名空间级)或ClusterSecretStore(集群级)资源,指定 senhasegura provider 并在 DSM 模块中完成认证配置。
SecretStore(命名空间级)
示例来自 docs/snippets/senhasegura-dsm-secretstore.yaml:
apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: senhasegura spec: provider: senhasegura: url: "https://senhasegura.changeme.com" module: DSM # Select senhasegura DSM module to sync secrets auth: clientId: "CHANGEME" clientSecretSecretRef: name: senhasegura-dsm-auth key: CLIENT_SECRET ignoreSslCertificate: false # OptionalClusterSecretStore(集群级)
示例来自 docs/snippets/senhasegura-dsm-clustersecretstore.yaml。与SecretStore唯一的区别是,ClusterSecretStore是集群级资源,它引用的认证 Secret 可能位于其他命名空间,因此需要显式声明namespace字段:
apiVersion: external-secrets.io/v1 kind: ClusterSecretStore metadata: name: senhasegura spec: provider: senhasegura: url: "https://senhasegura.changeme.com" module: DSM # Select senhasegura DSM module to sync secrets auth: clientId: "CHANGEME" clientSecretSecretRef: name: senhasegura-dsm-auth key: CLIENT_SECRET namespace: senhasegura # Namespace of Secret "senhasegura-dsm-auth" ignoreSslCertificate: false # Optional字段说明
字段定义见 apis/externalsecrets/v1/secretstore_senhasegura_types.go:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
url | string | 是 | Segura® 实例的地址。根据 provider.go 的校验逻辑,URL 必须为 HTTPS 且必须包含 Host,否则 Store 校验失败 |
module | string | 是 | 目标模块,当前唯一合法值为DSM(kubebuilder 枚举约束见 secretstore_senhasegura_types.go)。若配置其他值,NewClient会返回unknown senhasegura Provider Service错误 |
auth.clientId | string | 是 | DSM 应用的 Client ID,缺失时校验报错missing senhasegura authentication Client ID |
auth.clientSecretSecretRef | SecretKeySelector | 是 | 存放 Client Secret 的 Kubernetes Secret 引用,ClusterSecretStore需额外指定namespace |
ignoreSslCertificate | bool | 否 | 是否跳过 TLS 证书校验,默认false。仅用于测试或内网自签名证书场景,生产环境应保持关闭 |
值得注意的细节是,Store 在创建时就会经过 Validating Webhook 校验(ValidateStore,见 provider.go):URL 必须是 HTTPS、Host 非空、Client ID 非空。也就是说,非法配置的 Store 根本不会被创建出来,这能把认证错误提前到配置阶段暴露。
四、同步前准备:DSM 中的示例密钥
本文后续示例假设 Segura® DSM 中已存在三个密钥,均通过应用授权关联到 ESO 使用的 DSM 应用:
- Secret Identifier:
api-settings
URL=https://example.com/api/example TOKEN=example-token-value- Secret Identifier:
db-settings
DB_HOST='db.example' DB_PORT='5432' DB_USERNAME='example' DB_PASSWORD='example'- Secret Identifier:
hsm-settings
HSM_ADDRESS='hsm.example' HSM_PORT='9223'这里的“Secret Identifier”对应 DSM API 响应中每个密钥的identity字段,也是 ExternalSecret 中remoteRef.key的取值。
五、按 Secret Identifier 同步单个 ExternalSecret
当只需要同步指定标识符下的全部或部分 key/value 时,使用ExternalSecret的data字段。
行为说明
- 若
remoteRef.property为空,则返回该标识符下全部 key/value 的 JSON 编码值(对应源码 dsm.go 中GetSecret的ref.Property == ""分支,会对v.Data做json.Marshal); - 若指定了
remoteRef.property,则只返回该 key 对应的原始字符串值; - 此模式下,Kubernetes Secret 的数据键名(
.data.X)由secretKey显式指定,可以自由覆盖命名,例如API_SETTINGS和API_SETTINGS_TOKEN。
配置示例
示例来自 docs/snippets/senhasegura-dsm-external-secret-single.yaml:
apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: example-secret spec: refreshInterval: "30s" secretStoreRef: name: senhasegura kind: SecretStore target: name: example-secret data: # Define API_SETTINGS Kubernetes Secret key, with json-encoded values from senhasegura secret with identifier "api-settings" - secretKey: API_SETTINGS remoteRef: key: api-settings # Secret Identifier in senhasegura # Define API_SETTINGS_TOKEN Kubernetes Secret key, with single secret key (TOKEN) from senhasegura as string - secretKey: API_SETTINGS_TOKEN remoteRef: key: api-settings # Secret Identifier in senhasegura property: TOKEN # Optional, Key name within secret同步结果
应用该 ExternalSecret 后,控制器会创建名为example-secret的 Kubernetes Secret,其.data内容为:
API_SETTINGS='[{"TOKEN":"example-token-value","URL":"https://example.com/api/example"}]' API_SETTINGS_TOKEN='example-token-value'其中API_SETTINGS是api-settings全部键值的 JSON 编码(注意它是数组形式,对应源码中v.Data的类型为[]map[string]string),API_SETTINGS_TOKEN则是TOKEN键的原始字符串。
六、按 Secret Identifier 自动展开多个 key/value
如果应用需要多个密钥,且希望 DSM 中的每个 key/value 自动成为 Kubernetes Secret 的独立.data字段,无需为每个密钥单独编写data条目,可以使用dataFrom的extract方式,把多个 Secret Identifier 聚合到同一个 ExternalSecret 中。
配置示例
示例来自 docs/snippets/senhasegura-dsm-external-secret-multiple.yaml:
apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: example-secret spec: refreshInterval: "30s" secretStoreRef: name: senhasegura kind: SecretStore target: name: example-secret dataFrom: # Define Kubernetes Secret key with any k/v pair in senhasegura Secret with identifier "api-settings" or "db-settings" - extract: key: api-settings - extract: key: db-settings同步结果
dataFrom.extract底层调用客户端的GetSecretMap(见 dsm.go):该方法遍历 DSM 响应中匹配ref.Key的密钥,将其data中的每一对 key/value 直接写入secretDatamap。最终生成的 Kubernetes Secret.data为:
URL='https://example.com/api/example' TOKEN='example-token-value' DB_HOST='db.example' DB_PORT='5432' DB_USERNAME='example' DB_PASSWORD='example'这种模式的优势在于:一个 ExternalSecret 即可聚合多个 Secret Identifier 的所有键值,应用侧无需关心键的重新命名,直接使用原始键名即可。
七、同步模式与底层实现对照
下表汇总了两种同步模式与 senhasegura DSM 客户端源码实现的对应关系,便于排查与理解行为差异:
| ExternalSecret 写法 | 底层调用 | 行为 | 源码位置 |
|---|---|---|---|
data+remoteRef.property为空 | GetSecret | 返回该 Identifier 全部键值的 JSON 编码 | dsm/dsm.go |
data+remoteRef.property指定键 | GetSecret | 返回指定键的原始字符串 | dsm/dsm.go |
dataFrom.extract | GetSecretMap | 该 Identifier 下每个 key/value 展开为独立.data字段 | dsm/dsm.go |
dataFrom.find | GetAllSecrets | 尚未实现(返回errNotImplemented) | dsm/dsm.go |
关于“同步授权下全部密钥”的说明
原文档中注释掉了一段“Sync all secrets from DSM authorization”的示例(使用dataFrom.find: {})。从源码确认,GetAllSecrets目前返回errNotImplemented(dsm/dsm.go),源码注释中也有 TODO 说明该功能未来计划支持按名称正则匹配或标签匹配。因此当前版本请不要使用find方式同步全部密钥,应使用上文基于 Secret Identifier 的data/dataFrom.extract方式。
八、底层 API 与常见问题排查
DSM 数据获取流程
无论是单键还是多键同步,最终都会调用 dsm.go 中的FetchSecrets(),其行为如下:
- 向
https://<senhasegura-url>/iso/dapp/application发起GET请求; - 请求头携带
Authorization: Bearer <iso token>(Token 来自第二步认证); - 响应体为
IsoDappResponse结构(dsm.go),包含application.secrets数组,每个密钥含secret_id、secret_name、identity、version、expiration_date、engine以及data([]map[string]string); - 非 200 状态码返回
received invalid HTTP code from senhasegura,响应中response.error == true时返回received application error from senhasegura。
Store 连通性校验
Validate()方法(dsm.go)通过实际调用FetchSecrets()验证连接与凭据是否有效,成功返回ValidationResultReady,失败返回ValidationResultError。该结果会体现在SecretStore资源的status中,是排查认证问题最直接的入口。
常见问题排查清单
- Store 无法创建/被 Webhook 拒绝:检查
url是否为 HTTPS 且包含主机名、clientId是否填写,对应校验逻辑见 provider.go; - SecretStore 状态为 Error:检查 DSM 应用的
clientId/clientSecret是否匹配,授权是否正确关联了密钥,以及 ESO 运行环境能否访问 Segura® 地址; - 报错
cannot do request in senhasegura, SSL certificate is valid ?:通常是 TLS 证书问题。生产环境应确保证书有效;仅在测试环境可临时开启ignoreSslCertificate: true; find方式不生效:GetAllSecrets尚未实现(见上文),请改用 Secret Identifier 方式;- 同步到集群的 Secret 数据与 DSM 不一致:注意
refreshInterval决定重新拉取周期(示例中为30s),DSM 侧密钥更新后需等待下一个刷新周期。
九、测试与验证
senhasegura provider 的单元测试位于 providers/v1/senhasegura/provider_test.go,端到端测试用例位于 e2e/suites/provider/senhasegura_dsm_test.go(若该文件不存在,可查看 e2e/suites/provider 目录下以senhasegura命名的用例)。此外,仓库中还提供了可直接用于演练的清单文件:
- 认证 Secret:docs/snippets/senhasegura-dsm-secret.yaml
- SecretStore:docs/snippets/senhasegura-dsm-secretstore.yaml
- ClusterSecretStore:docs/snippets/senhasegura-dsm-clustersecretstore.yaml
- 单键同步 ExternalSecret:docs/snippets/senhasegura-dsm-external-secret-single.yaml
- 多键展开 ExternalSecret:docs/snippets/senhasegura-dsm-external-secret-multiple.yaml
建议的验证流程:
- 依次应用认证 Secret、SecretStore / ClusterSecretStore;
- 检查 Store 的
status是否为 Ready(Validate()已连通测试); - 应用 ExternalSecret 后,用
kubectl get secret <target-name> -o yaml检查.data字段是否符合预期; - 修改 DSM 中的密钥值,等待
refreshInterval后确认 Kubernetes Secret 是否同步更新。
十、总结
通过 senhasegura provider,External Secrets Operator 可以安全、自动地将 Segura® DSM 模块中的密钥同步为 Kubernetes Secret:
- 认证采用 DSM 应用的 OAuth2
client_credentials流程,Client Secret 存放于 Kubernetes Secret 中,避免明文泄漏; - 存储支持命名空间级
SecretStore与集群级ClusterSecretStore,Store 创建即校验 URL、HTTPS、Client ID 等关键配置; - 同步支持按 Secret Identifier 单键取值(
data+property)、整份 JSON 取值(property留空)以及多键自动展开(dataFrom.extract)三种方式,find(全量同步)能力尚未实现; - 能力边界:当前 provider 为只读,不支持 PushSecret 回写。
在实施时,请始终把ignoreSslCertificate保持为false,并严格遵循最小权限原则为 DSM 应用授权,确保密钥分发链路的安全。
【免费下载链接】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),仅供参考