What this check reads
Asks the server where its HBA file is (SHOW hba_file), strips comments and blank lines with grep -vE '^[[:space:]]*(#|$)', then counts the remaining lines whose last field ($NF) is trust, password or md5. Evaluated in that order: any trust is FAIL, else any password is FAIL, else any md5 is WARN, else PASS. The threshold on each of the three is a single line, and the count that triggered the result is quoted in the message.
When it applies
Runs only when PostgreSQL is detected (HAS_PG=1: a psql binary, a running postgres process, or /etc/postgresql / /var/lib/pgsql). Reading the HBA file needs PGOK=1 - the script must be able to run psql as the postgres OS user via root/sudo - and then reads the file root-only. It evaluates the file on disk, which is the pending configuration; a file edited but not reloaded is judged before the server has adopted it. Only the file named by SHOW hba_file is read: include, include_if_exists and include_dir directives (PostgreSQL 16+) are not followed.
What each result means
| Result | When | What it means |
|---|---|---|
| SKIP | PostgreSQL not installed / not accessible | Nothing was read. The two reasons are reported separately, and neither of them is a pass. |
| FAIL | At least one `trust` line | Clients matching those lines are logged in as whatever role they ask for, with no credential at all. |
| FAIL | At least one `password` (cleartext) line | Matching clients send their password unencrypted, which is only safe inside TLS. Move those lines to scram-sha-256. |
| WARN | At least one `md5` line | Those logins still work, but a captured md5 hash is replayable and crackable offline. Migrate them to scram-sha-256. |
| PASS | Only `peer`, `scram-sha-256`, `cert`, `ident`, `reject`, etc. | No line in that file lets a client in without a modern credential check. |
Why it matters
trust means any client matching that line is logged in as any requested role with no credential. password sends the password in cleartext (only safe inside TLS). md5 is unsalted MD5 over password+username; captured hashes are replayable and crackable offline. PostgreSQL recommends scram-sha-256 (SASL, RFC 7677) and, since PostgreSQL 18, emits a deprecation warning for md5. CIS PostgreSQL Benchmark: "Ensure the 'trust' method is not used" and "Ensure password encryption is SCRAM".
Why it fails, and when it is wrong
- Old
pg_hba.conffiles upgraded in place keepmd5. Default files since PostgreSQL 14 usescram-sha-256. - Docker
postgresimage withPOSTGRES_HOST_AUTH_METHOD=trustaddshost all all all trust. - Lines with auth options after the method (
md5 map=...,scram-sha-256 clientcert=verify-full,ldap ldapserver=...) are not counted because$NFis the option, not the method. This can mask atrust/md5line that has trailing options (rare) and apasswordline with options. include,include_if_exists,include_dir(PostgreSQL 16+) are not followed.- A
local all postgres trustline is sometimes left byinitdbhelpers; it is still a FAIL because any OS user canpsql -U postgres.
How to fix it
Find the file PostgreSQL is actually using
Ask the running server rather than assuming the packaged path, because a cluster can be pointed at an HBA file anywhere on disk.
HBA=$(sudo -u postgres psql -X -tAc 'SHOW hba_file')Read the active rules
Strip the comments and blank lines, then read the last column of every line that is left. That column is the authentication method.
sudo grep -vE '^[[:space:]]*(#|$)' "$HBA"Replace the weak methods
Edit that file and change trust, password and md5 to scram-sha-256 on every line that has them, local lines included, or to peer for local Unix-socket admin access. If any role's password is still stored as an md5 hash, do step 4 first: an md5 hash cannot satisfy a scram line, and those logins break the moment you reload.
SELECT pg_reload_conf();
SELECT * FROM pg_hba_file_rules WHERE error IS NOT NULL;Set the hashing algorithm before re-hashing
The stored hash format follows password_encryption at the time of the ALTER, so set it and reload before rewriting anything.
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();Then re-hash every password-authenticated role, with \password username in psql or ALTER ROLE username PASSWORD '...'.
Verify the fix
# Ask the server itself which rules are live, rather than reading the file:
sudo -u postgres psql -X -c "SELECT type,database,user_name,address,auth_method FROM pg_hba_file_rules WHERE auth_method IN ('trust','password','md5')"
# expected: no rows
sudo -u postgres psql -X -c "SELECT * FROM pg_hba_file_rules WHERE error IS NOT NULL"
# expected: no rows (no syntax errors pending)
sudo -u postgres psql -X -tAc 'SHOW password_encryption'
# expected: scram-sha-256Debugging
The probe found nothing. Confirm with command -v psql; pgrep -a postgres; ls -d /etc/postgresql /var/lib/pgsql 2>/dev/null.
PGOK=0. Run exactly what the script runs: sudo su -s /bin/sh postgres -c 'psql -X -tAc "SELECT 1"'. Failures are usually a stopped cluster, a non-default port/socket (PGPORT, unix_socket_directories), or no usable sudo.
The check reads the file, so an unreloaded edit shows immediately, but the server has not adopted it. Run SELECT pg_reload_conf(); and compare pg_hba_file_rules (live) with the file.
You probably edited a file the server does not use. sudo -u postgres psql -X -tAc 'SHOW hba_file' prints the only path the check reads. Rules pulled in by include/include_dir are invisible to the check, so a clean report does not prove included files are clean - inspect pg_hba_file_rules, which does resolve includes.
The script matches on the last field ($NF), so md5 map=... is missed. Cross-check with the pg_hba_file_rules query above, which parses the method properly.
Existing passwords hashed under md5 cannot satisfy a scram line. Re-hash first (ALTER ROLE ... PASSWORD ... with password_encryption already set to scram), then change the HBA lines.
Sources
- PostgreSQL: The pg_hba.conf File
- PostgreSQL: Password Authentication (scram vs md5 vs password)
- PostgreSQL: Trust Authentication
- PostgreSQL: pg_hba_file_rules view
- PostgreSQL wiki: How to Upgrade Passwords to SCRAM
- RFC 7677 SCRAM-SHA-256
- Docker Hub postgres image:
POSTGRES_HOST_AUTH_METHOD
How the script reads this
Next
Re-run the Authentication audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 8 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.
All 8 Authentication fixes