From 766555453ef00572cf584fb37608016a5a3a5cf3 Mon Sep 17 00:00:00 2001 From: Story Crater Bot <19826264+Riotpiaole@users.noreply.github.com> Date: Tue, 18 Aug 2026 15:08:03 -0700 Subject: [PATCH] refactor(cnpg): unify all apps on 'app' database user/credentials pattern --- bootstrap.sh | 6 + 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 +- k8s/security/iam/authentik-values.yaml | 43 +++++- 8 files changed, 51 insertions(+), 147 deletions(-) delete mode 100644 k8s/data/schemas/grant-schema-permissions-job.yaml diff --git a/bootstrap.sh b/bootstrap.sh index cca24eb..5ee59fd 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -82,6 +82,12 @@ kubectl get secret ddb-cluster-app -n ddb -o yaml \ | sed 's/namespace: ddb/namespace: cicd/' \ | kubectl apply -f - +# Copy DB secret to iam namespace (for authentik) +log "Copying ddb-cluster-app secret to iam namespace..." +kubectl get secret ddb-cluster-app -n ddb -o yaml \ + | sed 's/namespace: ddb/namespace: iam/' \ + | kubectl apply -f - + # 7. Wait for Forgejo log "Waiting for Forgejo to be ready..." kubectl wait --for=condition=available --timeout=600s \ 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 diff --git a/k8s/security/iam/authentik-values.yaml b/k8s/security/iam/authentik-values.yaml index 146d82a..e20a816 100644 --- a/k8s/security/iam/authentik-values.yaml +++ b/k8s/security/iam/authentik-values.yaml @@ -23,13 +23,14 @@ authentik: enabled: false # do not phone home to Sentry # PostgreSQL connection — points at CloudNativePG cluster in ddb namespace. - # password is injected via helmfile --set at deploy time. + # Uses 'app' bootstrap user (CNPG simple pattern, same as Forgejo). + # Credentials injected from ddb-cluster-app secret via env vars below. postgresql: host: ddb-cluster-rw.ddb.svc.cluster.local port: 5432 name: authentik - user: authentik - password: "" # injected via helmfile --set authentik.postgresql.password + user: app # All apps use shared 'app' user (CNPG design pattern) + password: "" # overridden by AUTHENTIK_POSTGRESQL__PASSWORD env var # Redis connection — bundled subchart, standalone mode (no sentinel/cluster). redis: @@ -123,7 +124,23 @@ server: volumes: *caVolumes volumeMounts: *caVolumeMounts initContainers: *caInitContainers - env: *caEnv + env: + # Merge CA trust env vars + - name: REQUESTS_CA_BUNDLE + value: /merged/ca-bundle.crt + - name: SSL_CERT_FILE + value: /merged/ca-bundle.crt + # Override database credentials to use 'app' from ddb-cluster-app + - name: AUTHENTIK_POSTGRESQL__USER + valueFrom: + secretKeyRef: + name: ddb-cluster-app + key: username + - name: AUTHENTIK_POSTGRESQL__PASSWORD + valueFrom: + secretKeyRef: + name: ddb-cluster-app + key: password podAnnotations: configmap.reloader.stakater.com/reload: "homelab-ca" homelab.io/restart-at: "2026-06-21T13-40" @@ -178,7 +195,23 @@ worker: volumes: *caVolumes volumeMounts: *caVolumeMounts initContainers: *caInitContainers - env: *caEnv + env: + # Merge CA trust env vars + - name: REQUESTS_CA_BUNDLE + value: /merged/ca-bundle.crt + - name: SSL_CERT_FILE + value: /merged/ca-bundle.crt + # Override database credentials to use 'app' from ddb-cluster-app + - name: AUTHENTIK_POSTGRESQL__USER + valueFrom: + secretKeyRef: + name: ddb-cluster-app + key: username + - name: AUTHENTIK_POSTGRESQL__PASSWORD + valueFrom: + secretKeyRef: + name: ddb-cluster-app + key: password podAnnotations: configmap.reloader.stakater.com/reload: "homelab-ca" homelab.io/restart-at: "2026-06-21T13-40"