diff --git a/apps/www/_blog/2025-05-08-event-triggers-wo-superuser.mdx b/apps/www/_blog/2025-05-08-event-triggers-wo-superuser.mdx new file mode 100644 index 00000000000..97deb348224 --- /dev/null +++ b/apps/www/_blog/2025-05-08-event-triggers-wo-superuser.mdx @@ -0,0 +1,123 @@ +--- +title: PostgreSQL Event Triggers without superuser access +description: Using Postgres Hooks to allow regular users to create event triggers +author: steve_chavez +author_url: https://github.com/steve-chavez +author_image_url: https://github.com/steve-chavez.png +image: 2025-05-08-event-triggers-wo-superuser/cover-event-triggers.png +thumb: 2025-05-08-event-triggers-wo-superuser/cover-event-triggers.png +categories: + - postgres +tags: + - postgres + - planetpg + - event triggers + - extensions +date: '2025-05-08' +--- + +Event triggers in Postgres are powerful, but only a superuser can create them. In cloud environments, granting superuser access isn't an option. + +But thanks to Postgres' extensibility, we can allow regular users to create event triggers, in a safe way. + +In this blog post, we’ll explain how we do this in the `supautils` extension, using a combination of the Utility Hook and the Function Manager Hook. + +## Privileged Role + +![utility hook](/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook.png) + +The core of `supautils` is the “privileged role”, which is a role that serves as proxy to superuser. It provides a safe subset of superuser capabilities and it’s accessible to regular users. + +When the privileged role does a `create event trigger`, we intercept the statement with a Utility Hook (`ProcessUtility_hook`). +Here we elevate the role to a superuser, continuing the usual flow and allowing the creation on Postgres core. As a last step, we downgrade to the privileged role and make +it the event trigger owner [^1]. + +Creating an event trigger like this is not safe though, as it would allow privilege escalation. + +## The privilege escalation problem + +Here, a problem arises. Once an event trigger is created: + +- It targets every role. +- It runs using the target role privileges [^2]. + +This means that a malicious user can create an event trigger like: + +```sql +create or replace function become_super() + returns event_trigger + language plpgsql as +$$ +begin + alter role malicious SUPERUSER; +end; +$$; + +create event trigger bad_event_trigger on ddl_command_end +execute procedure become_super(); +``` + +And once a superuser trips on the event trigger, it will fire with its privileges. Making the malicious user a superuser. + +## Skipping Event Triggers + +![fmgr hook](/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook.png) + +A solution would be skipping user event triggers for superusers. + +The Function Manager hook (`fmgr_hook`) allows us to intercept and modify functions’ execution. + +We can intercept the event trigger function and replace it with a “noop”. Postgres doesn’t provide a noop function, but we can use the existing `version()` function for the same purpose. + +Besides superusers, we also want to skip user event triggers for “reserved roles” [^3]. These are used for managed services (like `pgbouncer`). + +## User Event Triggers + +This now allows users to safely create event triggers, without superuser access: + +```sql +-- use the privileged role, which is configured to be "postgres" +set role postgres; +select current_setting('is_superuser'); -- prove it's not a superuser + current_setting +----------------- + off +(1 row) + +-- now create the event trigger +create function show_current_user() +returns event_trigger as $$ +begin + raise notice 'the event trigger is executed for %', current_user; +end; +$$ language plpgsql; + +create event trigger myevtrig on ddl_command_end +execute procedure show_current_user(); + +-- check it succeeds +create table foo(); +NOTICE: the event trigger is executed for postgres + +set role myrole; +create table bar(); +NOTICE: the event trigger is executed for myrole +``` + +## Future in Postgres core + +We would also like to allow regular user event triggers in Postgres core. To this end, we’ve submitted [some patches](https://www.postgresql.org/message-id/flat/CAGRrpzbtYDkg7_xwfzrqByYgCJQbbL38tADyuN%2B6tAkbA-Pnkg%40mail.gmail.com) which are already generating fruitful discussion. + +Note that user event triggers in Postgres core will likely be more restricted than the `supautils` version. + +## Try it out + +User event triggers are now available for new projects on the Supabase platform. + +You can also git clone the [supautils repo](https://github.com/supabase/supautils/) and `make install` it in your own deployment. + +Finally, we want to give a special shout out to the [Zero Sync](https://zero.rocicorp.dev) guys, which kept pushing us to release this feature. + +[^1]: This is so the event trigger can be altered or dropped by end users. +[^2]: This is not true if you mark the event trigger function as `security definer`, then it will run with the privileges of the function owner. But this is not a usual practice on event triggers, as they usually want to preserve the context of the current user. +[^3]: These are configurable. You can read more about reserved roles [here](https://supabase.com/blog/roles-postgres-hooks). diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers.png new file mode 100644 index 00000000000..0c1b0916883 Binary files /dev/null and b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/cover-event-triggers.png differ diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook.png new file mode 100644 index 00000000000..b862e3718a2 Binary files /dev/null and b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/fmgr-hook.png differ diff --git a/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook.png b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook.png new file mode 100644 index 00000000000..9b90a72440e Binary files /dev/null and b/apps/www/public/images/blog/2025-05-08-event-triggers-wo-superuser/utility-hook.png differ diff --git a/apps/www/public/planetpg-steve_chavez-rss.xml b/apps/www/public/planetpg-steve_chavez-rss.xml index 288cdacabf0..0cf42092594 100644 --- a/apps/www/public/planetpg-steve_chavez-rss.xml +++ b/apps/www/public/planetpg-steve_chavez-rss.xml @@ -7,7 +7,14 @@ en Fri, 16 Dec 2022 00:00:00 -0700 - + + https://supabase.com/blog/event-triggers-wo-superuser + PostgreSQL Event Triggers without superuser access + https://supabase.com/blog/event-triggers-wo-superuser + Using Postgres Hooks to allow regular users to create event triggers + Thu, 08 May 2025 00:00:00 -0700 + + https://supabase.com/blog/postgrest-11-prerelease PostgREST 11 pre-release https://supabase.com/blog/postgrest-11-prerelease diff --git a/apps/www/public/rss.xml b/apps/www/public/rss.xml index c988b8d92b2..be8ef9b42ee 100644 --- a/apps/www/public/rss.xml +++ b/apps/www/public/rss.xml @@ -7,7 +7,14 @@ en Fri, 04 Apr 2025 00:00:00 -0700 - + + https://supabase.com/blog/event-triggers-wo-superuser + PostgreSQL Event Triggers without superuser access + https://supabase.com/blog/event-triggers-wo-superuser + Using Postgres Hooks to allow regular users to create event triggers + Thu, 08 May 2025 00:00:00 -0700 + + https://supabase.com/blog/launch-week-14-top-10 Top 10 Launches of Launch Week 14 https://supabase.com/blog/launch-week-14-top-10