From d948fc94214eaf61e7d96dfb0f42850c8ebb0c0d Mon Sep 17 00:00:00 2001 From: Jeremy Schneider Date: Mon, 15 Dec 2025 20:37:15 -0800 Subject: [PATCH] Grant required perms except CREATE to PUBLIC for non-superuser usage This covers all grants except for CREATE on the repack schema to allow table owners to use pg_repack with --no-superuser-check. Importantly, table ownership permissions still prevent users from accessing intermediate repack data and log tables of other users, or attempting to repack tables they do not own - similar to VACUUM FULL. CREATE on the repack schema is not included to match the default behavior of postgres v15+ of not allowing new roles to create tables without an explicit grant. We also add a successful non-superuser test case by replacing the repack schema permission grants with a test that creates a user-owned table and verifies pg_repack works for non-superuser with --no-superuser-check flag. --- lib/pg_repack.sql.in | 12 ++++++++++++ regress/expected/nosuper.out | 22 +++++++++++++--------- regress/expected/nosuper_1.out | 22 +++++++++++++--------- regress/sql/nosuper.sql | 19 +++++++++++++------ 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/lib/pg_repack.sql.in b/lib/pg_repack.sql.in index b0596452..0c77f2bc 100644 --- a/lib/pg_repack.sql.in +++ b/lib/pg_repack.sql.in @@ -380,3 +380,15 @@ LANGUAGE C STABLE STRICT; CREATE FUNCTION repack.get_table_and_inheritors(regclass) RETURNS regclass[] AS 'MODULE_PATHNAME', 'repack_get_table_and_inheritors' LANGUAGE C STABLE STRICT; + +-- discussion of the security model is in https://github.com/reorg/pg_repack/pull/475 +-- +-- non-superusers need to be granted CREATE on the repack schema +-- after this grant, they can repack their own tables with --no-superuser-check +-- +-- pg_repack is not compatible with pg_maintain for allowing non-superusers +-- to repack tables they don't own +-- +GRANT USAGE ON SCHEMA repack TO PUBLIC; +GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA repack TO PUBLIC; +GRANT SELECT ON ALL TABLES IN SCHEMA repack TO PUBLIC; diff --git a/regress/expected/nosuper.out b/regress/expected/nosuper.out index 588af232..aa3a251a 100644 --- a/regress/expected/nosuper.out +++ b/regress/expected/nosuper.out @@ -5,6 +5,7 @@ SET client_min_messages = error; DROP ROLE IF EXISTS nosuper; SET client_min_messages = warning; CREATE ROLE nosuper WITH LOGIN; +GRANT CREATE ON SCHEMA repack TO nosuper; -- => OK \! pg_repack --dbname=contrib_regression --table=tbl_cluster --no-superuser-check INFO: repacking table "public.tbl_cluster" @@ -13,16 +14,19 @@ INFO: repacking table "public.tbl_cluster" ERROR: pg_repack failed with error: You must be a superuser to use pg_repack -- => ERROR \! pg_repack --dbname=contrib_regression --table=tbl_cluster --username=nosuper --no-superuser-check -ERROR: pg_repack failed with error: ERROR: permission denied for schema repack -LINE 1: select repack.version(), repack.version_sql() - ^ -GRANT ALL ON ALL TABLES IN SCHEMA repack TO nosuper; -GRANT USAGE ON SCHEMA repack TO nosuper; --- => ERROR -\! pg_repack --dbname=contrib_regression --table=tbl_cluster --username=nosuper --no-superuser-check INFO: repacking table "public.tbl_cluster" WARNING: lock_exclusive() failed for public.tbl_cluster ERROR: permission denied for table tbl_cluster -REVOKE ALL ON ALL TABLES IN SCHEMA repack FROM nosuper; -REVOKE USAGE ON SCHEMA repack FROM nosuper; +CREATE SCHEMA nosuper; +GRANT ALL ON SCHEMA nosuper TO nosuper; +SET SESSION AUTHORIZATION nosuper; +CREATE TABLE nosuper.nosuper_test (id SERIAL PRIMARY KEY, data TEXT); +INSERT INTO nosuper.nosuper_test (data) VALUES ('row1'), ('row2'); +RESET SESSION AUTHORIZATION; +-- => OK +\! pg_repack --dbname=contrib_regression --table=nosuper.nosuper_test --username=nosuper --no-superuser-check +INFO: repacking table "nosuper.nosuper_test" +DROP TABLE IF EXISTS nosuper.nosuper_test; +DROP SCHEMA IF EXISTS nosuper; +REVOKE CREATE ON SCHEMA repack FROM nosuper; DROP ROLE IF EXISTS nosuper; diff --git a/regress/expected/nosuper_1.out b/regress/expected/nosuper_1.out index f922549b..2242cc26 100644 --- a/regress/expected/nosuper_1.out +++ b/regress/expected/nosuper_1.out @@ -5,6 +5,7 @@ SET client_min_messages = error; DROP ROLE IF EXISTS nosuper; SET client_min_messages = warning; CREATE ROLE nosuper WITH LOGIN; +GRANT CREATE ON SCHEMA repack TO nosuper; -- => OK \! pg_repack --dbname=contrib_regression --table=tbl_cluster --no-superuser-check INFO: repacking table "public.tbl_cluster" @@ -13,16 +14,19 @@ INFO: repacking table "public.tbl_cluster" ERROR: pg_repack failed with error: You must be a superuser to use pg_repack -- => ERROR \! pg_repack --dbname=contrib_regression --table=tbl_cluster --username=nosuper --no-superuser-check -ERROR: pg_repack failed with error: ERROR: permission denied for schema repack -LINE 1: select repack.version(), repack.version_sql() - ^ -GRANT ALL ON ALL TABLES IN SCHEMA repack TO nosuper; -GRANT USAGE ON SCHEMA repack TO nosuper; --- => ERROR -\! pg_repack --dbname=contrib_regression --table=tbl_cluster --username=nosuper --no-superuser-check INFO: repacking table "public.tbl_cluster" WARNING: lock_exclusive() failed for public.tbl_cluster ERROR: permission denied for relation tbl_cluster -REVOKE ALL ON ALL TABLES IN SCHEMA repack FROM nosuper; -REVOKE USAGE ON SCHEMA repack FROM nosuper; +CREATE SCHEMA nosuper; +GRANT ALL ON SCHEMA nosuper TO nosuper; +SET SESSION AUTHORIZATION nosuper; +CREATE TABLE nosuper.nosuper_test (id SERIAL PRIMARY KEY, data TEXT); +INSERT INTO nosuper.nosuper_test (data) VALUES ('row1'), ('row2'); +RESET SESSION AUTHORIZATION; +-- => OK +\! pg_repack --dbname=contrib_regression --table=nosuper.nosuper_test --username=nosuper --no-superuser-check +INFO: repacking table "nosuper.nosuper_test" +DROP TABLE IF EXISTS nosuper.nosuper_test; +DROP SCHEMA IF EXISTS nosuper; +REVOKE CREATE ON SCHEMA repack FROM nosuper; DROP ROLE IF EXISTS nosuper; diff --git a/regress/sql/nosuper.sql b/regress/sql/nosuper.sql index 072f0fac..7cd83194 100644 --- a/regress/sql/nosuper.sql +++ b/regress/sql/nosuper.sql @@ -5,6 +5,8 @@ SET client_min_messages = error; DROP ROLE IF EXISTS nosuper; SET client_min_messages = warning; CREATE ROLE nosuper WITH LOGIN; +GRANT CREATE ON SCHEMA repack TO nosuper; + -- => OK \! pg_repack --dbname=contrib_regression --table=tbl_cluster --no-superuser-check -- => ERROR @@ -12,12 +14,17 @@ CREATE ROLE nosuper WITH LOGIN; -- => ERROR \! pg_repack --dbname=contrib_regression --table=tbl_cluster --username=nosuper --no-superuser-check -GRANT ALL ON ALL TABLES IN SCHEMA repack TO nosuper; -GRANT USAGE ON SCHEMA repack TO nosuper; +CREATE SCHEMA nosuper; +GRANT ALL ON SCHEMA nosuper TO nosuper; +SET SESSION AUTHORIZATION nosuper; +CREATE TABLE nosuper.nosuper_test (id SERIAL PRIMARY KEY, data TEXT); +INSERT INTO nosuper.nosuper_test (data) VALUES ('row1'), ('row2'); +RESET SESSION AUTHORIZATION; --- => ERROR -\! pg_repack --dbname=contrib_regression --table=tbl_cluster --username=nosuper --no-superuser-check +-- => OK +\! pg_repack --dbname=contrib_regression --table=nosuper.nosuper_test --username=nosuper --no-superuser-check -REVOKE ALL ON ALL TABLES IN SCHEMA repack FROM nosuper; -REVOKE USAGE ON SCHEMA repack FROM nosuper; +DROP TABLE IF EXISTS nosuper.nosuper_test; +DROP SCHEMA IF EXISTS nosuper; +REVOKE CREATE ON SCHEMA repack FROM nosuper; DROP ROLE IF EXISTS nosuper;