Prepare railiance01 delivery: dynamic leases, migrate Job, operator runbook
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

VaultDynamicSecret pulls database/creds/* so a rotating lease is not frozen
into KV. Runtime sets AUDIT_CORE_AUTO_MIGRATE=0; schema is a Job with the
migration lease. Image base is digest-pinned. Namespace and NetworkPolicies
are on the cluster; Deployment waits for the attended OpenBao ESO token.
This commit is contained in:
tegwick 2026-08-13 00:58:49 +02:00
parent bbf86b8373
commit 3a7d63e18f
18 changed files with 826 additions and 33 deletions

19
deploy/README.md Normal file
View file

@ -0,0 +1,19 @@
# railiance01 package
Target: railiance01 only. The workstation kubeconfig that talks to that API
is the `k3s-api-railiance01` tunnel (local port **16444**).
`~/.kube/config-hosteurope` currently points at 16443 (coulombcore); rewrite
the server port or export a copy before applying.
Apply order is documented in `docs/operator-runbook.md`. Do not apply the
Deployment until:
1. The image digest is pinned (not `REPLACE_AT_RELEASE`).
2. Secrets `audit-core-database`, `audit-core-database-migrate`, and
`audit-core-senders` exist.
3. Job `audit-core-migrate` has completed.
```bash
make image-build
make deploy-dry-run
```

View file

@ -93,6 +93,10 @@ spec:
# to the development store.
- name: AUDIT_CORE_REQUIRE_CUSTODY_CLASS
value: archive
# Runtime role cannot CREATE TABLE. Schema changes are a Job
# with the migration lease (deploy/migrate-job.yaml).
- name: AUDIT_CORE_AUTO_MIGRATE
value: "0"
- name: AUDIT_CORE_DATABASE_SCHEMA
value: audit_core
- name: AUDIT_CORE_THREADS

View file

@ -0,0 +1,31 @@
# Template of the railiance-platform add-on store. Prefer applying from:
# ~/railiance-platform/argocd/platform-addons/openbao-secretstore/openbao-audit-core.clustersecretstore.yaml
#
# Do not apply until Secret external-secrets/openbao-audit-core-eso-token exists
# (scripts/openbao-eso-token-apply.sh). This store is KV-only: sender registry
# lives at platform/workloads/audit-core/senders. Database leases are dynamic
# and come from VaultDynamicSecret, not this store.
---
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: openbao-audit-core
labels:
app.kubernetes.io/part-of: railiance-gitops
railiance-platform/component: external-secrets
app.kubernetes.io/name: audit-core
spec:
provider:
vault:
# In-cluster OpenBao on railiance01, not the public bao.coulomb.social UI.
server: http://openbao.openbao.svc:8200
path: platform
version: v2
auth:
tokenSecretRef:
name: openbao-audit-core-eso-token
namespace: external-secrets
key: token
conditions:
- namespaces:
- audit-core

View file

@ -1,14 +1,12 @@
# Credential delivery for audit-core (AUDIT-WP-0005-T02).
#
# Follows the ClusterSecretStore -> ExternalSecret -> Secret pattern already in
# use by activity-core and rapp-qonto. audit-core never holds a credential in
# its own configuration; it reads whatever is currently mounted.
# Two sources, because they are two kinds of secret:
# - database leases: VaultDynamicSecret -> ExternalSecret (this file)
# - sender registry: ClusterSecretStore openbao-audit-core -> ExternalSecret
#
# PREREQUISITE (rapp-postgres / railiance-platform, not this repo):
# - a ClusterSecretStore named openbao-audit-core, scoped to this namespace
# - the OpenBao database role rapp-postgres/audit-core-runtime issuing leases
# against the audit_core_app group role
# Apply order: ClusterSecretStore, then this, then the Deployment.
# 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.
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
@ -19,9 +17,6 @@ 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
secretStoreRef:
kind: ClusterSecretStore
name: openbao-audit-core
target:
name: audit-core-database
creationPolicy: Owner
@ -37,15 +32,38 @@ spec:
host: platform-pg-rw.databases.svc.cluster.local
port: "5432"
dbname: audit_core
data:
- secretKey: username
remoteRef:
key: platform/workloads/audit-core/database/audit-core-runtime
property: username
- secretKey: password
remoteRef:
key: platform/workloads/audit-core/database/audit-core-runtime
property: password
dataFrom:
- sourceRef:
generatorRef:
apiVersion: generators.external-secrets.io/v1alpha1
kind: VaultDynamicSecret
name: audit-core-runtime
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: audit-core-database-migrate
namespace: audit-core
spec:
refreshInterval: 15m
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:
- sourceRef:
generatorRef:
apiVersion: generators.external-secrets.io/v1alpha1
kind: VaultDynamicSecret
name: audit-core-migration
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
@ -71,5 +89,6 @@ spec:
# between, so there is no delivery gap.
- secretKey: senders.json
remoteRef:
key: platform/workloads/audit-core/senders
# CSS already mounts KV path `platform`; do not repeat the prefix.
key: workloads/audit-core/senders
property: senders.json

66
deploy/migrate-job.yaml Normal file
View file

@ -0,0 +1,66 @@
# One-shot schema apply (AUDIT-WP-0005-T02).
#
# Uses the migration lease, not the runtime lease. The runtime Deployment
# sets AUDIT_CORE_AUTO_MIGRATE=0 so a leaked app credential cannot change
# the schema. Re-apply after deleting the previous Job when a new migration
# ships (Jobs are immutable).
#
# Image digest must match deploy/audit-core.yaml.
---
apiVersion: batch/v1
kind: Job
metadata:
name: audit-core-migrate
namespace: audit-core
labels:
app.kubernetes.io/name: audit-core
app.kubernetes.io/component: migrate
spec:
backoffLimit: 6
ttlSecondsAfterFinished: 86400
template:
metadata:
labels:
app.kubernetes.io/name: audit-core
app.kubernetes.io/component: migrate
spec:
restartPolicy: OnFailure
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: migrate
image: forgejo.coulomb.social/coulomb/audit-core@sha256:REPLACE_AT_RELEASE
imagePullPolicy: IfNotPresent
command: ["python", "-m", "audit_core", "migrate"]
env:
- name: AUDIT_CORE_CREDENTIAL_DIR
value: /etc/audit-core/db
- name: AUDIT_CORE_DATABASE_SCHEMA
value: audit_core
resources:
requests:
cpu: 25m
memory: 64Mi
limits:
cpu: 250m
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
mountPath: /tmp
- name: database-credential
mountPath: /etc/audit-core/db
readOnly: true
volumes:
- name: tmp
emptyDir: {}
- name: database-credential
secret:
secretName: audit-core-database-migrate
defaultMode: 0400

View file

@ -0,0 +1,41 @@
# Dynamic PostgreSQL leases (AUDIT-WP-0005-T02).
#
# These are not KV secrets. Copying a lease into platform/workloads/... would
# freeze a rotating credential and recreate the delivery-gap problem the
# mounted directory exists to avoid. ESO's VaultDynamicSecret generator reads
# database/creds/* on each refresh and writes username/password files that
# audit-core re-reads on the next connection.
#
# Prerequisite: Secret external-secrets/openbao-audit-core-eso-token.
---
apiVersion: generators.external-secrets.io/v1alpha1
kind: VaultDynamicSecret
metadata:
name: audit-core-runtime
namespace: audit-core
spec:
path: database/creds/audit-core-runtime
method: GET
provider:
server: http://openbao.openbao.svc:8200
auth:
tokenSecretRef:
name: openbao-audit-core-eso-token
namespace: external-secrets
key: token
---
apiVersion: generators.external-secrets.io/v1alpha1
kind: VaultDynamicSecret
metadata:
name: audit-core-migration
namespace: audit-core
spec:
path: database/creds/audit-core-migration
method: GET
provider:
server: http://openbao.openbao.svc:8200
auth:
tokenSecretRef:
name: openbao-audit-core-eso-token
namespace: external-secrets
key: token