diff --git a/asset/postgres/ident.conf.tmpl b/asset/postgres/ident.conf.tmpl new file mode 100644 index 0000000..4e5e36b --- /dev/null +++ b/asset/postgres/ident.conf.tmpl @@ -0,0 +1,11 @@ +# moments — pg_ident.conf.d drop-in template +# Rendered by script/deploy.sh per host based on manifest.yml. +# Install path: /var/lib/pgsql/18/data/pg_ident.conf.d/{{HOST_FQDN}}.conf +# Apply with: sudo systemctl reload postgresql-18 — on BOTH magrathea +# (primary) and frankie (standby) so failover doesn't lock the app out. +# +# One line per (host, role) mapping. A host that runs both moments-api +# and moments-worker will have two lines (one for moments_ro, one for +# moments_rw). + +cert_cn {{HOST_FQDN}} {{DB_ROLE}} diff --git a/asset/sql/bootstrap-moments.sql b/asset/sql/bootstrap-moments.sql new file mode 100644 index 0000000..6da8209 --- /dev/null +++ b/asset/sql/bootstrap-moments.sql @@ -0,0 +1,17 @@ +-- In-database grants for the moments database. +-- Run after asset/sql/bootstrap.sql, against the moments database. +-- Idempotent — safe to re-run on every deploy. +-- +-- psql -h magrathea.kosherinata.internal -U postgres -d moments \ +-- -f asset/sql/bootstrap-moments.sql +-- +-- The schema itself is created by sqlx migrations executed by moments-api +-- on startup (which runs as moments_rw, the database owner). This file +-- only manages the read-only role's access to whatever moments_rw creates. + +GRANT USAGE ON SCHEMA public TO moments_ro; +GRANT SELECT ON ALL TABLES IN SCHEMA public TO moments_ro; + +-- Tables created later by moments_rw (running migrations) inherit SELECT for moments_ro. +ALTER DEFAULT PRIVILEGES FOR ROLE moments_rw IN SCHEMA public + GRANT SELECT ON TABLES TO moments_ro; diff --git a/asset/sql/bootstrap.sql b/asset/sql/bootstrap.sql new file mode 100644 index 0000000..98dda3d --- /dev/null +++ b/asset/sql/bootstrap.sql @@ -0,0 +1,26 @@ +-- moments role and database bootstrap. +-- Run as a postgres superuser against the cluster's `postgres` database. +-- Idempotent — safe to re-run on every deploy. +-- +-- psql -h magrathea.kosherinata.internal -U postgres -d postgres \ +-- -f asset/sql/bootstrap.sql +-- +-- After this completes, run asset/sql/bootstrap-moments.sql against the +-- newly created `moments` database to apply the in-database grants. + +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'moments_rw') THEN + CREATE ROLE moments_rw LOGIN; + END IF; + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'moments_ro') THEN + CREATE ROLE moments_ro LOGIN; + END IF; +END +$$; + +SELECT 'CREATE DATABASE moments OWNER moments_rw' +WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'moments') +\gexec + +GRANT CONNECT ON DATABASE moments TO moments_ro, moments_rw;