Can `listen_addresses` system configuration setting in Postgres stop pre-authentication exploits?
-
14-01-2021 - |
Question
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.
Solution
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
orhostnossl
rule is found when matching by source-address, ssl mode, target dbname and requested username - The matched
pg_hba.conf
rule must not specifyreject
- 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 thepublic
role all users are a member of hasCONNECT
rights, but you canREVOKE
that andGRANT
it only to specific users or roles/groups).
- A
- 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
- The "Client Authentication" chapter of the manual
listen_addresses
pg_hba.conf
- For Ubuntu and (mostly) Debian:
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.