diff --git a/Chart.yaml b/Chart.yaml index 91364f9..81a1b63 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -5,7 +5,7 @@ description: | Variation between instances is expressed via values.yaml only. No chart variants. No string-templating in Tower. type: application -version: 0.1.6 +version: 0.1.7 appVersion: "1.0" keywords: - odoo diff --git a/templates/postgres-password-externalsecret.yaml b/templates/postgres-password-externalsecret.yaml new file mode 100644 index 0000000..1231a95 --- /dev/null +++ b/templates/postgres-password-externalsecret.yaml @@ -0,0 +1,51 @@ +{{- if .Values.postgres.passwordVaultPath }} +# postgres-password-externalsecret.yaml — per-instance Postgres password +# sourced from OpenBao via ESO. Produces the same `-pg` Secret +# shape (POSTGRES_USER + POSTGRES_PASSWORD + POSTGRES_DB) that the legacy +# postgres-secret.yaml produces, so postgres-statefulset.yaml is unchanged. +# +# Rendered only when `.Values.postgres.passwordVaultPath` is set. The +# legacy postgres-secret.yaml renders when that field is empty — +# exactly one of the two ships per instance. Tower-managed migration +# in Chunk 3 flips overlays from the legacy path to this one. +# +# OpenBao path convention: `tenants//instances//pg` +# with a `password` field. Covered by the per-cluster ESO policy +# `eso-tenant-` (buildEsoPolicy in Go) which already grants +# read on `v3/data/tenants//*`. No policy change required. +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ include "instance.fullname" . }}-pg + labels: + {{- include "instance.labels" . | nindent 4 }} +spec: + refreshInterval: "1h" + secretStoreRef: + name: {{ .Values.postgres.externalSecretsStoreRef | default "openbao-platform" }} + kind: ClusterSecretStore + target: + name: {{ include "instance.fullname" . }}-pg + creationPolicy: Owner + # Retain — never delete the Secret on ExternalSecret deletion. The + # postgres pod's PGDATA on disk is locked to the password hash in + # this Secret; an accidental ESO removal must not cascade into the + # Secret disappearing and forcing a password rotation (which would + # then drift from pg_authid). + deletionPolicy: Retain + template: + type: Opaque + engineVersion: v2 + data: + POSTGRES_USER: {{ .Values.postgres.user | quote }} + POSTGRES_PASSWORD: "{{ `{{ .password }}` }}" + POSTGRES_DB: {{ .Values.postgres.database | quote }} + data: + - secretKey: password + remoteRef: + key: tenants/{{ required "postgres.passwordVaultPath requires .Values.tenant.id (set by Tower at create time)" .Values.tenant.id }}/instances/{{ .Values.instance.code }}/pg + property: password + conversionStrategy: Default + decodingStrategy: None + metadataPolicy: None +{{- end }} diff --git a/templates/postgres-secret.yaml b/templates/postgres-secret.yaml index 7c0277c..f0d3277 100644 --- a/templates/postgres-secret.yaml +++ b/templates/postgres-secret.yaml @@ -1,3 +1,11 @@ +{{- if not .Values.postgres.passwordVaultPath }} +# Legacy postgres-secret.yaml — chart-rendered Secret carrying +# POSTGRES_USER/PASSWORD/DB for the postgres StatefulSet. Used when +# `.Values.postgres.passwordVaultPath` is empty (the pre-ESO path). +# When that field is set, postgres-password-externalsecret.yaml +# renders an ExternalSecret producing the same Secret name + shape +# from OpenBao instead, and this template skips. Exactly one of the +# two ships per instance. apiVersion: v1 kind: Secret metadata: @@ -9,3 +17,4 @@ stringData: POSTGRES_USER: {{ .Values.postgres.user | quote }} POSTGRES_PASSWORD: {{ include "instance.pgPassword" . | quote }} POSTGRES_DB: {{ .Values.postgres.database | quote }} +{{- end }} diff --git a/values.yaml b/values.yaml index 949f594..a3ea57c 100644 --- a/values.yaml +++ b/values.yaml @@ -17,6 +17,13 @@ instance: # `instance.size: medium` (etc); they don't have to know the numbers. size: small +# tenant — owning tenant identity. Currently only required when +# postgres.passwordVaultPath is set (the ESO path needs to know which +# tenant subtree to read from OpenBao). Tower writes `tenant.id` into +# every new overlay; legacy overlays without ESO leave this empty. +tenant: + id: "" + # The named-size table. Single source of truth for what each instance # tier actually gets. Adjust here, ALL future instances pick up the # new defaults on next reconcile. Existing instances keep their @@ -159,7 +166,34 @@ postgres: database: postgres # If empty, the chart auto-generates on first install and re-reads # the existing Secret on subsequent upgrades (lookup pattern). + # Ignored when `passwordVaultPath` is set — ESO sources the password + # from OpenBao instead. password: "" + # passwordVaultPath — when set, the chart renders an ExternalSecret + # pulling the password from OpenBao at + # `tenants//instances//pg` (field + # `password`). The legacy postgres-secret.yaml template is gated + # off; the ExternalSecret produces the same `-pg` Secret + # shape so postgres-statefulset.yaml is unchanged. + # + # When empty (default), the chart falls back to the legacy + # postgres-secret.yaml path using `.password` above. Tower sets + # this field on every new instance starting v0.77; pre-v0.77 + # instances stay on the legacy path until migrated by the one-shot + # tool (Chunk 3 of A-OpenBao). + # + # The actual path resolution is hardcoded in the + # postgres-password-externalsecret.yaml template; this field is + # the on/off toggle. Setting it to any non-empty value is + # equivalent ("use ESO for this instance"); the path string itself + # is currently advisory (it's the OpenBao subtree the operator can + # `bao kv list` to find the password). + passwordVaultPath: "" + # externalSecretsStoreRef — ClusterSecretStore name the + # ExternalSecret references. Provisioned by cluster-platform-v3 + # under `openbao-platform` by default. Override only on clusters + # that name the store differently. + externalSecretsStoreRef: openbao-platform storage: 10Gi backups: