What this check reads
With a working postgres session (PGOK=1) it asks the running server directly with SHOW listen_addresses. Otherwise, as root, it takes the first file matched by ls /etc/postgresql/*/main/postgresql.conf /var/lib/pgsql/data/postgresql.conf /var/lib/pgsql/*/data/postgresql.conf, keeps the last listen_addresses line in it and strips the surrounding quotes. localhost, 127.0.0.1 and an empty value all pass, an empty value meaning Unix socket only, and on the config path a missing directive passes too because PostgreSQL's own default is localhost. Only * or a value containing 0.0.0.0 produces a WARN; any other specific address passes with the message asking you to confirm each one is intended.
When it applies
Runs only when PostgreSQL is detected (HAS_PG=1). With PGOK=1 it asks the running server (SHOW listen_addresses) - authoritative. Without it but with root, it greps the first postgresql.conf found under /etc/postgresql/*/main/ or /var/lib/pgsql/, which on a multi-cluster host may not be the cluster you care about. Unprivileged, it SKIPs. Note two deliberate behaviours: an empty listen_addresses is a PASS (Unix socket only), and any specific non-loopback address is also a PASS, with the message asking you to confirm each address is intended - only * and 0.0.0.0 produce a WARN.
What each result means
| Result | When | What it means |
|---|---|---|
| PASS | `localhost`, `127.0.0.1`, or empty (Unix socket only) | Nothing off this host can open a TCP connection to PostgreSQL. |
| WARN | `*` or contains `0.0.0.0` | The listener is on every interface, so 5432 answers on any network the host sits on unless a firewall stops it. |
| PASS | Any other specific address (with "confirm each address is intended") | A specific address was found and passed, with the message asking you to confirm that address is one you chose. |
| PASS | Config fallback and no directive (defaults to `localhost`) | No directive was set in the file, so PostgreSQL falls back to its own localhost default. |
| SKIP | Root or sudo required | Nothing was verified. Without root the setting could not be read, so the binding stays unknown. |
Why it matters
listen_addresses controls which interfaces accept TCP; pg_hba.conf then controls who may authenticate. Default is localhost. Docker images set *. CIS PostgreSQL "Ensure 'listen_addresses' is set appropriately".
Why it fails, and when it is wrong
- A managed cluster where the app is remote legitimately listens on a private IP; the check passes those with a note. Only
*/0.0.0.0are flagged. ::(IPv6 all) is not in the regex and would be reported as a specific address (PASS). Treat::like*.- Fallback reads only the main
postgresql.conf, notconf.d/*.conforpostgresql.auto.conf(whereALTER SYSTEMwrites). If a value was set viaALTER SYSTEM, the fallback shows the wrong thing; thePGOKpath shows the truth. - Multiple clusters (Debian
pg_lsclusters) are audited only via the default cluster/socket.
How to fix it
ALTER SYSTEM SET listen_addresses = 'localhost'; -- or '10.0.0.2'Restart PostgreSQL (this setting needs a restart, not a reload). Pair with hostssl lines scoped to the app subnet.
Verify the fix
sudo -u postgres psql -X -tAc 'SHOW listen_addresses'
# expected: localhost (or a deliberate, specific private address)
ss -tlnp | grep :5432
# expected: 127.0.0.1:5432 only
# Which file supplied the value, on a multi-cluster host:
sudo -u postgres psql -X -c "SELECT name, setting, sourcefile, sourceline FROM pg_settings WHERE name='listen_addresses'"
pg_lsclusters 2>/dev/null # Debian/Ubuntu: all clusters and their portsDebugging
The unprivileged path does nothing here. Re-run with sudo.
By design; the check cannot know whether 10.0.0.5 is intended. Confirm it is your private interface and that 5432 is firewalled to the application host, then accept.
listen_addresses needs a restart, not a reload, to take effect. The PGOK path shows the live value; pg_settings.sourcefile shows which file the value came from.
The config fallback takes the first postgresql.conf it finds. On a host with several clusters or versions, use pg_lsclusters and query each cluster on its own port.
Check ss -tlnp | grep 5432 for the truth, and remember a container publishing 5432 bypasses the host setting entirely. Even with a wide listen_addresses, pg_hba.conf still gates access - see PostgreSQL Allowed Hosts.
Sources
- PostgreSQL: listen_addresses (Connection Settings)
- PostgreSQL: ALTER SYSTEM and postgresql.auto.conf
- PostgreSQL: Client Authentication overview
- Debian/Ubuntu: pg_lsclusters / multiple clusters
How the script reads this
Next
Re-run the Network Isolation audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 7 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.
All 7 Network Isolation fixes