March 25, 2022·4 min read·engineeringpostgresqlpgbouncerauthentication

PgBouncer 1.17 moves database errors behind authentication

PgBouncer 1.17 adds host lists and changes when clients can learn about missing databases and backend failures. Credential-file reloads also become explicit.

A connection pooler knows more about a database installation than an unauthenticated client should necessarily learn. It knows which database names exist and can encounter backend failures containing configuration details. Returning those details before login turns the connection handshake into a way to inspect the installation without establishing an identity.

The March 25 announcement of PgBouncer 1.17.0 introduces host lists and several compatibility fixes. Its more consequential behavioral changes concern authentication. A missing database now produces its error after authentication, and server-disconnect errors no longer go to a client before login. The release changelog explains the information-disclosure reasoning behind both changes.

I regard connection pooling as part of the database operating environment. PostgreSQL remains my default relational choice, but a pooler contributes its own authentication behavior and configuration rules. Those rules deserve the same attention as the server settings behind it.

An error can disclose configuration

Before this change, asking for a nonexistent database could reveal that it did not exist before authentication completed. Repeating that operation with different names gave an unauthenticated client a way to probe the configured database collection. Deferring the error closes that particular source of information.

The changelog relates this behavior to the earlier treatment of missing users in version 1.15.0. That release continued authentication for an unknown user and returned a generic failure, while retaining details in the PgBouncer log. The common concern is that a useful diagnostic for an administrator can also answer questions posed by someone who has not authenticated.

Server errors have a similar problem. A backend disconnect during login can carry details that are not intended for unauthenticated clients. Version 1.17 suppresses those errors before client login. This does not make the backend healthy or remove the need for diagnostic information. It changes when the client is entitled to receive the failure.

There is a practical distinction between failing a connection and explaining the installation. An authentication exchange can reject a client without confirming a database inventory. The error ordering therefore contributes to access control even though it does not grant or revoke a database privilege itself.

Fast failure interacts with SCRAM recovery

The change also affects the fast-fail mechanism used after a server login failure. The versioned configuration reference describes server_login_retry. During its retry interval, new clients receive an error instead of waiting for another backend connection attempt. This prevents clients accumulating indefinitely behind a known connection problem.

Version 1.17 stops applying fast-fail at connection time, but still applies it immediately after authentication. The changelog identifies a SCRAM pass-through case where completing the client authentication exchange is necessary to repair a server-side connection through reauthentication. Rejecting the client too early prevents that repair path from running.

This is a useful example of an optimization depending on protocol state. Avoiding another connection attempt sounds independent of authentication until authentication itself supplies the exchange needed to recover. Moving the decision later preserves the rejection behavior in most situations while allowing that exchange to occur.

I prefer this explanation to a broad claim that connection handling is more reliable. It identifies the sequencing problem and the reason a successful client exchange can matter even when the previous backend login failed. Administrators can connect the behavior to a specific authentication configuration.

Host selection and connection reuse are separate

The release also allows a comma-separated host list in a database definition. PgBouncer connects to the listed hosts in round-robin order. The feature gives the database definition a direct way to name several destinations without requiring that the list be represented by a single DNS name.

That selection rule does not determine how PgBouncer reuses connections already in its pool. The configuration reference describes server_round_robin separately. By default, PgBouncer reuses server connections in last-in, first-out order, concentrating work on a smaller number of connections. The reference recommends round-robin reuse when the database address represents a round-robin arrangement, including a host list.

An illustrative configuration fragment enables that reuse policy.

[pgbouncer]
server_round_robin = 1

The setting concerns pooled connection reuse, while the host-list feature concerns opening connections to destinations. Keeping those decisions distinct explains why a list of hosts alone does not describe the resulting distribution of queries. Neither mechanism establishes that the listed databases contain equivalent data or accept the same operations. Those are properties of the destinations an administrator chooses.

Credential changes now require a reload

Another change removes automatic reloading of auth_file when its contents change. Version 1.17 reads that file again on configuration reload. Replacing the file and activating its new contents are therefore separate actions, which matters to any existing process that only writes a replacement credential file.

The release also repairs OpenSSL 3 support, which previously caused crashes, and increases the supported password length again. Together with the authentication-ordering changes, these details describe concrete changes to establishing a usable connection. A deployment procedure needs an explicit reload after changing its authentication file, and its client-visible errors now reflect the later point at which PgBouncer releases information about the requested database and backend connection.