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/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/db/migrations/20251121132723_correct_search_path_pgbouncer.sql b/migrations/db/migrations/20251121132723_correct_search_path_pgbouncer.sql new file mode 100644 index 000000000..a359c1d73 --- /dev/null +++ b/migrations/db/migrations/20251121132723_correct_search_path_pgbouncer.sql @@ -0,0 +1,27 @@ +-- 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 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/migrations/schema-15.sql b/migrations/schema-15.sql index b160ec911..20ecd988f 100644 --- a/migrations/schema-15.sql +++ b/migrations/schema-15.sql @@ -478,6 +478,7 @@ 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 + SET search_path TO '' AS $_$ begin raise debug 'PgBouncer auth request: %', p_usename; diff --git a/migrations/schema-17.sql b/migrations/schema-17.sql index c1f73fcab..da641e799 100644 --- a/migrations/schema-17.sql +++ b/migrations/schema-17.sql @@ -479,6 +479,7 @@ 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 + 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 d8b8ddac3..3c5e0ce56 100644 --- a/migrations/schema-orioledb-17.sql +++ b/migrations/schema-orioledb-17.sql @@ -493,6 +493,7 @@ 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 + SET search_path TO '' AS $_$ begin raise debug 'PgBouncer auth request: %', p_usename; diff --git a/nix/tests/expected/pgbouncer.out b/nix/tests/expected/pgbouncer.out index 83845cb52..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'; @@ -83,5 +82,19 @@ 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_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_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 e6b56cb6b..e9beb3e78 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_valid_user_password'); +reset role; + +-- and not other non-superusers +set role postgres; +select pgbouncer.get_auth('test_valid_user_password'); +reset role; + drop role test_expired_user_password; drop role test_valid_user_password;