Postgresql – Can `listen_addresses` system configuration setting in Postgres stop pre-authentication exploits

authenticationconnectionspostgresql

You can set various configuration parameters for Postgres by either editing the postgresql.conf file manually or by calling ALTER SYSTEM commands.

One of those config settings is listen_addresses. To quote the documentation:

Specifies the TCP/IP address(es) on which the server is to listen for connections from client applications. The value takes the form of a comma-separated list of host names and/or numeric IP addresses. The special entry * corresponds to all available IP interfaces. The entry 0.0.0.0 allows listening for all IPv4 addresses and :: allows listening for all IPv6 addresses. If the list is empty, the server does not listen on any IP interface at all, in which case only Unix-domain sockets can be used to connect to it. The default value is localhost, which allows only local TCP/IP “loopback” connections to be made. While client authentication (Chapter 20) allows fine-grained control over who can access the server, listen_addresses controls which interfaces accept connection attempts, which can help prevent repeated malicious connection requests on insecure network interfaces. This parameter can only be set at server start.

➥ Does this mean listen_addresses theoretically stops pre-authentication 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?

Of course, ideally, a firewall on the host operating system would also be in place to block unwanted incoming connections. But let’s ignore firewalls for the purpose of this Question.

Best Answer

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?

No. 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. 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 pg_hba.conf configuration.

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 111.1.0.0/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.allow and /etc/hosts.deny) must permit the connection. (Assuming they're supported on your OS and postgres is compiled to use them)
  • Within PostgreSQL code:
    • A pg_hba.conf host, hostssl or 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 reject
    • 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.

I ignored 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 libpq 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 host, hostssl or 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 hostssl in 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) 127.0.0.1 and ::1. Or even better, use unix sockets.

See also


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.