What this check reads
The check asks the running server where its rules live with SHOW hba_file, then reads that file as root, dropping comments and blank lines with grep -vE '^[[:space:]]*(#|$)'. An awk pass counts the remaining lines whose first field starts with host, which takes in host, hostssl, hostnossl, hostgssenc and hostnogssenc, and whose fourth field is exactly 0.0.0.0/0, ::/0 or all. One matching line is the threshold: any count above zero is a WARN that quotes the number of entries, and a count of zero passes.
When it applies
Runs only when PostgreSQL is detected and reachable via PGOK=1; otherwise SKIP. Reading the HBA file itself needs root. It audits the ACL, not the listener - a wide entry is reported even when listen_addresses='localhost'. Matching is narrow and literal: field 1 must start with host and field 4 must be exactly 0.0.0.0/0, ::/0 or all. Two consequences the README also records as known gaps: the separate-netmask form (host all all 0.0.0.0 0.0.0.0 md5) is missed, and include/include_if_exists/include_dir directives (PostgreSQL 16+) are not followed. samehost and samenet are correctly not reported.
What each result means
| Result | When | What it means |
|---|---|---|
| WARN | Any such line | At least one pg_hba entry accepts connections from any address, so the network no longer narrows who may attempt a login. |
| PASS | All host lines name specific addresses/subnets | Every host line this parser could see names a specific address, though separate-netmask lines and included files are beyond its reach. |
| SKIP | Not accessible | Nothing was verified. The HBA file was never located or read, so the connection rules remain unknown. |
Why it matters
pg_hba.conf is PostgreSQL's network ACL. An all/0.0.0.0/0 address column removes the network dimension entirely, leaving only the password (or worse, trust). OWASP: accounts connect only from allowed hosts. CIS PostgreSQL "Ensure pg_hba.conf entries are scoped".
Why it fails, and when it is wrong
- Docker
postgresimage appendshost all all all scram-sha-256(andtrustifPOSTGRES_HOST_AUTH_METHOD=trust). - Separate-netmask syntax (
host all all 0.0.0.0 0.0.0.0 md5) is missed: field 4 is0.0.0.0, not0.0.0.0/0(false PASS). samehost/samenetkeywords are fine and not reported.include/include_dir(PostgreSQL 16+) are not followed.- A
hostssl ... 0.0.0.0/0 certline (client-certificate auth) is strong despite the wide scope; the check still warns. - Lines are evaluated even if
listen_addresses=localhost; the check audits the ACL, not the listener.
How to fix it
# pg_hba.conf
hostssl appdb app 10.0.0.0/24 scram-sha-256
hostssl all all 0.0.0.0/0 reject # explicit catch-allThen SELECT pg_reload_conf(); and check SELECT * FROM pg_hba_file_rules;.
Verify the fix
# The server's own parsed view - resolves includes, unlike the check:
sudo -u postgres psql -X -c \
"SELECT line_number, type, database, user_name, address, netmask, auth_method
FROM pg_hba_file_rules ORDER BY line_number"
# expected: no address of 0.0.0.0/0, ::/0 or 'all' except a deliberate reject catch-all
sudo -u postgres psql -X -c "SELECT * FROM pg_hba_file_rules WHERE error IS NOT NULL"
# expected: no rows
# Prove it from outside:
psql "host=<server> user=app dbname=appdb" -c 'SELECT 1'
# expected from a non-allowed address: no pg_hba.conf entry for host ...Debugging
HAS_PG=0 or PGOK=0; see the PostgreSQL Auth Methods debugging notes.
The two known gaps. Check for the separate-netmask form with sudo grep -nE '^host' "$(sudo -u postgres psql -X -tAc 'SHOW hba_file')", and for rules pulled in by include_dir. The pg_hba_file_rules query above sees both and is the reliable answer.
Client-certificate authentication is strong even with a wide address scope, but the check only looks at the address column. Legitimate finding to accept, provided the auth method really is cert and not a password method.
Deliberate; the check audits the ACL. Tighten the entry anyway so the ACL still holds if the listener is ever widened.
pg_hba.conf needs SELECT pg_reload_conf();. The check reads the file, so it sees your edit before the server does; pg_hba_file_rules shows what the server would load, and a reload makes it live.
pg_hba.conf is first-match-wins, so the catch-all must be the last line. Check ordering with the line_number column.
Sources
- PostgreSQL: The pg_hba.conf File (address field, samehost/samenet, include)
- PostgreSQL: pg_hba_file_rules
- PostgreSQL: Certificate Authentication
- Docker Hub postgres image (generated pg_hba entries)
How the script reads this
Next
Re-run the Least-Privilege Permissions audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 6 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.
All 6 Least-Privilege Permissions fixes