From 0f2bc84cae95eccbb40eaac0a8d748195025199f Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 21 Nov 2025 14:19:10 +0100 Subject: [PATCH 1/5] fix: search path and migration grants search_path not set on pgbouncer.get_auth and later migrations don't apply permissions correctly. --- .../pgbouncer_auth_schema.sql | 4 ++- ...21132723_correct_search_path_pgbouncer.sql | 26 +++++++++++++++++++ migrations/schema-15.sql | 6 ++++- migrations/schema-17.sql | 6 ++++- migrations/schema-orioledb-17.sql | 6 ++++- nix/tests/expected/pgbouncer.out | 16 ++++++++++++ nix/tests/sql/pgbouncer.sql | 10 +++++++ 7 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 migrations/db/migrations/20251121132723_correct_search_path_pgbouncer.sql diff --git a/ansible/files/pgbouncer_config/pgbouncer_auth_schema.sql b/ansible/files/pgbouncer_config/pgbouncer_auth_schema.sql index c10ce44fd..d9d8af089 100644 --- a/ansible/files/pgbouncer_config/pgbouncer_auth_schema.sql +++ b/ansible/files/pgbouncer_config/pgbouncer_auth_schema.sql @@ -14,7 +14,9 @@ BEGIN SELECT usename::TEXT, passwd::TEXT FROM pg_catalog.pg_shadow WHERE usename = p_usename; END; -$$ LANGUAGE plpgsql SECURITY DEFINER; +$$ LANGUAGE plpgsql +SET search_path = '' +SECURITY DEFINER; REVOKE ALL ON FUNCTION pgbouncer.get_auth(p_usename TEXT) FROM PUBLIC; GRANT EXECUTE ON FUNCTION pgbouncer.get_auth(p_usename TEXT) TO pgbouncer; diff --git a/migrations/db/migrations/20251121132723_correct_search_path_pgbouncer.sql b/migrations/db/migrations/20251121132723_correct_search_path_pgbouncer.sql new file mode 100644 index 000000000..b9e48c2be --- /dev/null +++ b/migrations/db/migrations/20251121132723_correct_search_path_pgbouncer.sql @@ -0,0 +1,26 @@ +-- migrate:up + +create or replace function pgbouncer.get_auth(p_usename text) returns table (username text, password text) + language plpgsql + set search_path = '' + security definer + as $$ +begin + raise debug 'PgBouncer auth request: %', p_usename; + + return query + select + rolname::text, + case when rolvaliduntil < now() + then null + else rolpassword::text + end + from pg_authid + where rolname=$1 and rolcanlogin; +end; +$$; + +revoke execute on function pgbouncer.get_auth(text) from public; +grant execute on function pgbouncer.get_auth(text) to pgbouncer; +-- migrate:down + diff --git a/migrations/schema-15.sql b/migrations/schema-15.sql index b160ec911..cf3e28d0d 100644 --- a/migrations/schema-15.sql +++ b/migrations/schema-15.sql @@ -477,7 +477,9 @@ COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeh -- CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) - LANGUAGE plpgsql SECURITY DEFINER + LANGUAGE plpgsql + SET search_path = '' + SECURITY DEFINER AS $_$ begin raise debug 'PgBouncer auth request: %', p_usename; @@ -494,6 +496,8 @@ begin end; $_$; +REVOKE ALL ON FUNCTION pgbouncer.get_auth(p_usename TEXT) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pgbouncer.get_auth(p_usename TEXT) TO pgbouncer; SET default_tablespace = ''; diff --git a/migrations/schema-17.sql b/migrations/schema-17.sql index c1f73fcab..e63853ae9 100644 --- a/migrations/schema-17.sql +++ b/migrations/schema-17.sql @@ -478,7 +478,9 @@ COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeh -- CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) - LANGUAGE plpgsql SECURITY DEFINER + LANGUAGE plpgsql + SET search_path = '' + SECURITY DEFINER AS $_$ begin raise debug 'PgBouncer auth request: %', p_usename; @@ -495,6 +497,8 @@ begin end; $_$; +REVOKE ALL ON FUNCTION pgbouncer.get_auth(p_usename TEXT) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pgbouncer.get_auth(p_usename TEXT) TO pgbouncer; SET default_tablespace = ''; diff --git a/migrations/schema-orioledb-17.sql b/migrations/schema-orioledb-17.sql index d8b8ddac3..85abfa3a2 100644 --- a/migrations/schema-orioledb-17.sql +++ b/migrations/schema-orioledb-17.sql @@ -492,7 +492,9 @@ COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeh -- CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) - LANGUAGE plpgsql SECURITY DEFINER + LANGUAGE plpgsql + SET search_path = '' + SECURITY DEFINER AS $_$ begin raise debug 'PgBouncer auth request: %', p_usename; @@ -509,6 +511,8 @@ begin end; $_$; +REVOKE ALL ON FUNCTION pgbouncer.get_auth(p_usename TEXT) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pgbouncer.get_auth(p_usename TEXT) TO pgbouncer; SET default_tablespace = ''; diff --git a/nix/tests/expected/pgbouncer.out b/nix/tests/expected/pgbouncer.out index 83845cb52..a23a7590a 100644 --- a/nix/tests/expected/pgbouncer.out +++ b/nix/tests/expected/pgbouncer.out @@ -83,5 +83,21 @@ select pgbouncer.get_auth('test_valid_user_password'); (test_valid_user_password,SCRAM-SHA-256$4096:testsaltbase64$storedkeybase64$serverkeybase64) (1 row) + +-- Test pgbouncer.get_auth is executable by the pgbouncer user +set role pgbouncer; +select pgbouncer.get_auth('test_expired_user_password'); + get_auth +------------------------------- + (test_expired_user_password,) +(1 row) +reset role; + +-- and not other non-superusers +set role postgres; +select pgbouncer.get_auth('test_expired_user_password'); +ERROR: permission denied for function get_auth +reset role; + drop role test_expired_user_password; drop role test_valid_user_password; diff --git a/nix/tests/sql/pgbouncer.sql b/nix/tests/sql/pgbouncer.sql index e6b56cb6b..a1f600aaf 100644 --- a/nix/tests/sql/pgbouncer.sql +++ b/nix/tests/sql/pgbouncer.sql @@ -62,5 +62,15 @@ select pgbouncer.get_auth('test_expired_user_password'); select pgbouncer.get_auth('test_valid_user_password'); +-- Test pgbouncer.get_auth is executable by the pgbouncer user +set role pgbouncer; +select pgbouncer.get_auth('test_expired_user_password'); +reset role; + +-- and not other non-superusers +set role postgres; +select pgbouncer.get_auth('test_expired_user_password'); +reset role; + drop role test_expired_user_password; drop role test_valid_user_password; From 452e8e3788c9b55776085e40d31ea33b65ec427e Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 21 Nov 2025 11:34:25 -0500 Subject: [PATCH 2/5] tests: update pgbouncer tests --- nix/tests/expected/pgbouncer.out | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/nix/tests/expected/pgbouncer.out b/nix/tests/expected/pgbouncer.out index a23a7590a..5b4946b4e 100644 --- a/nix/tests/expected/pgbouncer.out +++ b/nix/tests/expected/pgbouncer.out @@ -83,7 +83,6 @@ select pgbouncer.get_auth('test_valid_user_password'); (test_valid_user_password,SCRAM-SHA-256$4096:testsaltbase64$storedkeybase64$serverkeybase64) (1 row) - -- Test pgbouncer.get_auth is executable by the pgbouncer user set role pgbouncer; select pgbouncer.get_auth('test_expired_user_password'); @@ -91,13 +90,16 @@ select pgbouncer.get_auth('test_expired_user_password'); ------------------------------- (test_expired_user_password,) (1 row) -reset role; +reset role; -- and not other non-superusers set role postgres; select pgbouncer.get_auth('test_expired_user_password'); -ERROR: permission denied for function get_auth -reset role; + get_auth +------------------------------- + (test_expired_user_password,) +(1 row) +reset role; drop role test_expired_user_password; drop role test_valid_user_password; From 9897f8085fd7e0dcc198881c6f070eb7553a29d3 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 21 Nov 2025 14:06:04 -0500 Subject: [PATCH 3/5] fix: update files used for schema tests --- migrations/schema-15.sql | 7 ++----- migrations/schema-17.sql | 5 ++--- migrations/schema-orioledb-17.sql | 5 ++--- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/migrations/schema-15.sql b/migrations/schema-15.sql index cf3e28d0d..20ecd988f 100644 --- a/migrations/schema-15.sql +++ b/migrations/schema-15.sql @@ -477,9 +477,8 @@ COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeh -- CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) - LANGUAGE plpgsql - SET search_path = '' - SECURITY DEFINER + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' AS $_$ begin raise debug 'PgBouncer auth request: %', p_usename; @@ -496,8 +495,6 @@ begin end; $_$; -REVOKE ALL ON FUNCTION pgbouncer.get_auth(p_usename TEXT) FROM PUBLIC; -GRANT EXECUTE ON FUNCTION pgbouncer.get_auth(p_usename TEXT) TO pgbouncer; SET default_tablespace = ''; diff --git a/migrations/schema-17.sql b/migrations/schema-17.sql index e63853ae9..a41c67b48 100644 --- a/migrations/schema-17.sql +++ b/migrations/schema-17.sql @@ -478,9 +478,8 @@ COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeh -- CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) - LANGUAGE plpgsql - SET search_path = '' - SECURITY DEFINER + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' AS $_$ begin raise debug 'PgBouncer auth request: %', p_usename; diff --git a/migrations/schema-orioledb-17.sql b/migrations/schema-orioledb-17.sql index 85abfa3a2..dd1480159 100644 --- a/migrations/schema-orioledb-17.sql +++ b/migrations/schema-orioledb-17.sql @@ -492,9 +492,8 @@ COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeh -- CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) - LANGUAGE plpgsql - SET search_path = '' - SECURITY DEFINER + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' AS $_$ begin raise debug 'PgBouncer auth request: %', p_usename; From a55d5a9d665b45b6326b1145faaf5eec3525e2a6 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Mon, 24 Nov 2025 10:57:23 +0100 Subject: [PATCH 4/5] chore: revoke from postgres role --- ...21132723_correct_search_path_pgbouncer.sql | 3 ++- nix/tests/expected/pgbouncer.out | 19 +++++++------------ nix/tests/sql/pgbouncer.sql | 4 ++-- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/migrations/db/migrations/20251121132723_correct_search_path_pgbouncer.sql b/migrations/db/migrations/20251121132723_correct_search_path_pgbouncer.sql index b9e48c2be..a359c1d73 100644 --- a/migrations/db/migrations/20251121132723_correct_search_path_pgbouncer.sql +++ b/migrations/db/migrations/20251121132723_correct_search_path_pgbouncer.sql @@ -20,7 +20,8 @@ begin end; $$; -revoke execute on function pgbouncer.get_auth(text) from public; +revoke all on function pgbouncer.get_auth(text) from public; +revoke execute on function pgbouncer.get_auth(text) from postgres; grant execute on function pgbouncer.get_auth(text) to pgbouncer; -- migrate:down diff --git a/nix/tests/expected/pgbouncer.out b/nix/tests/expected/pgbouncer.out index 5b4946b4e..196e8cd5d 100644 --- a/nix/tests/expected/pgbouncer.out +++ b/nix/tests/expected/pgbouncer.out @@ -62,9 +62,8 @@ ORDER BY object_name, grantee, privilege_type; schema | object_name | grantee | privilege_type -----------+-------------+----------------+---------------- pgbouncer | get_auth | pgbouncer | EXECUTE - pgbouncer | get_auth | postgres | EXECUTE pgbouncer | get_auth | supabase_admin | EXECUTE -(3 rows) +(2 rows) -- Ensure that pgbouncer.get_auth() function does not return an expired password create role test_expired_user_password with login password 'expired_password' valid until '2000-01-01 00:00:00+00'; @@ -85,21 +84,17 @@ select pgbouncer.get_auth('test_valid_user_password'); -- Test pgbouncer.get_auth is executable by the pgbouncer user set role pgbouncer; -select pgbouncer.get_auth('test_expired_user_password'); - get_auth -------------------------------- - (test_expired_user_password,) +select pgbouncer.get_auth('test_valid_user_password'); + get_auth +---------------------------------------------------------------------------------------------- + (test_valid_user_password,SCRAM-SHA-256$4096:testsaltbase64$storedkeybase64$serverkeybase64) (1 row) reset role; -- and not other non-superusers set role postgres; -select pgbouncer.get_auth('test_expired_user_password'); - get_auth -------------------------------- - (test_expired_user_password,) -(1 row) - +select pgbouncer.get_auth('test_valid_user_password'); +ERROR: permission denied for function get_auth reset role; drop role test_expired_user_password; drop role test_valid_user_password; diff --git a/nix/tests/sql/pgbouncer.sql b/nix/tests/sql/pgbouncer.sql index a1f600aaf..e9beb3e78 100644 --- a/nix/tests/sql/pgbouncer.sql +++ b/nix/tests/sql/pgbouncer.sql @@ -64,12 +64,12 @@ select pgbouncer.get_auth('test_valid_user_password'); -- Test pgbouncer.get_auth is executable by the pgbouncer user set role pgbouncer; -select pgbouncer.get_auth('test_expired_user_password'); +select pgbouncer.get_auth('test_valid_user_password'); reset role; -- and not other non-superusers set role postgres; -select pgbouncer.get_auth('test_expired_user_password'); +select pgbouncer.get_auth('test_valid_user_password'); reset role; drop role test_expired_user_password; From 35b3e863e5905f6b084271f31eb0a380410a5651 Mon Sep 17 00:00:00 2001 From: Bobbie Soedirgo Date: Wed, 26 Nov 2025 12:49:28 +0800 Subject: [PATCH 5/5] tmp DO NOT MERGE --- ansible/vars.yml | 6 +++--- migrations/schema-17.sql | 2 -- migrations/schema-orioledb-17.sql | 2 -- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/ansible/vars.yml b/ansible/vars.yml index 84035bca5..784658ce3 100644 --- a/ansible/vars.yml +++ b/ansible/vars.yml @@ -10,9 +10,9 @@ postgres_major: # Full version strings for each major version postgres_release: - postgresorioledb-17: "17.6.0.014-orioledb" - postgres17: "17.6.1.057" - postgres15: "15.14.1.057" + postgresorioledb-17: "17.6.0.015-orioledb" + postgres17: "17.6.1.058" + postgres15: "15.14.1.058" # Non Postgres Extensions pgbouncer_release: 1.19.0 diff --git a/migrations/schema-17.sql b/migrations/schema-17.sql index a41c67b48..da641e799 100644 --- a/migrations/schema-17.sql +++ b/migrations/schema-17.sql @@ -496,8 +496,6 @@ begin end; $_$; -REVOKE ALL ON FUNCTION pgbouncer.get_auth(p_usename TEXT) FROM PUBLIC; -GRANT EXECUTE ON FUNCTION pgbouncer.get_auth(p_usename TEXT) TO pgbouncer; SET default_tablespace = ''; diff --git a/migrations/schema-orioledb-17.sql b/migrations/schema-orioledb-17.sql index dd1480159..3c5e0ce56 100644 --- a/migrations/schema-orioledb-17.sql +++ b/migrations/schema-orioledb-17.sql @@ -510,8 +510,6 @@ begin end; $_$; -REVOKE ALL ON FUNCTION pgbouncer.get_auth(p_usename TEXT) FROM PUBLIC; -GRANT EXECUTE ON FUNCTION pgbouncer.get_auth(p_usename TEXT) TO pgbouncer; SET default_tablespace = '';