Skip to content

Design: passwordRotation for postgres/mariadb via a credentials-owning controller #71

Description

@scooby87

Context

cozystack/cozystack#4078 drops plaintext users[].password from the postgres and mariadb apps: passwords are now always chart-generated into the <release>-credentials Secret and read only from there. That part is sound and stays in #4078. The PR also introduced passwordRotation — an integer counter a tenant bumps to regenerate every managed password — and review showed that piece needs a different foundation, so it is being split out here for its own design discussion before implementation.

Why rotation cannot live in the chart render

While the credentials Secret is rendered by the chart, its cleartext value is part of the Helm release manifest, and helm-controller retains the last MaxHistory revisions of that manifest. A passwordRotation bump performed in response to a leak therefore does not retire the leaked password: it stays in up to several stored release revisions until unrelated upgrades push it out of history. Rotation whose purpose is to revoke, but which cannot revoke, is worse than no rotation — the credential the tenant used to control in one scrubbable place now lives in several the tenant cannot see. This is Helm behaviour, not something a template can work around.

Three further problems share the same root — there is no durable state and no actor outside the render:

  • No completion signal. A tenant writes an integer and nothing observable follows: no status, no condition, no event, no way to answer "did the new password reach the database". If the applying Job/operator step fails, the Secret advertises a credential the database never received and nothing says so.
  • Fragile lookup. Rotation state is a counter compared against a value read back with Helm's lookup, which returns empty during any render without API access; the template cannot distinguish "first install" from "the read did not happen", and both paths regenerate and overwrite the Secret while the database keeps the old password — a silent credential split.
  • Two mechanisms, and root outside both. Postgres converges through a post-upgrade hook Job; MariaDB through the operator reacting to a watch-labelled Secret. Different windows, different failure modes, one values field. MariaDB root is excluded from rotation entirely (the operator only applies rootPasswordSecretKeyRef at datadir bootstrap), so the feature does not cover the most privileged account on that engine.

Proposed direction

Give rotation an owner outside the Helm render: a controller that creates and holds the credentials Secret, with the chart referencing it by name rather than rendering it. The controller applies the rotation and reports the outcome in status. That also dissolves the problems above by construction — the swallowed first bump exists only because there is nowhere to keep the baseline, and the missing completion signal only because there is nothing to carry it.

Requirements for the design:

  • The credentials Secret is owned by the controller, not by the chart render, so its value never enters the Helm release manifest or its retained history.
  • Rotation reports a completion signal (a status/condition on the app CR) distinguishing "requested", "applied to the database", and "failed".
  • A single mechanism covers both engines, or the per-engine difference is explicit and justified.
  • root (MariaDB) is covered, or its exclusion is a deliberate, documented decision rather than an accident of the delivery path.
  • The baseline for an existing release is durable state, so the first bump is not spent establishing it.

Open questions

  • Where the controller lives (extend an existing operator vs a small dedicated one) and how it holds the Secret relative to Flux/Helm ownership.
  • How rotation interacts with a restore into a copy (the recovered roles carry the source hashes until reconciled — see #4078's restore handling).
  • Whether root rotation on MariaDB is feasible at all given rootPasswordSecretKeyRef is bootstrap-only, or needs an out-of-band reset.

Links

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions