listen_addresses filters connections before postgres sees them
➥ Does this mean
listen_addresses theoretically stops pre-authentication exploits?
Yes. If PostgreSQL's postmaster isn't listening on a given interface (as identified by its local address) then no remote host may connect to it via that interface. The operating system will report the TCP port as closed and send a TCP RST to the connecting host; PostgreSQL code is never reached, so PostgreSQL bugs cannot be exploited, even pre-auth ones.1.
listen_addresses blocks pre-auth exploits
Is there any engagement between the Postgres server and the incoming connection which might possibly result in an exploit? Or will the forbidden incoming connection be blocked without any engagement?
listen_addresses configures the listening TCP socket at the operating system level, binding it only to the network interface(s) specified2. It filters on the connection target address specified by the remote host. The OS won't tell PostgreSQL about connections to other interfaces at all.
Note that the same is is not true of
pg_hba.conf controls remote-host source address filtering and authentication configuration. The PostgreSQL postmaster does handle any connection that comes in on a listened-on address and is then rejected by
You can do sender-address based filtering before PostgreSQL sees the connection by configuring operating system firewall rules. Those will prevent PostgreSQL pre-auth exploits by blocking the connection from ever reaching PostgreSQL.
So if you know that only the hosts in network 220.127.116.11/16 have any business connecting to your PostgreSQL it's a good idea to configure a firewall rule accordingly. You should set
pg_hba.conf as a fallback, but the firewall rule should stop anyone even attempting to connect to postgres itself.
Understanding authentication flow
To connect to postgres you must pass a series of "gates" of sorts. Ignoring UNIX sockets for this explanation, we have:
- OS network and TCP level, before you hit PostgreSQL code:
- Any border router or firewall between you and postgres must permit the TCP connection
- Any operating system firewall configured on the postgres host must permit the TCP connection
listen_addresses - postgres must be listening on the interface or the OS treats the TCP port as closed
- Any network security extensions like TCP Wrappers (
/etc/hosts.deny) must permit the connection. (Assuming they're supported on your OS and postgres is compiled to use them)
- Within PostgreSQL code:
hostnossl rule is found when matching by source-address, ssl mode, target dbname and requested username
- The matched
pg_hba.conf rule must not specify
- If SSL client certificate checks are enabled, the SSL client certificate satisfies the configured CA cert.
- The requested username and database name must both exist in the PostgreSQL catalogs
- The requested username or a role it inherits must have the
LOGIN option in the PostgreSQL catalogs
- The requested user must have
CONNECT rights to the requested database. (By default the
public role all users are a member of has
CONNECT rights, but you can
REVOKE that and
GRANT it only to specific users or roles/groups).
- Yay, you've connected
If a connection is blocked at an earlier stage, it never interacts with later stages. I haven't checked the exact ordering of the dbname vs username privilege checks, but the rest is right.
pg_ident.conf and username mappings, client cert DN mappings, the details of things like SSPI/GSSAPI and low level auth methods, etc here.
Now, if you're using UNIX sockets (
unix_socket_directories, and a
host address that's a path or omitted entirely), the PostgreSQL stage is the same except that
pg_hba.conf matching doesn't check source-address and looks for
local lines instead of
hostnossl lines. The
peer auth mode is supported to require a unix username to postgres username match. Details in the manual.
Exposing PostgreSQL to the Internet
Let me note that pre-auth exploits for PostgreSQL are not wholly unheard of, but are rare. It's fairly routine to expose PostgreSQL on the Internet directly.
You should use
pg_hba.conf to enforce SSL connections to protect authentication exchanges and make casual scanning a bit harder. And you should adopt the same protective measures you use for any other Internet-exposed service: use fail2ban or similar, use an IDS, monitor logs, and have firewall rules to exclude anything that you know has no business connecting.
However, if you don't need to expose PostgreSQL to the Internet, don't do it. In particular, if you only need loopback connections, bind PostgreSQL to the loopback address(es)
::1. Or even better, use unix sockets.
1 The attacker could still exploit OS bugs in the network stack. Or (very unlikely) they might be able to use tricks like source-address spoofing to fool a buggy OS into letting it send an initial packet to PostgreSQL even when it's bound to a different interface. Any modern, sensibly configured OS will prevent that.
2 Internally each
listen_addresses entry is used to make a separate listening TCP socket. For UNIX-like OSes it's passed to the
bind(...) call on each socket. See
postmaster.c around line 1012,
if (ListenAddresses), and see the
StreamServerPort adapter in
src/backend/libpq/pqcomm.c around line 532.