Avec pgBouncer 1.6 sont arrivés deux nouveaux paramètres, auth_user et auth_query, qui permettent de faciliter la gestion du fichier des utilisateurs. Le fichier des utilisateurs contient les couples utilisateur / mot de passe hashé pour s’authentifier à la place de l’utilisateur final sur le serveur PostgreSQL. Il y a deux problèmes majeurs avec ce fichier utilisateur : son maintien fastidieux et le risque de collision entre noms de roles identiques mais avec des mots de passe différents si le même pgBoucner accède à plusieurs instances PostgreSQL.

auth_query permet de définir une requête à exécuter pour obtenir le couple utilisateur / mot de passe de l’instance lorsqu’ils sont introuvables dans le fichier.

Pour se connecter à l’instance PostgreSQL et exécuter la requête, pgBouncer utilise la valeur de auth_user comme nom d’utilisateur. Si PostgreSQL demande un mot de passe, pgBouncer utilise celui défini dans le fichier des utilisateurs. On peut utiliser un utilisateur commun à toutes les instances, qu’il faut alors créer sur chaque instance, ou ajouter le paramètre auth_user au niveau de la définir de l’accès à la base de données dans la section [databases].

Enfin, l’utilisateur auth_user doit pouvoir lire le contenu de pg_shadow pour obtenir les mots de passe hashés, il est alors recommandé de créer une fonction dans chaque base de données accédée par pgBouncer sur l’instance cible. Sinon, il faut donner accès à pg_shadow par un GRANT.

Concrètement cela donne, dans /etc/pgbouncer/pgbouncer.ini :

[pgbouncer]

; ...

auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt

;; si on choisit de faire un GRANT sur pg_shadow
auth_query = SELECT usename, passwd FROM pg_shadow WHERE usename=$1

;; si on choisit de créer la fonction donnée dans la doc de pgBouncer
auth_query = SELECT pgbouncer.user_lookup($1)

Sur l’instance PostgreSQL, pour créer l’utilisateur pgbouncer chargé d’exécuter l’auth_query :

CREATE ROLE pgbouncer WITH PASSWORD 'le mot de passe';
SELECT usename, passwd FROM pg_catalog.pg_shadow WHERE username = 'pgbouncer';

Créer le fichier /etc/pgbouncer/userlist.txt et mettre le résultat du SELECT sous cette forme :

"pgbouncer" "md5ac0b6e3b0d0904c0b602607e36c256eb"

Si on choisit de faire un GRANT sur pg_shadow, faire cette opération dans toutes les bases de données accédées via pgBouncer :

GRANT SELECT ON pg_catalog.pg_shadow TO pgbouncer;

Sinon exécuter le script SQL suivant dans toutes les bases de données accédées via pgBouncer, c’est la fonction donnée dans la doc avec la création du schéma en plus :

CREATE SCHEMA pgbouncer;
REVOKE ALL ON SCHEMA pgbouncer FROM public, pgbouncer;
GRANT USAGE ON SCHEMA pgbouncer TO pgbouncer;

CREATE OR REPLACE FUNCTION pgbouncer.user_lookup(in i_username text, out uname text, out phash text)
RETURNS record AS $$
BEGIN
    SELECT usename, passwd FROM pg_catalog.pg_shadow
    WHERE usename = i_username INTO uname, phash;
    RETURN;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
REVOKE ALL ON FUNCTION pgbouncer.user_lookup(text) FROM public, pgbouncer;
GRANT EXECUTE ON FUNCTION pgbouncer.user_lookup(text) TO pgbouncer;

On peut ajouter la fonction ou le GRANT à template1 pour les futures bases de données.