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,构建时通过cloudru或all_providersbuild tag 启用:
func init() { // Register cloudru provider esv1.Register(cloudru.NewProvider(), cloudru.ProviderSpec(), cloudru.MaintenanceStatus()) }提供者的能力被声明为只读(SecretStoreReadOnly),即只能把云端密钥拉取到集群内,不能反向推送或删除云端密钥,见 provider.go 的Capabilities()实现以及 client.go 中PushSecret、DeleteSecret、SecretExists均返回"不支持"的错误。因此本文所有场景均为"从 Cloud.ru 拉取到 Kubernetes"的单向同步。
认证机制:服务账号与 Access Key
使用 Cloud.ru Secret Manager 前,需要完成两步准备工作:
- 在 Cloud.ru Console 中创建服务账号(Service Account)。
- 为该服务账号创建 Access Key(访问密钥),获得一对
key_id与key_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_id与key_secret。
从源码看,ESO 并不是用 curl 方式换取令牌,而是通过 gRPC 调用 Cloud.ru IAM 服务(iam-sdk的AuthServiceClient.GetToken),并且对令牌做了进程内缓存:APIClient保存accessToken与accessTokenExpiresAt,只有当令牌过期(ExpiresIn到期)时才重新向 IAM 换取,避免每个请求都重复认证,见 adapter/csm_client.go。同时,提供者按discoveryURL + KeyID + Secret组合对客户端做缓存(见 provider.go),相同凭据复用同一连接,减少重复建连与令牌获取。
API 端点地址并非硬编码,而是从https://api.cloud.ru/endpoints动态发现(secret-manager与iam两个产品端点),也可以通过环境变量CLOUDRU_DISCOVERY_URL、CLOUDRU_CSM_ADDRESS、CLOUDRU_IAM_ADDRESS覆盖,细节见 endpoints.go 与 provider.go。
在 Cloud.ru 中创建密钥
通过控制台创建
- 登录 Cloud.ru 控制台。
- 点击左上角菜单,滚动到
Management(管理)区域,选择Secret Manager。 - 点击
Create secret(创建密钥)。 - 填写密钥名称(secret name)与密钥值(secret value)。
- 点击
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_TOKEN | Cloud.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 的property、dataFrom用法直接对应。
创建 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.secretRef与projectID均为必填项,且projectID必须是合法的 UUID 格式,否则校验失败;accessKeyIDSecretRef与accessKeySecretSecretRef也会通过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: productionfind的底层实现是GetAllSecrets(client.go):tags、name.regexp、path至少设置其一,否则返回错误 "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 相关报错:使用
property或dataFrom.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),仅供参考