Recommended secure migration-operator design for a Supabase Free project #51492
Replies: 2 comments 2 replies
|
Your requirements are all achievable on the Free plan — the constraint to design around is that the platform connection (pooler / SQL editor) hands you The core pattern: separate the connection from the execution role You can't avoid -- one-time setup (as postgres, which has CREATEROLE)
create role app_owner nologin;
grant app_owner to postgres with set true; -- PG16+: membership with SET, no INHERIT
-- every migration thereafter opens with:
set role app_owner; -- DDL below this line executes as app_owner, not postgresAll schema objects (tables, functions) are created owned by Runtime separation (your requirement 4) stays as you specified: Pre-migration verification (your Q3) — three checks worth automating as a gate script that refuses to run if any fail: -- 1. execution context is the operator, not the platform login
select current_user; -- must be app_owner, not postgres
select current_setting('role', true);
-- 2. memberships look like the design intends (PG16+ columns)
select member, grantor, admin_option, set_option, inherit_option
from pg_auth_members
where roleid in ('app_owner'::regrole, 'rpc_owner'::regrole);
-- 3. runtime role holds no direct table grants (RLS is the only path)
select table_name, privilege_type
from information_schema.role_table_grants
where grantee = 'authenticated' and table_schema = 'public';
-- expect: zero rowsAnd one post-migration check that catches what migrations most often silently introduce — a permissive policy: select tablename, policyname, roles, qual
from pg_policies
where qual = 'true' and roles::text in ('{anon}','{public}');
-- expect: zero rows, or rows you can explainSupabase-specific limitations (your Q4), honestly:
The one-sentence version: |
|
Yes, a dedicated restricted login works on Free, and it doesn’t need createrole or membership in your runtime role. postgres is used once, by hand, to create the roles and set the password. After that, routine migrations log in as app_migrator and never as postgres. Here’s how I’d set it up and verify it.
create role app_owner nologin; create schema app; Then set its password from psql with \password app_migrator. Supabase logs DDL (log_statement = 'ddl'), so a password written inside create role or alter role lands in your Postgres logs in plain text. \password hashes it first, so only the hash reaches the server. Every migration file starts with set role app_owner;, so the objects it creates are owned by app_owner. This isn’t the pattern you ruled out: the connection identity is app_migrator, and the only role it can switch to is app_owner. Because app_migrator is noinherit, it gets nothing from app_owner until it does that.
select rolname, rolsuper, rolcreaterole, rolcreatedb, rolbypassrls, rolreplication, rolinherit, rolcanlogin select r.rolname as granted_role, m.rolname as member, a.admin_option select r, pg_has_role('app_migrator', r, 'member') You want superuser, createrole, createdb, bypassrls and replication false on both, app_owner as app_migrator’s only membership without admin option, and false for every row of the last query. That last one is what shows app_migrator can’t assume your runtime roles or postgres. On Postgres 16 and later, also select a.inherit_option, a.set_option: you want inherit false and set true. Run the role-attribute check on any custom runtime role too.
Supabase’s automatic grants to anon, authenticated and service_role are default privileges for tables created in the public schema. Tables in app get none of them, so every grant has to be explicit, including execute on your admin functions. |
Uh oh!
There was an error while loading. Please reload this page.
Hello Supabase community,
I’m building a marketplace application using Supabase on the Free plan. I manage my own organization and projects and want to build a secure Admin Panel without upgrading my plan or changing my existing Production and Staging environments during the design stage.
I need to apply a reviewed SQL security foundation to Staging. My requirements are:
I considered a two-stage design with a separately controlled provisioning authority and a restricted migration executor. However, PostgreSQL role-membership behavior and the privileges needed to create roles may make the separation more difficult than expected.
My questions:
I’m looking for technical guidance only. I will not share project identifiers, credentials, connection strings, or secrets publicly.
All reactions