Deliver database credentials as a rotatable mounted directory
AUDIT-WP-0005-T02 (progress). rapp-postgres has landed platform-pg with the
audit_core database, roles, and dynamic credential provisioning, so
audit-core's side is now built against it.
In-cluster delivery is a mounted directory rather than environment variables.
A dynamic lease rotates while the pod runs and an env var is fixed at process
start, so env delivery would force a restart on every rotation - and every
restart is a delivery gap, which is what this task forbids.
CredentialDirectory is re-read on every connection attempt via psycopg_pool's
callable kwargs, so a rotated lease takes effect with no restart. Rotation is
logged by password fingerprint, never by value.
deploy/externalsecrets.yaml follows the ClusterSecretStore -> ExternalSecret ->
Secret pattern already used by activity-core and rapp-qonto, at a 15m refresh
rather than the default 1h since the interval bounds how long a revoked lease
can stay mounted. All manifests validated --dry-run=server --validate=strict.
The rotation test was initially vacuous: it passed against a deliberately naive
implementation that read credentials once at startup, because pooled sessions
stay authenticated after a password change and nothing forced a reconnect. It
now terminates the role's sessions first, and is verified to fail against the
naive implementation and pass against the real one. Tests 82 -> 84.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 01:36:28 +02:00
|
|
|
# Credential delivery for audit-core (AUDIT-WP-0005-T02).
|
|
|
|
|
#
|
2026-08-13 00:58:49 +02:00
|
|
|
# Two sources, because they are two kinds of secret:
|
|
|
|
|
# - database leases: VaultDynamicSecret -> ExternalSecret (this file)
|
|
|
|
|
# - sender registry: ClusterSecretStore openbao-audit-core -> ExternalSecret
|
Deliver database credentials as a rotatable mounted directory
AUDIT-WP-0005-T02 (progress). rapp-postgres has landed platform-pg with the
audit_core database, roles, and dynamic credential provisioning, so
audit-core's side is now built against it.
In-cluster delivery is a mounted directory rather than environment variables.
A dynamic lease rotates while the pod runs and an env var is fixed at process
start, so env delivery would force a restart on every rotation - and every
restart is a delivery gap, which is what this task forbids.
CredentialDirectory is re-read on every connection attempt via psycopg_pool's
callable kwargs, so a rotated lease takes effect with no restart. Rotation is
logged by password fingerprint, never by value.
deploy/externalsecrets.yaml follows the ClusterSecretStore -> ExternalSecret ->
Secret pattern already used by activity-core and rapp-qonto, at a 15m refresh
rather than the default 1h since the interval bounds how long a revoked lease
can stay mounted. All manifests validated --dry-run=server --validate=strict.
The rotation test was initially vacuous: it passed against a deliberately naive
implementation that read credentials once at startup, because pooled sessions
stay authenticated after a password change and nothing forced a reconnect. It
now terminates the role's sessions first, and is verified to fail against the
naive implementation and pass against the real one. Tests 82 -> 84.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 01:36:28 +02:00
|
|
|
#
|
2026-08-13 00:58:49 +02:00
|
|
|
# audit-core never holds a credential in its own configuration; it reads
|
|
|
|
|
# whatever is currently mounted. Apply order: namespace, ESO token,
|
|
|
|
|
# ClusterSecretStore, VaultDynamicSecret, this, migrate Job, Deployment.
|
Deliver database credentials as a rotatable mounted directory
AUDIT-WP-0005-T02 (progress). rapp-postgres has landed platform-pg with the
audit_core database, roles, and dynamic credential provisioning, so
audit-core's side is now built against it.
In-cluster delivery is a mounted directory rather than environment variables.
A dynamic lease rotates while the pod runs and an env var is fixed at process
start, so env delivery would force a restart on every rotation - and every
restart is a delivery gap, which is what this task forbids.
CredentialDirectory is re-read on every connection attempt via psycopg_pool's
callable kwargs, so a rotated lease takes effect with no restart. Rotation is
logged by password fingerprint, never by value.
deploy/externalsecrets.yaml follows the ClusterSecretStore -> ExternalSecret ->
Secret pattern already used by activity-core and rapp-qonto, at a 15m refresh
rather than the default 1h since the interval bounds how long a revoked lease
can stay mounted. All manifests validated --dry-run=server --validate=strict.
The rotation test was initially vacuous: it passed against a deliberately naive
implementation that read credentials once at startup, because pooled sessions
stay authenticated after a password change and nothing forced a reconnect. It
now terminates the role's sessions first, and is verified to fail against the
naive implementation and pass against the real one. Tests 82 -> 84.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 01:36:28 +02:00
|
|
|
---
|
|
|
|
|
apiVersion: external-secrets.io/v1
|
|
|
|
|
kind: ExternalSecret
|
|
|
|
|
metadata:
|
|
|
|
|
name: audit-core-database
|
|
|
|
|
namespace: audit-core
|
|
|
|
|
spec:
|
|
|
|
|
# Shorter than the platform default of 1h: these are dynamic leases, and the
|
|
|
|
|
# refresh interval bounds how long a revoked lease can remain mounted.
|
|
|
|
|
refreshInterval: 15m
|
2026-08-13 10:27:13 +02:00
|
|
|
secretStoreRef:
|
|
|
|
|
kind: ClusterSecretStore
|
|
|
|
|
name: openbao-audit-core-database
|
Deliver database credentials as a rotatable mounted directory
AUDIT-WP-0005-T02 (progress). rapp-postgres has landed platform-pg with the
audit_core database, roles, and dynamic credential provisioning, so
audit-core's side is now built against it.
In-cluster delivery is a mounted directory rather than environment variables.
A dynamic lease rotates while the pod runs and an env var is fixed at process
start, so env delivery would force a restart on every rotation - and every
restart is a delivery gap, which is what this task forbids.
CredentialDirectory is re-read on every connection attempt via psycopg_pool's
callable kwargs, so a rotated lease takes effect with no restart. Rotation is
logged by password fingerprint, never by value.
deploy/externalsecrets.yaml follows the ClusterSecretStore -> ExternalSecret ->
Secret pattern already used by activity-core and rapp-qonto, at a 15m refresh
rather than the default 1h since the interval bounds how long a revoked lease
can stay mounted. All manifests validated --dry-run=server --validate=strict.
The rotation test was initially vacuous: it passed against a deliberately naive
implementation that read credentials once at startup, because pooled sessions
stay authenticated after a password change and nothing forced a reconnect. It
now terminates the role's sessions first, and is verified to fail against the
naive implementation and pass against the real one. Tests 82 -> 84.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 01:36:28 +02:00
|
|
|
target:
|
|
|
|
|
name: audit-core-database
|
|
|
|
|
creationPolicy: Owner
|
|
|
|
|
deletionPolicy: Retain
|
|
|
|
|
template:
|
|
|
|
|
engineVersion: v2
|
|
|
|
|
# One value per file. The pod mounts this Secret as a directory and
|
|
|
|
|
# audit-core re-reads it on every connection attempt, so a rotated lease
|
|
|
|
|
# takes effect without a restart and without a delivery gap.
|
|
|
|
|
data:
|
|
|
|
|
username: "{{ .username }}"
|
|
|
|
|
password: "{{ .password }}"
|
|
|
|
|
host: platform-pg-rw.databases.svc.cluster.local
|
|
|
|
|
port: "5432"
|
|
|
|
|
dbname: audit_core
|
2026-08-13 10:27:13 +02:00
|
|
|
# One read = one lease. Two remoteRefs would mint two roles and pair
|
|
|
|
|
# username from A with password from B — auth then fails.
|
2026-08-13 00:58:49 +02:00
|
|
|
dataFrom:
|
2026-08-13 10:27:13 +02:00
|
|
|
- extract:
|
|
|
|
|
key: creds/audit-core-runtime
|
2026-08-13 00:58:49 +02:00
|
|
|
---
|
|
|
|
|
apiVersion: external-secrets.io/v1
|
|
|
|
|
kind: ExternalSecret
|
|
|
|
|
metadata:
|
|
|
|
|
name: audit-core-database-migrate
|
|
|
|
|
namespace: audit-core
|
|
|
|
|
spec:
|
|
|
|
|
refreshInterval: 15m
|
2026-08-13 10:27:13 +02:00
|
|
|
secretStoreRef:
|
|
|
|
|
kind: ClusterSecretStore
|
|
|
|
|
name: openbao-audit-core-database
|
2026-08-13 00:58:49 +02:00
|
|
|
target:
|
|
|
|
|
name: audit-core-database-migrate
|
|
|
|
|
creationPolicy: Owner
|
|
|
|
|
deletionPolicy: Retain
|
|
|
|
|
template:
|
|
|
|
|
engineVersion: v2
|
|
|
|
|
data:
|
|
|
|
|
username: "{{ .username }}"
|
|
|
|
|
password: "{{ .password }}"
|
|
|
|
|
host: platform-pg-rw.databases.svc.cluster.local
|
|
|
|
|
port: "5432"
|
|
|
|
|
dbname: audit_core
|
|
|
|
|
dataFrom:
|
2026-08-13 10:27:13 +02:00
|
|
|
- extract:
|
|
|
|
|
key: creds/audit-core-migration
|
|
|
|
|
# Sender registry is not pulled from OpenBao on first deploy
|
|
|
|
|
# (ops-mason/plans/audit-core-openbao-runtime-custody.md). Tokens are minted
|
|
|
|
|
# in-cluster into Secret audit-core-senders. Apply
|
|
|
|
|
# deploy/externalsecret-senders.yaml only after the Mason wrap-migrate has
|
|
|
|
|
# filled platform/workloads/audit-core/senders.
|