mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat(docker): remove superuser access (#42975)
This commit is contained in:
1 parent
e080513666
commit
cc4d985c40
4 files changed
+233
No files matched your search
@@ -2897,6 +2897,7 @@ export const self_hosting: NavMenuConstant = {
|
||||
{ name: 'Configure Phone Login & MFA', url: '/guides/self-hosting/self-hosted-phone-mfa' },
|
||||
{ name: 'Configure SAML 2.0 SSO', url: '/guides/self-hosting/self-hosted-saml-sso' },
|
||||
{ name: 'Enable MCP server', url: '/guides/self-hosting/enable-mcp' },
|
||||
{ name: 'Remove superuser access', url: '/guides/self-hosting/remove-superuser-access' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: 'Remove superuser access from Studio'
|
||||
description: 'Learn how to switch from the supabase_admin to postgres role in self-hosted Supabase.'
|
||||
subtitle: 'Learn how to switch from the supabase_admin to postgres role in self-hosted Supabase.'
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
In late 2022, Supabase introduced a security change in hosted projects that removed superuser access from the dashboard SQL editor and shifted ownership of user-created database objects away from `supabase_admin` toward the `postgres` role.
|
||||
You can read more about it in the [official announcement](https://github.com/orgs/supabase/discussions/9314).
|
||||
|
||||
However, this migration was never automatically applied to self-hosted Supabase instances.
|
||||
|
||||
As a result:
|
||||
|
||||
- Objects created via the dashboard may still be owned by `supabase_admin`
|
||||
- Behavior differs from the Supabase platform
|
||||
- Some migrations may fail when run as `postgres`
|
||||
|
||||
This guide explains how to align your self-hosted Supabase instance with the security enhancements and ownership model used on the Supabase platform.
|
||||
|
||||
## Changing the configuration
|
||||
|
||||
### Step 1: Update database object ownership
|
||||
|
||||
Use the provided script to reassign ownership of database objects in the `public` schema from `supabase_admin` to `postgres`. From the project directory containing `docker-compose.yml`, run:
|
||||
|
||||
```sh
|
||||
sh utils/reassign-owner.sh
|
||||
```
|
||||
|
||||
<Admonition type="tip">
|
||||
|
||||
This script only updates ownership for database objects in the `public` schema. Supabase-managed and custom schemas are not affected.
|
||||
|
||||
</Admonition>
|
||||
|
||||
### Step 2: Update environment variables in docker-compose.yml
|
||||
|
||||
- In your `docker-compose.yml` configuration, uncomment the following line for the `studio` service to use the `postgres` role for read/write operations:
|
||||
|
||||
```yml name=docker-compose.yml
|
||||
studio:
|
||||
environment:
|
||||
POSTGRES_USER_READ_WRITE: postgres
|
||||
```
|
||||
|
||||
- Locate the `meta` service environment variables and change the `PG_META_DB_USER` environment variable from `supabase_admin` to `postgres`:
|
||||
|
||||
```yml name=docker-compose.yml
|
||||
meta:
|
||||
environment:
|
||||
PG_META_DB_USER: postgres
|
||||
```
|
||||
|
||||
<Admonition type="tip">
|
||||
|
||||
Studio uses its own credentials to access Postgres via `postgres-meta`, so this change is only needed for backward compatibility and consistency.
|
||||
|
||||
</Admonition>
|
||||
|
||||
### Step 3: Restart Supabase
|
||||
|
||||
```sh
|
||||
docker compose down && docker compose up -d
|
||||
```
|
||||
|
||||
## Verify roles
|
||||
|
||||
After restarting your services, verify that Supabase Studio is now using the `postgres` role. Run the following query in the Supabase Studio SQL Editor:
|
||||
|
||||
```sql
|
||||
select current_user;
|
||||
-- expected result: postgres
|
||||
```
|
||||
@@ -37,6 +37,10 @@ services:
|
||||
POSTGRES_HOST: ${POSTGRES_HOST}
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
|
||||
# See: https://supabase.com/docs/guides/self-hosting/remove-superuser-access
|
||||
#POSTGRES_USER_READ_WRITE: postgres
|
||||
|
||||
PG_META_CRYPTO_KEY: ${PG_META_CRYPTO_KEY}
|
||||
PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS}
|
||||
PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Reassign ownership of public schema objects from supabase_admin to postgres.
|
||||
#
|
||||
# Context and documentation:
|
||||
# https://supabase.com/docs/guides/self-hosting/remove-superuser-access
|
||||
#
|
||||
# Credits:
|
||||
# Original version by Inder Singh.
|
||||
#
|
||||
# Usage:
|
||||
# sh utils/reassign-owner.sh
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
if ! docker compose version >/dev/null 2>&1; then
|
||||
echo "Docker Compose not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check Postgres service
|
||||
db_image_prefix="supabase.postgres:"
|
||||
|
||||
compose_output=$(docker compose ps \
|
||||
--format '{{.Image}}\t{{.Service}}\t{{.Status}}' 2>/dev/null |
|
||||
grep -m1 "^$db_image_prefix" || true)
|
||||
|
||||
if [ -z "$compose_output" ]; then
|
||||
echo "Postgres container not found. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
db_srv_name=$(echo "$compose_output" | cut -f2)
|
||||
db_srv_status=$(echo "$compose_output" | cut -f3)
|
||||
|
||||
case "$db_srv_status" in
|
||||
Up*)
|
||||
;;
|
||||
*)
|
||||
echo "Postgres container status: $db_srv_status"
|
||||
echo "Exiting."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! test -t 0; then
|
||||
echo ""
|
||||
echo "Running non-interactively. Not reassigning ownership."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
printf "Reassign public schema objects to postgres user? (y/N) "
|
||||
read -r REPLY
|
||||
case "$REPLY" in
|
||||
[Yy])
|
||||
;;
|
||||
*)
|
||||
echo "Canceled. Not reassigning ownership."
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
docker compose exec -T "$db_srv_name" psql -v ON_ERROR_STOP=1 -U supabase_admin -d postgres <<'EOF'
|
||||
\echo 'Current supabase_admin-owned objects in public schema:'
|
||||
SELECT c.relname, c.relkind, c.relowner::regrole
|
||||
FROM pg_class c
|
||||
WHERE c.relnamespace = 'public'::regnamespace
|
||||
AND c.relowner = 'supabase_admin'::regrole;
|
||||
|
||||
-- Reassign user objects in public schema from supabase_admin to postgres.
|
||||
-- (Only affects public schema; Supabase-managed schemas stay as-is.
|
||||
-- Extension-owned objects are skipped.)
|
||||
DO $$
|
||||
DECLARE
|
||||
rec record;
|
||||
rel_count int := 0;
|
||||
fn_count int := 0;
|
||||
type_count int := 0;
|
||||
BEGIN
|
||||
-- Tables, views, sequences, materialized views, partitioned tables
|
||||
FOR rec IN
|
||||
SELECT c.relname, c.relkind
|
||||
FROM pg_class c
|
||||
WHERE c.relnamespace = 'public'::regnamespace
|
||||
AND c.relowner = 'supabase_admin'::regrole
|
||||
AND c.relkind IN ('r', 'v', 'S', 'm', 'p')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM pg_depend d
|
||||
WHERE d.classid = 'pg_class'::regclass
|
||||
AND d.objid = c.oid
|
||||
AND d.deptype = 'e'
|
||||
)
|
||||
ORDER BY CASE c.relkind
|
||||
WHEN 'p' THEN 0 -- partitioned parents first; cascades ownership to partitions
|
||||
WHEN 'm' THEN 1
|
||||
WHEN 'r' THEN 2
|
||||
WHEN 'v' THEN 3
|
||||
WHEN 'S' THEN 4
|
||||
END
|
||||
LOOP
|
||||
EXECUTE format('ALTER TABLE public.%I OWNER TO postgres', rec.relname);
|
||||
rel_count := rel_count + 1;
|
||||
END LOOP;
|
||||
|
||||
-- Functions and procedures
|
||||
FOR rec IN
|
||||
SELECT p.oid, p.proname, pg_get_function_identity_arguments(p.oid) AS args
|
||||
FROM pg_proc p
|
||||
WHERE p.pronamespace = 'public'::regnamespace
|
||||
AND p.proowner = 'supabase_admin'::regrole
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM pg_depend d
|
||||
WHERE d.classid = 'pg_proc'::regclass
|
||||
AND d.objid = p.oid
|
||||
AND d.deptype = 'e'
|
||||
)
|
||||
LOOP
|
||||
EXECUTE format('ALTER ROUTINE public.%I(%s) OWNER TO postgres', rec.proname, rec.args);
|
||||
fn_count := fn_count + 1;
|
||||
END LOOP;
|
||||
|
||||
-- Types (excluding array types and table-bound composites)
|
||||
FOR rec IN
|
||||
SELECT t.typname
|
||||
FROM pg_type t
|
||||
WHERE t.typnamespace = 'public'::regnamespace
|
||||
AND t.typowner = 'supabase_admin'::regrole
|
||||
AND t.typrelid = 0
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM pg_type el
|
||||
WHERE el.oid = t.typelem
|
||||
AND el.typarray = t.oid
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM pg_depend d
|
||||
WHERE d.classid = 'pg_type'::regclass
|
||||
AND d.objid = t.oid
|
||||
AND d.deptype = 'e'
|
||||
)
|
||||
LOOP
|
||||
EXECUTE format('ALTER TYPE public.%I OWNER TO postgres', rec.typname);
|
||||
type_count := type_count + 1;
|
||||
END LOOP;
|
||||
|
||||
RAISE NOTICE 'Reassigned % relation(s), % routine(s), % type(s) from supabase_admin to postgres.',
|
||||
rel_count, fn_count, type_count;
|
||||
END
|
||||
$$;
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "Done."
|
||||
Reference in new issue
Block a user