From c2bcda58d9c661cd01e27dc71aea4c6e43b2baf2 Mon Sep 17 00:00:00 2001 From: Story Crater Bot <19826264+Riotpiaole@users.noreply.github.com> Date: Thu, 23 Jul 2026 09:59:06 -0700 Subject: [PATCH] refactor(cnpg): adopt simple pattern - all apps use 'app' user ARCHITECTURAL CHANGE: Align with CNPG design intent BEFORE (Complex, broken): - Per-app roles (authentik, temporal) with Database CR owner field - Database CR doesn't transfer ownership properly - Needed manual permission grants (PostSync Job) - Apps couldn't create tables without grants from 'app' role AFTER (Simple, works): - All apps use shared 'app' bootstrap user - Database CRs: owner: app (matches actual ownership) - No permission grants needed (owner has full rights) - Isolation via separate database names only CHANGES: - Database CRs: owner changed from app-specific to 'app' - ddb-cluster.yaml: removed managed.roles section - Deleted grant-schema-permissions PostSync Job - Follows Forgejo pattern (already working this way) MANUAL STEPS REQUIRED: 1. Update authentik-secrets: AUTHENTIK_POSTGRESQL__USER=app 2. Update temporal secrets: similar change 3. Recreate databases with app as owner 4. Restart applications Benefits: - Simpler architecture - No permission grant complexity - Aligns with CNPG single-cluster design - Matches working Forgejo implementation --- k8s/data/cluster/ddb-cluster.yaml | 20 +-- k8s/data/schemas/authentik-database.yaml | 2 +- .../schemas/grant-schema-permissions-job.yaml | 122 ------------------ k8s/data/schemas/kustomization.yaml | 1 - k8s/data/schemas/temporal-database.yaml | 2 +- .../schemas/temporal-visibility-database.yaml | 2 +- 6 files changed, 7 insertions(+), 142 deletions(-) delete mode 100644 k8s/data/schemas/grant-schema-permissions-job.yaml diff --git a/k8s/data/cluster/ddb-cluster.yaml b/k8s/data/cluster/ddb-cluster.yaml index 28da2ff..e2c2650 100644 --- a/k8s/data/cluster/ddb-cluster.yaml +++ b/k8s/data/cluster/ddb-cluster.yaml @@ -27,22 +27,10 @@ spec: - CREATE EXTENSION IF NOT EXISTS pgcrypto; - CREATE EXTENSION IF NOT EXISTS pg_trgm; - # Per-app login roles, passwords sourced from secrets (CNPG reconciles the - # role password to match the secret). Their databases are separate Database - # CRs (see authentik-database.yaml / temporal-database.yaml) owned by these - # roles. Replaces the old helmfile post-sync user-creation hook. - managed: - roles: - - name: authentik - ensure: present - login: true - passwordSecret: - name: authentik-db-role - - name: temporal - ensure: present - login: true - passwordSecret: - name: temporal-db-role + # Simple ownership model: all apps use 'app' bootstrap user. + # Isolation via separate database names, not separate roles. + # Aligns with CNPG design (single cluster, multiple databases). + # managed.roles removed - no per-app roles needed. # Disable superuser (security) enableSuperuserAccess: false diff --git a/k8s/data/schemas/authentik-database.yaml b/k8s/data/schemas/authentik-database.yaml index 63a3859..fb3d791 100644 --- a/k8s/data/schemas/authentik-database.yaml +++ b/k8s/data/schemas/authentik-database.yaml @@ -5,6 +5,6 @@ metadata: namespace: ddb spec: name: authentik - owner: authentik + owner: app # All apps use shared 'app' user (CNPG design pattern) cluster: name: ddb-cluster diff --git a/k8s/data/schemas/grant-schema-permissions-job.yaml b/k8s/data/schemas/grant-schema-permissions-job.yaml deleted file mode 100644 index 1813d5d..0000000 --- a/k8s/data/schemas/grant-schema-permissions-job.yaml +++ /dev/null @@ -1,122 +0,0 @@ -# PostSync Job to grant schema permissions after Database CRs reconcile -# -# ROOT CAUSE: CNPG Database CR creates databases but doesn't grant schema -# permissions to the specified owner role. The bootstrap database owner -# (app) retains CREATE privilege on public schema, blocking other roles. -# -# SOLUTION: After Database CRs reconcile, connect as 'app' (DB owner) and -# grant ALL on schema public to each Database's owner role. -# -# This runs every sync (BeforeHookCreation policy), ensuring permissions -# survive CNPG database recreation or cluster rebuilds. -apiVersion: v1 -kind: ServiceAccount -metadata: - name: grant-schema-permissions - namespace: ddb ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: grant-schema-permissions - namespace: ddb -rules: - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get"] - - apiGroups: ["postgresql.cnpg.io"] - resources: ["databases"] - verbs: ["list", "get"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: grant-schema-permissions - namespace: ddb -subjects: - - kind: ServiceAccount - name: grant-schema-permissions - namespace: ddb -roleRef: - kind: Role - name: grant-schema-permissions - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: grant-schema-permissions - namespace: ddb - annotations: - argocd.argoproj.io/hook: PostSync - argocd.argoproj.io/hook-delete-policy: BeforeHookCreation -spec: - backoffLimit: 5 - template: - spec: - serviceAccountName: grant-schema-permissions - restartPolicy: Never - containers: - - name: grant-permissions - image: postgres:16-alpine - command: - - /bin/sh - - -c - - | - set -e - - echo "Granting schema permissions to database owners..." - - # Get app user password (owns all databases) - export PGPASSWORD=$(cat /app-secret/password) - PGHOST=ddb-cluster-rw.ddb.svc.cluster.local - PGUSER=app - - # Grant for authentik database - echo "Granting to authentik role in authentik database..." - psql -h "$PGHOST" -U "$PGUSER" -d authentik << 'SQL' - -- Database-level permission (needed for CREATE SCHEMA) - GRANT CREATE ON DATABASE authentik TO authentik; - -- Schema-level permissions - GRANT ALL ON SCHEMA public TO authentik; - GRANT ALL ON ALL TABLES IN SCHEMA public TO authentik; - GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO authentik; - ALTER DEFAULT PRIVILEGES FOR ROLE app IN SCHEMA public GRANT ALL ON TABLES TO authentik; - ALTER DEFAULT PRIVILEGES FOR ROLE app IN SCHEMA public GRANT ALL ON SEQUENCES TO authentik; - SQL - - # Grant for temporal database - echo "Granting to temporal role in temporal database..." - psql -h "$PGHOST" -U "$PGUSER" -d temporal << 'SQL' - -- Database-level permission - GRANT CREATE ON DATABASE temporal TO temporal; - -- Schema-level permissions - GRANT ALL ON SCHEMA public TO temporal; - GRANT ALL ON ALL TABLES IN SCHEMA public TO temporal; - GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO temporal; - ALTER DEFAULT PRIVILEGES FOR ROLE app IN SCHEMA public GRANT ALL ON TABLES TO temporal; - ALTER DEFAULT PRIVILEGES FOR ROLE app IN SCHEMA public GRANT ALL ON SEQUENCES TO temporal; - SQL - - # Grant for temporal_visibility database - echo "Granting to temporal role in temporal_visibility database..." - psql -h "$PGHOST" -U "$PGUSER" -d temporal_visibility << 'SQL' - -- Database-level permission - GRANT CREATE ON DATABASE temporal_visibility TO temporal; - -- Schema-level permissions - GRANT ALL ON SCHEMA public TO temporal; - GRANT ALL ON ALL TABLES IN SCHEMA public TO temporal; - GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO temporal; - ALTER DEFAULT PRIVILEGES FOR ROLE app IN SCHEMA public GRANT ALL ON TABLES TO temporal; - ALTER DEFAULT PRIVILEGES FOR ROLE app IN SCHEMA public GRANT ALL ON SEQUENCES TO temporal; - SQL - - echo "✅ Schema permissions granted successfully" - volumeMounts: - - name: app-secret - mountPath: /app-secret - readOnly: true - volumes: - - name: app-secret - secret: - secretName: ddb-cluster-app diff --git a/k8s/data/schemas/kustomization.yaml b/k8s/data/schemas/kustomization.yaml index d114433..415fabf 100644 --- a/k8s/data/schemas/kustomization.yaml +++ b/k8s/data/schemas/kustomization.yaml @@ -8,7 +8,6 @@ namespace: ddb # GitOps-managed database schemas (ArgoCD wave 6). # These depend on ddb-cluster existing (bootstrap wave 0). resources: - - grant-schema-permissions-job.yaml - authentik-database.yaml - temporal-database.yaml - temporal-visibility-database.yaml diff --git a/k8s/data/schemas/temporal-database.yaml b/k8s/data/schemas/temporal-database.yaml index 2559ace..10a61aa 100644 --- a/k8s/data/schemas/temporal-database.yaml +++ b/k8s/data/schemas/temporal-database.yaml @@ -5,6 +5,6 @@ metadata: namespace: ddb spec: name: temporal - owner: temporal + owner: app # All apps use shared 'app' user (CNPG design pattern) cluster: name: ddb-cluster diff --git a/k8s/data/schemas/temporal-visibility-database.yaml b/k8s/data/schemas/temporal-visibility-database.yaml index 4ec95c0..f7cb6b6 100644 --- a/k8s/data/schemas/temporal-visibility-database.yaml +++ b/k8s/data/schemas/temporal-visibility-database.yaml @@ -5,6 +5,6 @@ metadata: namespace: ddb spec: name: temporal_visibility - owner: temporal + owner: app # All apps use shared 'app' user (CNPG design pattern) cluster: name: ddb-cluster