feat(obj): source linode accessKeyId from obj-secrets - #3466
Open
aweingarten wants to merge 5 commits into
Open
Conversation
The two halves of the Linode object-storage credential were sourced asymmetrically: `secretAccessKey` came from the `obj-secrets` Secret (ESO-friendly, rotatable without touching git), while `accessKeyId` was inlined into every consumer's ExternalSecret template straight from git settings. Linode OBJ keys rotate as a pair, so an operator that rotates them out-of-band had to push the new `accessKeyId` back into the values repo on every rotation — carrying a non-secret identifier through git purely because its paired secret is handled cleanly, and requiring a git-sync reconciler with a write-scoped token to do it. Mark `accessKeyId` as an x-secret so it lands in `obj-secrets` next to `secretAccessKey`, and template it from there in all six consumers (loki, harbor ×2, gitea, keycloak, kubeflow-pipelines). Rotation now means writing one Secret, no git writes at all. The field stays `required`, exactly like `secretAccessKey` — this changes where the value is stored, not whether it must be supplied. Refs linode#3459
aweingarten
requested review from
Ani1357,
CasLubbers,
ferruhcihan,
j-zimnowoda and
merll
as code owners
July 28, 2026 14:03
Contributor
There was a problem hiding this comment.
Pull request overview
This PR makes Linode object-storage credentials symmetric by sourcing both accessKeyId and secretAccessKey from the obj-secrets Secret (via ExternalSecrets), eliminating the need to inline accessKeyId from git-backed settings and enabling rotation by writing a single Secret.
Changes:
- Mark
obj.provider.linode.accessKeyIdas a secret invalues-schema.yaml(x-secret: '') so it is handled by the existing sealed-secret pipeline. - Update all Linode OBJ consumers (Loki, Harbor x2, Gitea, Keycloak, Kubeflow Pipelines) to template
accessKeyIdfrom the ExternalSecret data map (mirroringsecretAccessKey). - Update fixtures and add a unit test to assert both Linode credential halves are classified as secrets.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| values/loki/loki-raw.gotmpl | Switch AWS access key ID to be sourced from obj-secrets via ExternalSecret templating. |
| values/kubeflow-pipelines/kubeflow-pipelines-raw.gotmpl | Source accessKeyId from obj-secrets and template into the target Secret. |
| values/keycloak/keycloak-raw.gotmpl | Source S3_STORAGE_ACCOUNT from ExternalSecret-provided accessKeyId. |
| values/harbor/harbor-raw.gotmpl | Update both Harbor S3 credential secrets to pull accessKeyId from obj-secrets (two consumers). |
| values/gitea/gitea-raw.gotmpl | Source S3_STORAGE_ACCOUNT from ExternalSecret-provided accessKeyId. |
| values-schema.yaml | Classify obj.provider.linode.accessKeyId as a secret (x-secret: ''). |
| tests/fixtures/env/settings/obj.yaml | Remove inline accessKeyId from git-backed settings fixture. |
| tests/fixtures/env/manifests/namespaces/apl-secrets/sealedsecrets/obj-secrets.yaml | Add provider_linode_accessKeyId to the obj-secrets sealed-secret fixture. |
| src/common/obj-secret-paths.test.ts | Add test ensuring both Linode credential halves are discovered as secret schema paths. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📌 Summary
Fixes #3459 — implements the direction @j-zimnowoda agreed with there ("it makes perfect sense to keep the id and token all together").
The two halves of the Linode object-storage credential were sourced asymmetrically:
secretAccessKeycame from theobj-secretsSecret (ESO-friendly, rotatable without touching git), whileaccessKeyIdwas inlined into every consumer's ExternalSecret template straight from git settings.Linode OBJ keys rotate as a pair, so an operator that provisions and rotates them out-of-band had to push the new
accessKeyIdback into the values repo on every rotation — carrying a non-secret identifier through git purely because its paired secret is already handled cleanly, and standing up a git-sync reconciler with aContents: writetoken to do it.This is option 1 from the issue:
values-schema.yaml:obj.provider.linode.accessKeyIdgainsx-secret: '', so it flows intoobj-secretsasprovider_linode_accessKeyIdthrough the existing sealed-secret machinery — the same routesecretAccessKeyalready takes.Rotation becomes: write one Secret. No git writes at all.
🔍 Reviewer Notes
provider_linode_accessKeyId) is derived by the existingbuildSecretToNamespaceMappath→underscore rule, not hardcoded — same rule that producesprovider_linode_secretAccessKey.requiredis unchanged.accessKeyIdstays in therequiredlist next tosecretAccessKey; this changes where the value is stored, not whether it must be supplied.generateSecrets()merges input values over generated ones and then picks by schema x-secret path, so anaccessKeyIdarriving inVALUES_INPUTis picked up and sealed on the next bootstrap, andstripAllSecretsdrops it from the settings written to disk. That self-migrates installs where the value comes from the helm values. If you know of a supported topology whereaccessKeyIdlives only in the values repo and never inVALUES_INPUT/stored cluster secrets, that case would want avalues-changes.yamlentry to relocate it — happy to add one, I just couldn't find such a path.accessKeyIdmoves out oftests/fixtures/env/settings/obj.yamland into theobj-secretssealed-secret fixture, mirroringsecretAccessKey.obj.provider.linode.accessKeyIdfrom settings (console/API) would need the same treatment — flagging in case that's in scope for you.Verified locally:
jest(includingsealed-secretsandbootstrapsuites),eslint,tsc --noEmitall clean. The helmfile-backed gates (validate-values,validate-templates,lint:hf) needhelmfile/gucci, which I don't have locally — leaving those to CI.🧹 Checklist