External Secrets Operator 集成 Cloud.ru Secret Manager:认证、SecretStore 与 ExternalSecret 完整配置指南
2026/9/17 23:21:00 网站建设 项目流程

External Secrets Operator 集成 Cloud.ru Secret Manager:认证、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)官方提供者文档的实战解读,围绕 Cloud.ru Secret Manager(CSM)与 Key Manager 密钥管理系统协同工作的安全模型,完整讲解从服务账号与 Access Key 认证、在控制台或 REST API 中创建密钥,到编写 SecretStore 与多种形态 ExternalSecret(单值、Multi KeyValue、dataFrom提取、按名称/标签/路径检索)的端到端配置,并结合当前仓库providers/v1/cloudru的源码实现,说明底层认证、令牌缓存与 API 调用机制。

Cloud.ru 提供者概览

External Secrets Operator 通过与 Cloud.ru 云平台的 Secret Manager 服务集成,把云端的密钥安全地同步为 Kubernetes Secret。Cloud.ru Secret Manager 与 Key Manager 密钥管理系统协同工作,确保密钥在存储与使用过程中的加密安全。

在 External Secrets Operator 中,该提供者的注册位于 pkg/register/cloudru.go,构建时通过cloudruall_providersbuild tag 启用:

func init() { // Register cloudru provider esv1.Register(cloudru.NewProvider(), cloudru.ProviderSpec(), cloudru.MaintenanceStatus()) }

提供者的能力被声明为只读SecretStoreReadOnly),即只能把云端密钥拉取到集群内,不能反向推送或删除云端密钥,见 provider.go 的Capabilities()实现以及 client.go 中PushSecretDeleteSecretSecretExists均返回"不支持"的错误。因此本文所有场景均为"从 Cloud.ru 拉取到 Kubernetes"的单向同步。

认证机制:服务账号与 Access Key

使用 Cloud.ru Secret Manager 前,需要完成两步准备工作:

  1. 在 Cloud.ru Console 中创建服务账号(Service Account)。
  2. 为该服务账号创建 Access Key(访问密钥),获得一对key_idkey_secret

重要:与 SecretManager API 交互时必须使用访问令牌(access token)。拿到 Access Key 后,可通过下面这条 curl 命令换取令牌:

curl -i --data-urlencode 'grant_type=access_key' \ --data-urlencode "client_id=$KEY_ID" \ --data-urlencode "client_secret=$SECRET" \ https://id.cloud.ru/auth/system/openid/token

其中$KEY_ID$SECRET即上一步创建的 Access Key 的key_idkey_secret

从源码看,ESO 并不是用 curl 方式换取令牌,而是通过 gRPC 调用 Cloud.ru IAM 服务(iam-sdkAuthServiceClient.GetToken),并且对令牌做了进程内缓存APIClient保存accessTokenaccessTokenExpiresAt,只有当令牌过期(ExpiresIn到期)时才重新向 IAM 换取,避免每个请求都重复认证,见 adapter/csm_client.go。同时,提供者按discoveryURL + KeyID + Secret组合对客户端做缓存(见 provider.go),相同凭据复用同一连接,减少重复建连与令牌获取。

API 端点地址并非硬编码,而是从https://api.cloud.ru/endpoints动态发现(secret-manageriam两个产品端点),也可以通过环境变量CLOUDRU_DISCOVERY_URLCLOUDRU_CSM_ADDRESSCLOUDRU_IAM_ADDRESS覆盖,细节见 endpoints.go 与 provider.go。

在 Cloud.ru 中创建密钥

通过控制台创建

  1. 登录 Cloud.ru 控制台。
  2. 点击左上角菜单,滚动到Management(管理)区域,选择Secret Manager
  3. 点击Create secret(创建密钥)。
  4. 填写密钥名称(secret name)与密钥值(secret value)。
  5. 点击Create(创建)。

通过 SecretManager API 创建

也可以直接调用 SecretManager API 创建密钥:

curl --location 'https://secretmanager.api.cloud.ru/v1/secrets' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ${ACCESS_TOKEN}' \ --data '{ "description": "your secret description", "labels": { "env": "production" }, "name": "my_first_secret", "parent_id": "50000000-4000-3000-2000-100000000001", "payload": { "data": { "value": "aGksIHRoZXJlJ3Mgbm90aGluZyBpbnRlcmVzdGluZyBoZXJlCg==" } } }'

请求体各字段说明:

字段说明
ACCESS_TOKENCloud.ru API 的访问令牌,见上文"认证机制"一节
parent_id父服务实例标识符,即 ServiceInstanceID。获取方式:在个人账户左上角点击九宫格按钮,选择Management → Secret Manager,复制 Service Instance ID 字段的值
name密钥的名称
description密钥的描述
labels密钥的标签(tags),用于检索
payload.data.value密钥值,base64 编码后的内容

注意:如需在 Cloud.ru 中创建多键值(Multi KeyValue)密钥,可以直接把 JSON 对象作为密钥值:

{ "key1": "value1", "key2": "value2" }

该 JSON 形态与后文 ExternalSecret 的propertydataFrom用法直接对应。

创建 SecretStore:接入 Cloud.ru 凭据

第一步:创建 Kubernetes Secret 存放凭据

ESO 需要从集群内的 Kubernetes Secret 读取 Access Key 用于认证,先创建如下csm-secret

apiVersion: v1 kind: Secret metadata: name: csm-secret labels: type: csm type: Opaque stringData: key_id: '000000000000000000001' key_secret: '000000000000000000002'

字段说明:

  • key_id:Access Key 的key_id
  • key_secret:Access Key 的key_secret

从源码看,ESO 通过KubeCredentialsResolver使用SecretKeyRef从指定命名空间解析这两个字段,见 resolver.go。如果解析失败(如 Secret 不存在或键缺失),会返回 "failed to resolve accessKeyID / accessKeySecret" 错误。

第二步:创建 SecretStore

创建指向csm-secret的 SecretStore(关于 SecretStore 的通用说明可参考 SecretStore 文档):

apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: csm spec: provider: cloudrusm: auth: secretRef: accessKeyIDSecretRef: name: csm-secret key: key_id accessKeySecretSecretRef: name: csm-secret key: key_secret projectID: 50000000-4000-3000-2000-100000000001

字段说明:

  • accessKeyIDSecretRef:引用存放 Access Key 的 Kubernetes Secret(name为 Secret 名,key为键名)。
  • accessKeySecretSecretRef:同上,引用 Access Key 的 secret 部分。
  • projectID:项目标识符。获取方式:在个人账户左上角点击项目名称,在弹出的窗口中点击所需项目名称旁边的三个点,再点击 "Copy the Project ID"(复制项目 ID)。

cloudrusm提供者的类型定义位于 apis/externalsecrets/v1/secretstore_cloudru_types.go。从 provider.go 的ValidateStore可知,创建连接前会做严格校验:auth.secretRefprojectID均为必填项,且projectID必须是合法的 UUID 格式,否则校验失败;accessKeyIDSecretRefaccessKeySecretSecretRef也会通过ValidateReferentSecretSelector校验引用有效性。因此配置时请务必确保projectID形如50000000-4000-3000-2000-100000000001的标准 UUID。

创建 ExternalSecret:五种典型用法

以下 ExternalSecret 均引用上文名为csm的 SecretStore(关于 ExternalSecret 的完整字段说明见 ExternalSecret 文档)。

1. 经典单值密钥(非 JSON)

apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: csm-ext-secret spec: refreshInterval: 10s secretStoreRef: name: csm kind: SecretStore target: name: my-awesome-secret creationPolicy: Owner data: - secretKey: target_key remoteRef: key: my_first_secret # 也可以使用 secret.id(例如 50000000-4000-3000-2000-100000000001)

remoteRef.key既可以是密钥名称,也可以是密钥的 UUID 标识符。从 client.go 的accessSecret实现可见其判断逻辑:若key能被解析为 UUID,则直接按密钥 ID 调用AccessSecretVersion;否则视为密钥名称,通过AccessSecretVersionByPath按路径访问。另外,remoteRef.version支持形如"1"的整型版本号或关键字latest(空值也按 latest 处理),传入其他非数字值会返回ErrInvalidSecretVersion错误。

2. 从 Multi KeyValue 密钥中取单个键(属性必须是 JSON 格式)

apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: csm-ext-secret spec: refreshInterval: 10s secretStoreRef: name: csm kind: SecretStore target: name: my-awesome-secret creationPolicy: Owner data: - secretKey: target_key remoteRef: key: my_first_secret # 也可以使用 secret.id(例如 50000000-4000-3000-2000-100000000001) property: cloudru.secret.key # 密钥值中的 JSON 路径

property支持点号分隔的嵌套 JSON 路径。底层使用gjson解析,见 client.go:如果指定了property但密钥值不是合法 JSON,会明确报错"expecting the secret in JSON format";如果 JSON 路径不存在,则报错"the requested property does not exist in secret"。

3. 提取全部字段(值必须是 JSON 格式)

apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: csm-ext-secret spec: refreshInterval: 10s secretStoreRef: name: csm kind: SecretStore target: name: my-awesome-secret creationPolicy: Owner dataFrom: - extract: key: my_first_secret # 也可以使用 secret.id(例如 50000000-4000-3000-2000-100000000001)

该用法对应GetSecretMap实现(client.go):密钥值按 JSON 对象解析后,每个顶层键都会成为 Kubernetes Secret 中的一个条目,嵌套对象则保留其 JSON 文本(测试用例见 client_test.go)。若密钥值不是合法 JSON,同步会失败。

4. 按名称或标签(tags)检索

dataFrom.find支持单独或组合使用name(正则)与tags过滤要同步的密钥集合:

apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: csm-ext-secret spec: refreshInterval: 10s secretStoreRef: name: csm kind: SecretStore target: name: my-awesome-secret creationPolicy: Owner dataFrom: - find: # 可以单独或同时使用 name 和 tags 来检索密钥 tags: env: production name: regexp: "my.*secret"

5. 按路径检索(支持与名称、标签任意组合)

  • 仅按路径,取回该路径下的全部密钥:
dataFrom: - find: # 获取指定路径下的所有密钥 path: "oss/snmp-auths"
  • 按路径 + 名称过滤
dataFrom: - find: # 获取路径下名称匹配模式的密钥 path: "oss/snmp-auths" name: regexp: ".*"
  • 按路径 + 标签过滤
dataFrom: - find: # 获取路径下带有指定标签的密钥 path: "oss/snmp-auths" tags: env: production
  • 按路径 + 名称 + 标签组合过滤
dataFrom: - find: # 获取路径下名称匹配模式且带有指定标签的密钥 path: "oss/snmp-auths" name: regexp: "auth.*" tags: env: production

find的底层实现是GetAllSecrets(client.go):tagsname.regexppath至少设置其一,否则返回错误 "at least one of the following fields must be set: tags, name, path";检索通过 CSM v2SearchSecretRequest完成(深度-1表示递归搜索,见 adapter/csm_client.go),随后对每个命中的密钥取其latest版本。最终输出的 Secret 键名取自密钥的路径,其中的/会被替换为_以保证是合法的 Kubernetes Secret 键名(可由conversionStrategy控制,测试验证见 client_test.go)。

常见问题与注意事项

  • 认证失败:确认key_id/key_secret对应的 Access Key 有效,且服务账号具备访问 Secret Manager 的权限;令牌由 IAM 服务颁发,令牌过期后 ESO 会自动重新获取。
  • projectID不合法:SecretStore 校验要求projectID必须是标准 UUID,否则提供者拒绝创建客户端,请从控制台复制正确的 Project ID。
  • JSON 相关报错:使用propertydataFrom.extract时,云端密钥值必须是合法 JSON;使用property时指定的 JSON 路径必须真实存在。
  • 只读能力:Cloud.ru 提供者仅支持从云端拉取密钥(ReadOnly),不支持 PushSecret、删除或存在性检查,相关调用会直接返回"not supported / not implemented"错误。
  • 版本选择remoteRef.version只接受整型字符串或latest,其他值会报ErrInvalidSecretVersion

参考与延伸

  • 提供者注册:pkg/register/cloudru.go
  • API 类型定义:apis/externalsecrets/v1/secretstore_cloudru_types.go
  • 提供者实现:providers/v1/cloudru/secretmanager/provider.go、client.go、adapter/csm_client.go
  • 单元测试:providers/v1/cloudru/secretmanager/client_test.go
  • 通用资源说明: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

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

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

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

立即咨询