mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
feat: blog post for event triggers wo superuser (#35570)
This commit is contained in:
1 parent
73f38a40da
commit
c38251b2e7
6 files changed
+139
-2
No files matched your search
@@ -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
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
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).
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@@ -7,7 +7,14 @@
|
||||
<language>en</language>
|
||||
<lastBuildDate>Fri, 16 Dec 2022 00:00:00 -0700</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-steve_chavez-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/event-triggers-wo-superuser</guid>
|
||||
<title>PostgreSQL Event Triggers without superuser access</title>
|
||||
<link>https://supabase.com/blog/event-triggers-wo-superuser</link>
|
||||
<description>Using Postgres Hooks to allow regular users to create event triggers</description>
|
||||
<pubDate>Thu, 08 May 2025 00:00:00 -0700</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgrest-11-prerelease</guid>
|
||||
<title>PostgREST 11 pre-release</title>
|
||||
<link>https://supabase.com/blog/postgrest-11-prerelease</link>
|
||||
|
||||
@@ -7,7 +7,14 @@
|
||||
<language>en</language>
|
||||
<lastBuildDate>Fri, 04 Apr 2025 00:00:00 -0700</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/event-triggers-wo-superuser</guid>
|
||||
<title>PostgreSQL Event Triggers without superuser access</title>
|
||||
<link>https://supabase.com/blog/event-triggers-wo-superuser</link>
|
||||
<description>Using Postgres Hooks to allow regular users to create event triggers</description>
|
||||
<pubDate>Thu, 08 May 2025 00:00:00 -0700</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/launch-week-14-top-10</guid>
|
||||
<title>Top 10 Launches of Launch Week 14</title>
|
||||
<link>https://supabase.com/blog/launch-week-14-top-10</link>
|
||||
|
||||
Reference in new issue
Block a user