feat(security): self-provision the bunyip_app NOBYPASSRLS role (BUNYIP-360) #365
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/BUNYIP-360-provision-app-role"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Activates the per-user RLS shipped in BUNYIP-344 (#363/#364) by provisioning the unprivileged
bunyip_approle automatically, so theuser_isolationpolicies stop being a runtime no-op. Mirrors the Mokosh self-provisioning posture (mokosh-server/src/db/provision.rs), scoped to bunyip's single extra role.How
db::provision_app_roleidempotently createsbunyip_app(NOSUPERUSER NOBYPASSRLS) over the primary pool, which already connects as the DB owner/superuser and canCREATE ROLE, so no separate admin connection is needed. It runs AFTER migrations (soGRANT ... ON ALL TABLEScovers everything they created) plusALTER DEFAULT PRIVILEGESfor future migrations. Every statement is idempotent, so it is safe to run each boot.main.rscalls it whenBUNYIP_APP_PASSWORDis set, then builds the self-service app pool as that role (APP_DATABASE_URL). Unset keeps the previous no-op fallback to the primary pool.compose.dev.ymldefaults both values offPOSTGRES_PASSWORD, so a freshjust devactivates RLS with no extra config (dev-sso inherits it).compose.ymladds thebunyip_app_password+app_database_urlfile secrets (empty files keep RLS inactive)..env.examplerewrites the old manual-provisioning note to describe the automatic path and the password-must-match constraint.Verification
just check-containergreen (fmt + clippy -D warnings + 104 tests).bunyip_approle, connects a pool AS it (a genuine NOBYPASSRLS login), and asserts it is fail-closed with no GUC (zero rows) and correctly scoped throughbegin_with_user(only the acting user's row). Confirmed on the live DB thatbunyip_appisNOSUPERUSER + NOBYPASSRLS + LOGINwith the expected table grants. The DB-backed test skips whenRLS_TEST_DATABASE_URLis unset, so the no-Postgres CI stays green.Deploy notes
just dev(fresh volume) provisions and activates RLS../secrets/bunyip_app_passwordand./secrets/app_database_url(the latter =postgres://bunyip_app:<same-password>@postgres:5432/bunyip). Leaving both empty keeps RLS inactive. On boot the api logsbunyip_app RLS role provisionedthenSelf-service RLS pool established.DATABASE_URLrole is unchanged (still the bypassing owner that runs migrations and system paths).Closes BUNYIP-360.
🤖 Generated with Claude Code