Skip to content

feat(templates): collapse-aware single-source infra secrets + CI enforcement - #1470

Merged
guimoreirar merged 26 commits into
developfrom
helm-chart-standardization-revamp
Jun 15, 2026
Merged

feat(templates): collapse-aware single-source infra secrets + CI enforcement#1470
guimoreirar merged 26 commits into
developfrom
helm-chart-standardization-revamp

Conversation

@fredcamaral

Copy link
Copy Markdown
Member

Pull Request Type

  • Pipeline
  • Documentation
  • Midaz
  • Plugin Access Manager
  • Reporter
  • Plugin Fees
  • Plugin BR PIX Direct JD
  • Plugin BR PIX Indirect BTG
  • Plugin BR PIX Switch
  • Otel Collector
  • Fetcher
  • Matcher
  • Flowker
  • Underwriter

Summary

Repo-wide standardization revamp: brings all 20 charts onto a single, CI-enforced chart contract and fixes a class of latent infra-credential bugs that rendered cleanly but failed authentication at runtime.

Why

Charts derived bundled Bitnami subchart Secret/Service names with naive printf "%s-%s" .Release.Name "<sub>". That ignores the Bitnami name collapse (release named after the subchart → Secret is just <subchart>, not <release>-<subchart>), nameOverride/fullnameOverride, and auth.existingSecret. Result: dangling secretKeyRefCreateContainerConfigError, or — worse — the app reading an empty password while the subchart auto-generated its own, an auth failure invisible until the first query. None of this was caught by CI, because the render gate used release name = chart name, which never collapses.

What changed

Naming & secrets (the core fix)

  • All app charts derive infra Secret/Service names via common.names.dependency.fullname (collapse-aware, honors overrides and existingSecret). 11 charts rewired in the final wave; aliased deps pass the alias (e.g. access-manager's auth-database); groundhog2k/valkey.io subcharts replicate their own naming locally.
  • Single-source secrets: bundled Bitnami infra auto-generates passwords into its own Secret; apps consume via secretKeyRef; operators supply infra creds only for external infra. No duplicated infra passwords in app-owned Secrets. A hardcoded default Erlang cookie (credential leak) was zeroed.
  • Fail-loud gates: missing infra creds on the external/disabled path fail the render with an actionable message instead of dangling (e.g. reporter.rabbitmqErlangCookieRequired, midaz.mongodbAuthRequired, access-manager DB_PASSWORD, plus a consistency gate for renamed existing Secrets).
  • plugin-br-pix-indirect-btg was the last chart off-standard and in the worst state (zeroed defaults rendering clean but breaking at runtime) — fully migrated, including a fixed REDIS_HOST that pointed at a non-existent -valkey-master Service.

CI hardening

  • Validator restructured one-dir-per-tool under .github/scripts/<tool>/main.go; two committed ELF binaries removed; go vet ./... clean.
  • Path-aware secret classifier (catches nested erlangCookie.value), ConfigMap-template scanning, dual-secret rule.
  • Collapse render gate: each chart re-rendered with release name = each Bitnami dep name + a dangling-secretKeyRef assertion, with a documented allowlist (otel-api-key, kedaorg-certs). This is what makes the H1 class visible to CI.

Schemas & deps

  • Real per-chart values.schema.json for all charts via a deterministic generator: closed root (additionalProperties:false over actual top-level keys + global), opaque third-party subchart blocks. Unknown top-level keys now fail lint/install.
  • Bitnami dependency versions pinned (exact, no ranges).

Docs

  • docs/helm-chart-standard.md rewritten to prescribe the collapse-aware derivation (the old printf anti-pattern was documented as correct), fail-loud gates, schema policy, validation/collapse gate, KEDA authenticationRef rule.
  • Every chart README swept for stale claims: image tags, zeroed password defaults, single-source guidance, release-name assumptions, dependency versions/provenance.

Verification

  • validate-helm-charts --root .. --strict0 violations
  • validate-helm-charts --root .. --render-gate --allexit 0, 20/20 charts incl. collapse renders
  • helm lint → 20/20 clean
  • go vet ./... → clean
  • plugin-br-pix-indirect-btg migration verified against a 7-case scenario matrix (bundled / external+inline / external+existingSecret / external+none→fails loud / bundled+useExistingSecrets→subchart Secret still wins / collapse renders)

Checklist

  • I have tested these changes locally.
  • I have updated the documentation accordingly.
  • I have added necessary comments to the code, especially in complex areas.
  • I have ensured that my changes adhere to the project's coding standards.
  • I have checked for any potential security issues.
  • I have ensured that all tests pass.
  • I have updated the version appropriately (if applicable).
  • I have confirmed this code is ready for review.

Additional Notes

Targeting main per maintainer request (the template's default base is develop).

Scope: 209 files, +6725/−1011, 21 commits. The diff is large but the changes are systematic — the same single-source pattern applied across 20 charts plus the CI that enforces it. Reviewing one fully-migrated chart (reporter or plugin-br-pix-indirect-btg) against docs/helm-chart-standard.md is the fastest way to validate the pattern; the render gate proves the rest.

Apply the chart standard across all charts: chart-type annotations,
values.schema.json for every chart, hardened container security
contexts (allowPrivilegeEscalation, seccompProfile), emptied default
credentials and encryption keys, helpers.tpl renamed to _helpers.tpl,
secrets manifests normalized to secrets.yaml, and Chart.lock files
tracked for reproducible dependency resolution.

X-Lerian-Ref: 0x1
Add validate-helm-charts.go with a static contract check (--strict)
and a render gate (--render-gate) backed by a standard baseline config
and dummy render values for charts requiring production secrets. Wire
the render gate into the release workflow and enforce the contract via
a dedicated helm-chart-standard workflow. Track charts/*/Chart.lock in
git and use octal literal notation in existing scripts.

X-Lerian-Ref: 0x1
Document the chart contract enforced by CI and the render inventory
describing how each chart is validated. Link both from the root README
along with the local validation commands.

X-Lerian-Ref: 0x1
Remove custom `*.enabled` templates for nested dependencies from
`_helpers.tpl` in multiple charts (`plugin-access-manager`,
`plugin-fees`, `reporter`) to rely on standard Helm dependency
conditions natively. Remove hardcoded `erlangCookie` secret from
the `matcher` chart values.

Refactor the `helm-chart-standard` CI workflow to pass GitHub context
and step outputs through environment variables instead of direct string
interpolation in bash scripts, improving security and workflow stability.

Update auto-generated `helm-render-inventory.md` to reflect the latest
chart rendering statuses after these changes.
Document the single-source contract (Pattern A: app reads the Bitnami
subchart Secret via secretKeyRef; Pattern B: non-Bitnami brokers read the
app Secret via existingSecret) and the verified Bitnami Secret name/key
table in the chart standard, including the `enabled` coercion gotcha and
the valkey.io N/A note.

Add a `dual-secret-infra-password` validator rule that flags an infra
password (POSTGRES/MONGO/REDIS/DB_* PASSWORD) still gated by `required`
in a Secret template while the matching bundled Bitnami subchart is
declared as a dependency — the exact dual-secret regression the refactor
removes. The rule ignores the post-refactor conditional-write form and
external-only keys with no subchart.

X-Lerian-Ref: 0x1
Read MONGO_PASSWORD from the Bitnami mongodb subchart Secret
(<release>-mongodb, key mongodb-root-password) via a secretKeyRef env
entry instead of keeping an operator-provided copy. Adds the reusable
infraSecretRef helper, drops the review-era required gate, and guards
the three cases (bundled subchart / external inline / existingSecret) so
the external BYOC path is preserved. Slims the render fixture and updates
the README contract.

X-Lerian-Ref: 0x1
Apply the Bitnami secretKeyRef pattern to the single-subchart charts:

- plugin-br-payments: POSTGRES_PASSWORD from <release>-postgresql/password.
- plugin-br-pix-direct-jd: DATABASE_PASSWORD + POSTGRES_PASSWORD across the
  pix Deployment and job CronJob; external client/JD/Midaz secrets stay
  required.
- underwriter: Postgres primary/replica + Valkey passwords; the external
  RABBITMQ_DEFAULT_PASS stays operator-provided.
- plugin-access-manager: repoint the auth-backend, migrations and init-user
  workloads at the aliased <release>-auth-database/password Secret, drop
  DB_PASSWORD from the plugin-auth Secret, and remove the dead plaintext
  dataSourceName helper.

Each replaces the review-era required gate with the three-way
internal/external-inline/existingSecret guard and updates fixtures/READMEs.

X-Lerian-Ref: 0x1
MongoDB (Pattern A): the manager, worker and KEDA ScaledJob read
MONGO_PASSWORD from <release>-mongodb/mongodb-root-password via
secretKeyRef; drop the required gate and range it out of both component
Secrets.

RabbitMQ (Pattern B): point the groundhog2k broker at the reporter-manager
app Secret via authentication.existingSecret so the broker credentials
live only in secrets.RABBITMQ_DEFAULT_PASS (which KEDA already reads),
and add a stable, required-when-bundled RABBITMQ_ERLANG_COOKIE since
existingSecret suppresses the inline cookie. This also resolves the latent
broker/app user mismatch.

Valkey (valkey.io) exposes no Secret-based auth and ships disabled, so it
is documented as not single-sourceable. Removes the dead dataSourceName
helper.

X-Lerian-Ref: 0x1
- matcher: Postgres primary/replica + Valkey passwords via secretKeyRef.
- midaz: ledger single-sources Postgres (onboarding/transaction + replicas)
  and Mongo (onboarding/transaction) plus Valkey; crm single-sources its
  Mongo password. The single midaz pg role and root mongo user mean several
  app env names map to the same subchart key.
- plugin-br-bank-transfer: Postgres/Valkey via secretKeyRef; MongoDB is
  URI-only, so MONGO_URI is assembled on the deployment as
  mongodb://bank_transfer:$(MONGO_PASSWORD)@<release>-mongodb... with
  MONGO_PASSWORD sourced from <release>-mongodb/mongodb-passwords; the
  plaintext URI/password leave the Secret and the release-derived host
  fallback is preserved.

RabbitMQ is intentionally left operator-provided in matcher and midaz: each
ships files/rabbitmq/load_definitions.json with a baked password_hash the
broker imports at boot, a second source Pattern B cannot reconcile. This is
documented in each README as a tracked follow-up.

X-Lerian-Ref: 0x1
- flowker: URI-only app, root mongo user. MONGO_PASSWORD is read from
  <release>-mongodb/mongodb-root-password and MONGO_URI is assembled on the
  deployment via $(MONGO_PASSWORD) expansion with a release-derived host;
  the required MONGO_URI gate and plaintext URI leave the Secret. Also fixes
  a live bug where the wait-for-mongodb init container referenced deleted
  MONGO_HOST/MONGO_PORT keys and rendered `nc -z "" `.
- plugin-bc-correios: single-source POSTGRES_PASSWORD from
  <release>-postgresql/password (un-nesting env from the tracing
  conditional); valkey/CACHE_PASSWORD left untouched as it is not a live
  dual-secret, and RabbitMQ stays a documented follow-up.

Regenerate the render inventory.

X-Lerian-Ref: 0x1
Replace printf "%s-%s" .Release.Name derivations of Bitnami subchart
Secret and Service names with common.names.dependency.fullname, which
honors Bitnami release-name collapse (release name containing the
subchart name yields an uncollapsed ref to a Secret that does not
exist, so pods fail with CreateContainerConfigError) as well as
nameOverride and fullnameOverride. Applies to 11 charts; matcher and
plugin-br-payments also derive in-cluster hosts in configmap/NOTES.

The _helpers.tpl files are committed whole and also carry:
- plugin-access-manager: required gate on auth.secrets.DB_PASSWORD
  when auth-database is external/disabled (H2) and real fallbacks for
  the single-arg default no-op in the name helpers (L2)
- midaz: new midaz.mongodbAuthRequired fail gate (H3)
- reporter: rabbitmq existingSecret consistency fail gate (A1)
- plugin-fees: dead dataSourceName helper removed (L10) and
  collapse-aware mongoHost helpers (companion of the infra-host
  fallback commit)

X-Lerian-Ref: 0x1
Wire the new fail gates into their consumers so misconfiguration
fails at render time instead of dangling secretKeyRefs at runtime:

- midaz ledger and crm include midaz.mongodbAuthRequired: rendering
  with the bundled MongoDB and mongodb.auth.enabled=false now fails
  with guidance (Bitnami creates no Secret in that mode, while the
  deployments previously still referenced mongodb-root-password)
- reporter manager and worker secrets include the rabbitmq
  existingSecret consistency gate: a fullname override that breaks
  the default reporter-manager reference now fails with guidance
  instead of leaving the broker pointing at a stale Secret name

X-Lerian-Ref: 0x1
- plugin-br-bank-transfer: zero the committed RabbitMQ Erlang cookie
  (a real cluster-auth credential; the only groundhog2k chart not yet
  remediated) and drop the two never-consumed CI fixture keys
- underwriter: suppress infra POSTGRES_PASSWORD/REDIS_PASSWORD in the
  app Secret when the bundled subchart provides them (matcher's gated
  pattern), avoiding the double-definition the standard forbids; add
  the missing CI render fixture
- plugin-bc-correios: drop pre-install/pre-upgrade hook annotations
  from the app Secret so it follows the release lifecycle instead of
  being orphaned on uninstall
- plugin-access-manager: delete orphaned common.authorizer.clientSecret
- go-boilerplate-ddd: move MULTI_TENANT_REDIS_PASSWORD out of the
  ConfigMap into the Secret map
- reporter: drop inert secrets.REDIS_PASSWORD (valkey auth disabled)
  and realign values-template with values (stale MONGO_PASSWORD out,
  required RABBITMQ_ERLANG_COOKIE in)

X-Lerian-Ref: 0x1
KEDA cpu/memory scalers ignore authenticationRef; emit it only for
trigger types that consume it.

X-Lerian-Ref: 0x1
- plugin-br-pix-direct-jd: rename pix.useExistingSecrets to the
  singular useExistingSecret the templates actually read (the plural
  key was dead, silently ignoring the operator toggle)
- README dependency notes corrected to match Chart.yaml reality:
  tracer (no bundled PostgreSQL; bootstrap Job only), flowker (no
  bundled PostgreSQL; audit DB is external), lerian-notification (no
  bundled dependencies), plugin-br-pix-indirect-btg, plugin-br-pix-switch
- plugin-br-pix-indirect-btg: image tag default fallback on the three
  worker deployments, matching the pix sibling

X-Lerian-Ref: 0x1
Pin floating minor ranges in Chart.yaml to the exact patch versions
already resolved in each Chart.lock (postgresql 16.3.5, mongodb
16.4.12). A future helm dependency update can no longer silently
re-resolve to a different patch whose generated-Secret contract the
single-source pattern depends on; locks regenerated, vendored tgz
versions unchanged.

X-Lerian-Ref: 0x1
Replace the three copy-pasted generic schemas (additionalProperties
true everywhere, zero required, component keys from unrelated charts)
with real per-chart schemas produced by a committed generator. Root
objects are closed (additionalProperties false) over each chart's
actual top-level values keys plus global, so operator typos now fail
helm schema validation at render time; third-party subchart blocks
stay opaque. Regeneration is deterministic:

  cd .github/scripts && go run ./generate-values-schemas --root ../..

X-Lerian-Ref: 0x1
validate-helm-charts gains the checks that would have caught this
branch's own findings:

- render gate now also renders every chart under a release name equal
  to each Bitnami dependency name (the collapse scenario CI never
  exercised) and asserts no rendered secretKeyRef dangles, with a
  small documented allowlist for operator-provisioned Secrets
- secret classifier is path-aware: generic carriers like value under
  erlangCookie/password parents now classify as credentials (the
  blind spot that let a real committed Erlang cookie pass CI), with
  cookie/erlang added to the token list
- secret-in-configmap also scans ConfigMap templates, not only values
- isPasswordKey tightened to word boundaries (no more compass/bypass)
- migration-phase scaffolding (phaseOwner, Migration Queue) removed;
  the committed render-inventory snapshot is deleted in favor of
  on-demand generation

Scripts restructured one-directory-per-tool so go vet ./... passes;
two stale committed ELF build artifacts removed and gitignored;
workflow and doc references updated to the new paths.

X-Lerian-Ref: 0x1
- plugin-bc-correios: null the four hardcoded in-release host
  defaults so the template fallbacks become source of truth; the
  shipped CACHE_ADDR pointed at -valkey-master, a Service that does
  not exist (valkey names it -primary). Fix two fallbacks the
  literals were masking: OBJECT_STORAGE_ENDPOINT derives from
  bc-correios.fullname (the inline SeaweedFS Service name) instead of
  the release name, and RABBITMQ_HOST replicates groundhog2k's
  fullname collapse. The configmap also carries this chart's
  collapse-safe Bitnami derivations
- plugin-fees: null the hardcoded MONGO_HOST FQDN and derive the
  bundled subchart Service via the collapse-aware mongoHost helpers
  (helpers landed with the name-derivation commit); explicit value
  still wins for external MongoDB; values-template and README aligned
- plugin-br-pix-direct-jd: guard the pix and job app Secrets with
  useExistingSecret so no redundant Secret is emitted when an
  existing one is supplied (qrcode already had the guard)

X-Lerian-Ref: 0x1
Sweep all chart READMEs, the canonical standard doc, and the root
README for claims that went stale during the standardization revamp.

- helm-chart-standard.md: replace the naive printf name-derivation
  prescription with common.names.dependency.fullname (alias rule,
  collapse rationale); add fail-loud gate, values.schema.json policy,
  expanded validation (collapse render gate + allowlist), and KEDA
  cpu/memory authenticationRef sections; correct the dependency pin
  table to the exact-but-non-uniform reality.
- Chart READMEs: refresh image tag defaults, zero stale 'lerian'
  password defaults, rewrite single-source secret guidance, fix
  release-name assumptions where Secret refs are now collapse-aware,
  correct midaz RabbitMQ provenance (groundhog2k, not Bitnami), fix
  flowker MONGO_URI placement and removed-key examples, correct
  pix-switch component/image claims, refresh dependency versions.
- Drop bank-transfer phantom secret key row (key exists nowhere in
  the chart); fix Valkey repository lines to the OCI registry; fix
  fees upgrade command OCI path; use <version> placeholders in
  pix-indirect-btg install examples; correct otel matrix version.
- One values.yaml comment fix (bank-transfer MONGO_PASSWORD is only
  required for external MongoDB, not unconditionally).

X-Lerian-Ref: 0x1
Migrate the last chart off duplicated infra credentials to the
standard's Pattern A. Previously the bundled install rendered but the
app received an empty DB/Mongo password while the subcharts
auto-generated their own, failing auth at runtime.

- Add collapse-aware infraSecretRef and host helpers: postgresql ->
  key 'password' (non-root auth.username pix_btg, enablePostgresUser
  false, standalone so replica reuses the primary Secret); mongodb ->
  'mongodb-root-password' (app connects as rootUser pix_btg; the
  bootstrap job only serves global.externalMongoDefinitions, default
  off); valkey.io naming replicated locally (not Bitnami common).
- Wire DB_PASSWORD/DB_REPLICA_PASSWORD/MONGO_PASSWORD as discrete
  secretKeyRef env entries on all four deployments, layered over
  envFrom; emit them in the app Secrets only on the external path.
- Fail loud when postgresql/mongodb is external or disabled and no
  credential is supplied via component secrets, subchart
  auth.existingSecret, or component useExistingSecrets +
  existingSecretName (same trust model as the envFrom path).
- Null hardcoded release-name + namespace host defaults
  (DB_HOST/DB_REPLICA_HOST/MONGO_HOST/REDIS_HOST) and derive them
  collapse-aware; the old REDIS_HOST '-valkey-master' suffix was
  wrong (valkey.io renders a single '<release>-valkey' Service).
- REDIS_PASSWORD stays operator-provided: valkey.io exposes no
  Secret-based password to source from.
- Update README to the single-source model.

Verified: strict 0 violations; render-gate --all exit 0 incl. collapse
renders (mongodb/postgresql/valkey); helm lint clean; scenario matrix
bundled / external+inline / external+existingSecret / external+none
(fails loud) / bundled+useExistingSecrets (subchart Secret still wins).

X-Lerian-Ref: 0x1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants