-- auth_setup.sql — create the PgBouncer auth_query plumbing on tracksolid_db. -- ───────────────────────────────────────────────────────────────────────────── -- Run ONCE as the postgres SUPERUSER (the SECURITY DEFINER function must be owned by -- a superuser to read pg_shadow). Apply with: -- docker exec -i psql -U postgres -d tracksolid_db \ -- -v ON_ERROR_STOP=1 -v pgb_pw="$(cat ~/.pgbouncer_auth.pw)" < auth_setup.sql -- -- This lets PgBouncer authenticate ANY app user by looking its stored SCRAM verifier -- up at connect time — so you never hand-maintain a userlist of every app password. -- Only the pgbouncer_auth role itself needs an entry in userlist.txt. \set ON_ERROR_STOP on -- 1) A minimal LOGIN role PgBouncer uses to run the lookup. No other privileges. DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'pgbouncer_auth') THEN CREATE ROLE pgbouncer_auth LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE; END IF; END $$; ALTER ROLE pgbouncer_auth WITH LOGIN PASSWORD :'pgb_pw'; -- 2) The lookup function. SECURITY DEFINER (owned by postgres) so it can read -- pg_shadow; returns exactly (username, scram_verifier) for one user. CREATE SCHEMA IF NOT EXISTS pgbouncer AUTHORIZATION postgres; CREATE OR REPLACE FUNCTION pgbouncer.user_lookup( IN i_username text, OUT uname text, OUT phash text ) RETURNS record LANGUAGE sql SECURITY DEFINER SET search_path = pg_catalog AS $$ SELECT usename, passwd FROM pg_catalog.pg_shadow WHERE usename = i_username; $$; -- 3) Lock the function down to ONLY pgbouncer_auth. REVOKE ALL ON FUNCTION pgbouncer.user_lookup(text) FROM public; GRANT USAGE ON SCHEMA pgbouncer TO pgbouncer_auth; GRANT EXECUTE ON FUNCTION pgbouncer.user_lookup(text) TO pgbouncer_auth;