Skip to content

feat(obj): source linode accessKeyId from obj-secrets - #3466

Open
aweingarten wants to merge 5 commits into
linode:mainfrom
aweingarten:feat/obj-accesskeyid-from-obj-secrets
Open

feat(obj): source linode accessKeyId from obj-secrets#3466
aweingarten wants to merge 5 commits into
linode:mainfrom
aweingarten:feat/obj-accesskeyid-from-obj-secrets

Conversation

@aweingarten

Copy link
Copy Markdown
Contributor

📌 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: 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 provisions and 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 already handled cleanly, and standing up a git-sync reconciler with a Contents: write token to do it.

This is option 1 from the issue:

  • values-schema.yaml: obj.provider.linode.accessKeyId gains x-secret: '', so it flows into obj-secrets as provider_linode_accessKeyId through the existing sealed-secret machinery — the same route secretAccessKey already takes.
  • All six consumers template it from the ExternalSecret instead of inlining it: loki, harbor (×2), gitea, keycloak, kubeflow-pipelines.

Rotation becomes: write one Secret. No git writes at all.

🔍 Reviewer Notes

  • No new contract. The key name (provider_linode_accessKeyId) is derived by the existing buildSecretToNamespaceMap path→underscore rule, not hardcoded — same rule that produces provider_linode_secretAccessKey.
  • required is unchanged. accessKeyId stays in the required list next to secretAccessKey; this changes where the value is stored, not whether it must be supplied.
  • Upgrade path — worth a second opinion. generateSecrets() merges input values over generated ones and then picks by schema x-secret path, so an accessKeyId arriving in VALUES_INPUT is picked up and sealed on the next bootstrap, and stripAllSecrets drops 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 where accessKeyId lives only in the values repo and never in VALUES_INPUT/stored cluster secrets, that case would want a values-changes.yaml entry to relocate it — happy to add one, I just couldn't find such a path.
  • Fixtures were updated to match the new classification: accessKeyId moves out of tests/fixtures/env/settings/obj.yaml and into the obj-secrets sealed-secret fixture, mirroring secretAccessKey.
  • Consumers outside this repo that read obj.provider.linode.accessKeyId from settings (console/API) would need the same treatment — flagging in case that's in scope for you.

Verified locally: jest (including sealed-secrets and bootstrap suites), eslint, tsc --noEmit all clean. The helmfile-backed gates (validate-values, validate-templates, lint:hf) need helmfile/gucci, which I don't have locally — leaving those to CI.

🧹 Checklist

  • Code is readable, maintainable, and robust.
  • Unit tests added/updated

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
Copilot AI lite review requested due to automatic review settings August 3, 2026 14:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.accessKeyId as a secret in values-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 accessKeyId from the ExternalSecret data map (mirroring secretAccessKey).
  • 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.

Copilot AI review requested due to automatic review settings August 3, 2026 14:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings August 4, 2026 07:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Object storage (linode): source accessKeyId from obj-secrets like secretAccessKey, so key rotation needs no git write

3 participants