Replies: 3 comments
|
Great architecture — the header-gate-via-pre_request pattern is sound, and you've already got the two pieces that matter right (SECURITY DEFINER only for the minimal verifier read, the check itself as SECURITY INVOKER). Since both The principleActivate and prove the gate while there is nothing to steal. The dangerous window is "schema exposed, gate not enforced" — so you never allow that state to exist: the pre_request goes live first, gets verified against a probe, and only then does Step-by-step1. Ship the function while the schema is dark (one migration, all in
2. Enable the pre_request and reload config: alter role authenticator set pgrst.db_pre_request = 'private.require_gateway';
notify pgrst, 'reload config';(The 3. Prove the gate before anything valuable is reachable. Put a throwaway probe in the already exposed schema: create or replace function public.api_gate_probe() returns int
language sql security definer set search_path = public as $$ select 1 $$;
grant execute on function public.api_gate_probe() to anon, authenticated;Then, against the live Data API:
That probe is the load-bearing step: it proves the pre_request actually runs for anonymous Data API traffic on your project's PostgREST build before 4. Only after the probe proves the gate, expose the schema: alter role authenticator set pgrst.db_schemas = 'public, api'; -- keep whatever you already expose
notify pgrst, 'reload config';
notify pgrst, 'reload schema'; -- refresh the endpoint cacheVerify again with the same three calls against a real 5. Document the rollback as the mirror image (unset Failure modes worth knowing before you flip it
One extra hardening idea since you're already in this territory: give the gate a I do Supabase/PostgREST security audits for production apps, so if you'd like a second pair of eyes on the final migration before it ships, happy to take a look — but the sequence above should stand on its own either way. |
|
@cekuu35's order (gate first, prove it with a probe, then expose Fixes to that sequence:
The Data API toggle isn't a clean maintenance mode (Q2). Supabase's Securing your API guide says that with it off, "none of the auto-generated REST endpoints respond, regardless of grants or RLS." But in the dashboard's code, turning it off clears the exposed schemas list, and turning it back on restores only Use the exposed schemas list as your staging step and stop button instead (Q2 and Q5). With the Management API (
Withholding Replicas and pooling (Q3). |
|
Your order is the right one, with one change in emphasis: prove the gate on a probe first, and treat the exposed schema list as the switch, not the on and off toggle. On Q1, yes. The Securing your API guide documents this exact registration as the normal procedure: alter role authenticator set pgrst.db_pre_request, then notify pgrst, 'reload config'. The guide gives no plan split for it. Build the functions first while api is not exposed. One grant detail matters before you test. The hook runs on every Data API request, so anon, authenticated and service_role each need usage on schema private and execute on private.require_gateway() and on the SECURITY DEFINER helper. If any one of them misses usage or execute, that role fails even with the correct header. Prove it before api exists. Put one probe function in a schema that is already exposed, then call it over HTTP at /rest/v1/rpc/probe_name (functions live under rpc, tables do not). Test three cases: no header rejected, wrong header rejected, correct header passes. Raise the rejection with sqlstate PGRST and status 403 as shown in the guide error responses, so a gate rejection is easy to tell apart from a missing grant or a missing table. Only after those three pass should you set the exposed schemas to api only in the Data API settings, then repeat the same three tests on a real api object. On Q2, a disabled Data API does block REST calls while it is off; the guide says none of the auto generated REST endpoints respond then. But do not lean on the toggle to keep your final list. In Studio, turning it off writes an empty exposed list, and turning it back on falls back to public when the stored list is empty. So the toggle can quietly bring public back. The exposed list itself is the safer stop switch: point it at the probe schema while you work, and point it back there if enforcement ever looks wrong. Withholding execute is a valid emergency stop, but it fails good requests too, with a permission error, so use it as a kill switch and not as a staging step. On Q4, service_role goes through the hook too, because the hook is global to the Data API. Server code, Edge Functions and cron jobs that call REST with the secret key will be rejected unless they send the header or your function lets that role through on purpose. The hook only covers the Data API, not Realtime or Storage, so those still rest on RLS and Storage policies. On Q3 and Q5: a stored setting proves nothing by itself. NOTIFY is the documented reload step, and only the HTTP probes prove enforcement. If probes fail, set the exposed list back to the probe schema and fix it there. If you add read replicas later, repeat the three probes on each endpoint you expose, and do not assume a pass on one endpoint covers another. |
Uh oh!
There was an error while loading. Please reload this page.
Hi Supabase community,
I'm preparing to enable a custom Data API schema on a managed Supabase project (PostgreSQL 17) and would appreciate guidance on the safest supported deployment procedure.
Our architecture
We use a Cloudflare Worker as our application gateway.
The Worker sends an
X-Company-Gatewayheader to PostgREST. A private PostgreSQL function,private.require_gateway(), validates the header against SHA-256 verifier material stored in a restricted private table.The verifier is accessed through a minimal-privilege
SECURITY DEFINERhelper. The main pre-request function remainsSECURITY INVOKER.The intended configuration is:
pgrst.db_pre_request = 'private.require_gateway'apionlyprivateandauthremain unexposedanonandauthenticatedData API requests require the gateway headerThe implementation has passed local PostgreSQL and actual PostgREST 14.5/16.3 HTTP/JWT tests.
Our main concern is safe deployment on managed Supabase without temporarily exposing the API before gateway enforcement is active.
Questions
1. Hook registration authority
The official documentation shows:
ALTER ROLE authenticator SET pgrst.db_pre_request = 'private.require_gateway';followed by:
NOTIFY pgrst, 'reload config';Is this procedure supported when executed from the SQL Editor as the project's
postgresrole on managed Supabase, including Free-plan projects?2. Safe deployment / maintenance mode
Can we temporarily disable the Data API through the Supabase Dashboard, install our private gateway verifier and pre-request hook, change Exposed schemas to
apionly, and then re-enable the Data API?Is disabling the Data API sufficient to prevent an unprotected access window? Would you recommend temporarily withholding
EXECUTEpermissions as an additional safeguard?3. Configuration propagation
How can we verify that
db_pre_requestand the exposed-schema configuration have been applied to all relevant PostgREST serving instances?Is
NOTIFY pgrst, 'reload config'sufficient on managed Supabase, or are additional steps needed when replicas or pooled connections are involved?4. Effective enforcement testing
What is the recommended way to verify that the pre-request hook is actually running before allowing normal application traffic?
We want to verify missing, incorrect and correct gateway credentials through real HTTP requests, rather than relying only on stored database settings.
Does a global pre-request hook require special handling for
service_rolerequests or Supabase-managed Data API operations?5. Failure and recovery
If hook registration succeeds in PostgreSQL but effective HTTP enforcement is not confirmed, what is the safest supported way to keep the Data API inaccessible while correcting the configuration?
Our account functionality is currently disabled, and we have not applied these hosted gateway changes.
I'm looking for a supported, fail-closed procedure for managed Supabase rather than a custom privilege workaround.
Any guidance or references to existing implementations would be greatly appreciated.
Thank you!
All reactions