External Secrets Operator PushSecret dataTo 实战:从 Kubernetes Secret 批量推送密钥到云厂商

发布时间:2026/9/17 7:11:20
External Secrets Operator PushSecret dataTo 实战:从 Kubernetes Secret 批量推送密钥到云厂商 External Secrets Operator PushSecret dataTo 实战从 Kubernetes Secret 批量推送密钥到云厂商【免费下载链接】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-secretsdataTo是 External Secrets OperatorESO中PushSecret的核心扩展字段它让你无需逐条枚举data条目即可将 Kubernetes Secret 中的全部或经过正则过滤、重写后的部分密钥批量推送到外部密钥提供商支持「每键一个变量」与「打包成一个命名 JSON 对象」两种模式。读完本文你将掌握dataTo的模式选择、各主流提供商AWS、Azure、GCP、Vault、GitHub Actions、Doppler 等的完整配置示例、match/rewrite/metadata/conversionStrategy的用法以及控制器底层的执行链路与错误处理行为可直接落地到你的 PushSecret 配置中。dataTo 是什么用一段 YAML 取代 N 行样板配置传统的PushSecret通过spec.data逐个声明「源 Secret 的哪个 key → 推到提供商的哪个 remoteKey」。当源 Secret 有 20 个 key 时就需要 20 行几乎一模一样的样板 YAML而且一旦应用代码新增了 key 而忘记同步 PushSecret 配置该 key 就会静默地永远推不到提供商同步漂移。spec.dataTo正是为解决这类出站K8s → 提供商批量推送问题而设计的它与ExternalSecret的dataFrom入站批量拉取形成对称dataFrom 从提供商拉数据到 K8sdataTo 从 K8s推数据到提供商。方向一目了然命名上的对称性也便于记忆和检索。相关设计动机详见 docs/design/pushsecret-datato.md。两种模式一览dataTo支持两种截然不同的工作模式选择哪一个完全取决于你所用提供商的密钥模型模式何时使用remoteKeyPer-key逐键提供商以「一个命名变量/条目对应一个密钥」建模如 GitHub Actions、Doppler不设置Bundle打包提供商把结构化配置存为「一个命名密钥」承载 JSON 载荷如 AWS Secrets Manager、Azure Key Vault、GCP Secret Manager、Vault必填在 apis/externalsecrets/v1alpha1/pushsecret_types.go 中PushSecretDataTo结构体清晰定义了remoteKey的语义// RemoteKey is the name of the single provider secret that will receive ALL // matched keys bundled as a JSON object (e.g. {DB_HOST:...,DB_USER:...}). // When set, per-key expansion is skipped and a single push is performed. // When not set, each matched key is pushed as its own individual provider secret. RemoteKey string json:remoteKey,omitempty即设置了remoteKey就进入 Bundle 模式跳过逐键展开执行单次推送不设置则每个匹配的 key 独立成为提供商侧的一个 secret/变量。如何选择正确的模式Per-key 模式环境变量型提供商像GitHub Actions和Doppler这类提供商把每个密钥建模为独立的命名变量——Kubernetes Secret 里的每个 key 恰好对应提供商中的一个变量。此时不要设置remoteKeykey 名本身就会成为提供商的变量名# GitHub Actions / Doppler —— 每个 key 一个变量 dataTo: - storeRef: name: github-store # 不设置 remoteKey —— 每个 K8s key 成为独立的 GitHub secret match: regexp: ^APP_假设 K8s Secret 中有APP_TOKEN和APP_ENV推送到 GitHub Actions 后的结果APP_TOKEN → value of APP_TOKEN APP_ENV → value of APP_ENVBundle 模式命名密钥型提供商AWS Secrets Manager、Azure Key Vault、GCP Secret Manager、HashiCorp Vault这类提供商把密钥建模为「一个命名对象承载一段 JSON 载荷」。使用remoteKey为这个对象命名所有匹配到的 key 会被打包成一个 JSON 对象推入其中# AWS SM / Azure KV / GCP SM / Vault —— 所有 key 打包进一个命名密钥 dataTo: - storeRef: name: aws-store remoteKey: my-app/config # AWS Secrets Manager 的 secret 名 match: regexp: ^DB_推送到 AWS Secrets Manager 后的结果my-app/config → {DB_HOST:localhost,DB_USER:admin,DB_PASS:s3cr3t}⚠️在命名密钥型提供商上省略remoteKey的后果如果在 AWS Secrets Manager 这类提供商上省略remoteKeydataTo会回退到 per-key 模式为每个匹配的 key 各创建一个 AWS secretDB_HOST、DB_USER、DB_PASS各自成为独立 secret。这在 AWS 上几乎永远不会是你想要的结果——针对 AWS SM、Azure KV、GCP SM、Vault 时务必设置remoteKey。Provider 参考表ProviderSecret 模型用remoteKey备注AWS Secrets Manager命名密钥JSON是remoteKey secret 名store 的prefix会前置拼接AWS Parameter Store命名参数是remoteKey 参数路径AWS Certificate Manager命名证书是remoteKeyexternal-secrets-remote-keytag 值Azure Key Vault命名 secret/key/cert是remoteKey 对象名GCP Secret Manager命名密钥是remoteKey secret IDHashiCorp Vault命名路径JSON是remoteKey Vault 路径Oracle Vault命名密钥是remoteKey secret 名Kubernetes命名 Secret是remoteKey 目标 Secret 名Bitwarden命名条目是remoteKey item keyGitHub Actions环境变量每 key 一个否key 名 Actions secret 名Doppler环境变量每 key 一个否key 名 Doppler 变量名Webhook可配置视实现而定请查阅你的 webhook 实现各 Provider 实战示例AWS Secrets Manager⚠️prefix remoteKey 拼接后的名称AWS SecretStore 的prefix会被前置拼接到每个remoteKey上。如果 store 配置了prefix: myapp/而dataTo的remoteKey: db-config最终 AWS secret 名是myapp/db-config不是db-config。一个常见误区是既设置prefix: secrets-sync-temp/又设置remoteKey: secrets-sync-temp结果创建出secrets-sync-temp/secrets-sync-temp——而不是你想要的secrets-sync-temp。若希望 secret 名恰好是secrets-sync-temp要么去掉 store 上的 prefix要么把remoteKey只写成后缀部分。让值在 AWS Console 中可读默认情况下 ESO 以二进制SecretBinary存储 secret 值AWS Console 可能把二进制 secret 显示为空白或不可读。在metadata中加上secretPushFormat: string即可把 JSON 以可读的SecretString形式存储。apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: aws-store spec: provider: aws: service: SecretsManager region: us-east-1 # 不设置 prefix —— remoteKey 即完整 secret 名。 # 若设置 prefix最终名称 prefix remoteKey。 --- apiVersion: external-secrets.io/v1alpha1 kind: PushSecret metadata: name: push-to-aws spec: secretStoreRefs: - name: aws-store kind: SecretStore selector: secret: name: app-secrets # 含 DB_HOST、DB_USER、DB_PASS 的 K8s Secret dataTo: - storeRef: name: aws-store remoteKey: my-app/db-config # → AWS secret 名恰为 my-app/db-config match: regexp: ^DB_ metadata: apiVersion: kubernetes.external-secrets.io/v1alpha1 kind: PushSecretMetadata spec: secretPushFormat: string # 以 SecretString 存储Console 中可读推送到 AWS Secrets Manager 后的结果my-app/db-config → {DB_HOST:localhost,DB_USER:admin,DB_PASS:s3cr3t}⚠️metadata 必须是完整的 PushSecretMetadata 包装结构dataTo.metadata不是普通的 key-value 映射。它必须是一个合法的PushSecretMetadata对象包含apiVersion、kind和spec。直接把secretPushFormat: string放在metadata:下会导致解析错误。这也是PushSecretDataTo.Metadata字段在 API 类型中被声明为*apiextensionsv1.JSONapis/externalsecrets/v1alpha1/pushsecret_types.go的原因——它接收的是任意合法的、结构由提供商决定的 JSON 对象。带 store prefix 时# SecretStore 配置了 prefix: myapp/ # dataTo remoteKey: db-config # → AWS secret 名myapp/db-configAWS Certificate ManagerACM 将 TLS 证书作为单个资源导入。源 Kubernetes Secret 必须是kubernetes.io/tls类型且同时包含tls.crtPEM 编码的叶子证书可附带中间证书和tls.keyPEM 编码的私钥。remoteKey成为external-secrets-remote-keytag 的值——ESO 在后续 reconcile 时正是靠这个 tag 定位证书的。⚠️不要过滤掉tls.crt或tls.keyACM provider 始终从源 secret 读取tls.crt和tls.key。如果match.regexp排除了其中任何一个推送会以key tls.crt not found or empty失败。要么完全省略match要么写一个同时包含两个 key 的模式例如^tls\.。apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: aws-acm-store spec: provider: aws: service: CertificateManager region: us-east-1 --- apiVersion: external-secrets.io/v1alpha1 kind: PushSecret metadata: name: push-tls-to-acm spec: secretStoreRefs: - name: aws-acm-store kind: SecretStore selector: secret: name: my-tls-cert # 含 tls.crt 和 tls.key 的 kubernetes.io/tls Secret dataTo: - storeRef: name: aws-acm-store remoteKey: my-app-cert # → external-secrets-remote-key tag metadata: apiVersion: kubernetes.external-secrets.io/v1alpha1 kind: PushSecretMetadata spec: tags: # 可选导入证书上的额外 AWS 资源 tag environment: prod team: platform推送到 ACM 的结果证书上会带有managed-byexternal-secrets、external-secrets-remote-keymy-app-cert以及metadata.spec.tags中自定义的 tag。保留 tagmanaged-by和external-secrets-remote-key无法通过metadata覆盖。带 store prefix 时# SecretStore 配置了 prefix: certs/ # dataTo remoteKey: my-app-cert # → external-secrets-remote-key tag 值certs/my-app-certAzure Key VaultdataTo: - storeRef: name: azure-store remoteKey: app-db-config # Azure Key Vault secret 名 match: regexp: ^DB_GCP Secret ManagerdataTo: - storeRef: name: gcp-store remoteKey: projects/my-project/secrets/app-db-config match: regexp: ^DB_HashiCorp VaultdataTo: - storeRef: name: vault-store remoteKey: secret/data/myapp/db # Vault 路径KV v2 风格 match: regexp: ^DB_GitHub ActionsdataTo: - storeRef: name: github-store # 不设置 remoteKey —— 每个 K8s key 成为独立的 Actions secret match: regexp: ^DEPLOY_结果生成名为DEPLOY_TOKEN、DEPLOY_ENV等独立的 GitHub Actions secrets。DopplerdataTo: - storeRef: name: doppler-store # 不设置 remoteKey —— 每个 K8s key 成为独立的 Doppler 变量用 match 过滤要推送的 key使用match.regexp只推送源 Secret 中一部分 key省略时推送全部 keydataTo: - storeRef: name: aws-store remoteKey: myapp/db-secrets match: regexp: ^DB_ # 只推送以 DB_ 开头的 keydataTo: - storeRef: name: aws-store remoteKey: myapp/all-secrets # 不写 match → 推送源 Secret 中的全部 key从源码看匹配逻辑由 pkg/controllers/pushsecret/pushsecret_controller.go 中的matchKeys函数实现PushSecretDataToMatch.RegExp为空或 nil 时匹配所有 key非空时对每个 key 执行正则匹配。正则非法时matchKeys返回错误PushSecret 进入错误状态并在.status.conditions中给出细节。用 rewrite 做 key 名变换rewrite仅在 per-key 模式不设置remoteKey下生效。它在 key 名成为提供商变量/secret 名之前对 key 名做变换。有两种重写类型Regexp 重写dataTo: - storeRef: name: github-store match: regexp: ^db- rewrite: - regexp: source: ^db- target: DATABASE_ # db-host → DATABASE_hosttarget支持捕获组如$1、$2复用ExternalSecret的ExternalSecretRewriteRegexp类型。Template 重写Go 模板dataTo: - storeRef: name: github-store rewrite: - transform: template: {{ .value | upper }} # db-host → DB-HOST.value中承载的是 key 名本身可使用任意 Go 模板函数upper、lower、trimPrefix等。此类型复用ExternalSecretRewriteTransform。链式重写多条 rewrite 按顺序依次应用——每条 rewrite 看到的是上一条的输出dataTo: - storeRef: name: github-store match: regexp: ^prod-db- rewrite: - regexp: {source: ^prod-, target: } # prod-db-host → db-host - regexp: {source: ^db-, target: DATABASE_} # db-host → DATABASE_hostBundle 模式下 rewrite 被忽略设置remoteKey后key 名不再作为提供商路径使用——只有它们的值出现在 JSON 对象中。此时 rewrite 条目会被静默忽略。这一点在 API 层就有硬性约束PushSecretDataTo上的 CRD XValidation 规则明确声明remoteKey与rewrite互斥见 pushsecret_types.go 中的remoteKey and rewrite are mutually exclusive: rewrite is only supported in per-key mode (without remoteKey)。从实现看控制器使用rewriteWithKeyMapping()而非ExternalSecret的esutils.RewriteMap()执行重写它返回source key → 重写后 key的映射用于后续的冲突检测重复 remote key与状态追踪而RewriteMap只是原地变换map[string][]byte无法追踪原始 key 与最终 key 的对应关系。在同一 PushSecret 中使用多个 dataTo 条目把匹配到的 key 拆分到同一 PushSecret 内的不同目标# AWS两个独立 secret各按类别划分 dataTo: - storeRef: name: aws-store remoteKey: myapp/database match: regexp: ^DB_ - storeRef: name: aws-store remoteKey: myapp/api match: regexp: ^API_# GitHub把不同环境变量组推到不同 store dataTo: - storeRef: name: github-prod-store match: regexp: ^PROD_ - storeRef: name: github-staging-store match: regexp: ^STAGING_组合 dataTo 与显式 data批量默认 逐个例外对于同一个源 key显式的data条目总是覆盖dataTo。可以借此实现「批量推送默认规则 单独例外」spec: dataTo: - storeRef: name: aws-store remoteKey: myapp/config # 默认把所有 key 打包到这里 data: - match: secretKey: MASTER_PASSWORD remoteRef: remoteKey: myapp/security/master-password # 该 key 拥有独立 secret控制器在合并时会对冲突做归一化处理resolveSourceKeyConflicts/mergeDataEntriespushsecret_controller.go负责剔除被显式data覆盖的dataTo条目且比较时使用原始未做 Unicode 转换的K8s key 名。conversionStrategykey 名编码转换conversionStrategy: ReverseUnicode会在匹配与推送之前解码 Unicode 转义后的 key 名。它先于match和rewrite生效dataTo: - storeRef: name: aws-store remoteKey: myapp/config conversionStrategy: ReverseUnicode典型场景是与ExternalSecret上的conversionStrategy: Unicode配对使用后者把非法字符转义为 Unicode 编码以规避命名冲突详见 getallsecrets 指南。API 枚举值只有两个pushsecret_types.goNone默认不做转换和ReverseUnicode反转 Unicode 转义序列。实现上控制器在expandSingleDataTo中先调用esutils.ReverseKeys(dataTo.ConversionStrategy, secret.Data)对 key 做转换再执行正则匹配因此match.regexp看到的是转换后的 key 名。错误处理行为速查情况行为match中正则非法PushSecret 进入错误状态查看.status.conditionsrewrite 产生空 key调谐失败并在错误中指明违规的源 key两个条目产生相同 remote key调谐失败列出所有冲突的源match没有匹配到任何 key不是错误输出 info 日志PushSecret 保持 ReadystoreRef不在secretStoreRefs中apply 时校验报错关于最后一行dataTo的每个条目都必须指定storeRefname或labelSelector二选一这是设计上为防止「意外推送到所有 store」而强制要求的见 API 上的 XValidation 规则storeRef must specify either name or labelSelector。控制器侧的validateDataToStoreRefs与validateDataToMatchesResolvedStores会在调谐前校验 storeRef 合法性且 storeRef 引用的 store 必须出现在spec.secretStoreRefs中。与 PushSecret 其他特性的交互dataTo不是孤立功能它与 PushSecret 既有特性协同工作spec.template模板在dataTo展开之前应用dataTo的match针对的是模板输出后的 keyupdatePolicy: IfNotExists按条目生效——如果远端 secret 已存在则跳过该推送deletionPolicy: Delete所有dataTo展开的条目都会被记录在status.syncedPushSecrets结构为map[storeName]map[remoteKey]PushSecretData中源 Secret 被删除时所有被追踪的提供商 secret 会被清理conversionStrategy在 key 匹配和重写之前应用正则模式看到的是转换后的 key 名。最佳实践清单命名密钥型提供商务必设置remoteKeyAWS SM、Azure KV、GCP SM、Vault——省略会变成每 key 一个 secret这些提供商上几乎从不想要这种行为环境变量型提供商绝不设置remoteKeyGitHub Actions、Doppler——key 名本身就是变量名打包前先过滤——用match.regexp明确哪些 key 会进入 bundle避免无意中把敏感 key 也推出去先验证正则模式——写 pattern 之前先检查源 Secret 的 keykubectl get secret my-secret -o jsonpath{.data} | jq keys用显式data处理例外——常见情况走dataTo批量推送需要自定义路径或属性的 key 用显式data条目监控状态——用kubectl get pushsecret name -o yaml检查同步错误PushSecret 的Readycondition 的reason字段会给出Synced/Errored/SourceDeleted等状态见 pushsecret_types.go。完整的可运行示例还可以参考仓库中的 pushsecret-datato-basic.yaml、pushsecret-datato-regex.yaml、pushsecret-datato-rewrite.yaml、pushsecret-datato-chained.yaml、pushsecret-datato-template.yaml、pushsecret-datato-override.yaml 等片段以及 tests/pushsecret_test.yaml 中的端到端测试用例。相关文档PushSecret 指南 —— PushSecret 基础用法updatePolicy、deletionPolicy、备份场景、密钥轮换PushSecret API 参考 ——dataTo各字段的完整 API 规范模板化指南 —— 高级模板用法ExternalSecret dataFrom 重写 —— 入站方向的镜像功能从提供商批量拉取密钥dataTo 设计文档 ——dataTo的设计动机、备选方案与边界情况【免费下载链接】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),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询