Enable TLS for PostgreSQL
postgresql.confssl = onssl_cert_file = '/etc/postgresql/ssl/server.crt'ssl_key_file = '/etc/postgresql/ssl/server.key'ssl_min_protocol_version = 'TLSv1.2'Then require it in pg_hba.conf, which is the half that matters:
# TYPE DATABASE USER ADDRESS METHODhostssl appdb appuser 10.0.1.0/24 scram-sha-256hostnossl all all 0.0.0.0/0 rejectTurning ssl on for the first time needs a restart; certificate changes
afterwards only need a reload.
Why it matters
Section titled “Why it matters”ssl defaults to off. That is one of the few PostgreSQL defaults that is
not the safe choice, and it means a source-built or minimally-configured cluster
sends every query, every result row and every parameter in cleartext.
SCRAM does not save you here. It protects the stored hash and resists replay of the credential — but the data itself, and everything you select, crosses the wire in the clear. Anyone on the path reads your table contents.
Debian and Ubuntu packages enable ssl = on with a snakeoil certificate at
install time, so a packaged cluster is usually encrypting already. A container or
a compiled-from-source cluster usually is not. Check rather than assume.
ssl = on offers TLS; hostssl requires it
Section titled “ssl = on offers TLS; hostssl requires it”This is the trap, and it mirrors the same one in Redis and SSH: enabling a secure option is not the same as disabling the insecure one.
With ssl = on and a host rule, TLS is available. A client that asks for it
gets it; a client that doesn’t, connects in plaintext and is accepted. Since
libpq’s default sslmode is prefer, most clients will negotiate TLS — and any
client that doesn’t silently won’t, with no error and no log entry that stands
out.
Three rule types decide this:
| Rule | Matches |
|---|---|
host |
TLS and plaintext — offers encryption, requires nothing |
hostssl |
TLS only |
hostnossl |
Plaintext only — useful as an explicit reject |
Use hostssl for anything crossing a network. The hostnossl ... reject line is
belt and braces: it turns a plaintext attempt into a loud refusal instead of a
quiet success.
The client’s sslmode is the other half
Section titled “The client’s sslmode is the other half”A server can require TLS; only the client can require that it’s the right
server. sslmode=require encrypts but does not verify the certificate — it
stops passive eavesdropping and does nothing against an active
machine-in-the-middle.
For that you need the client to verify:
psql "host=db.internal dbname=appdb sslmode=verify-full sslrootcert=/etc/ssl/ca.crt"verify-full checks the chain and the hostname. It is the only mode that means
what people usually think require means.
Related
Section titled “Related”- Scope pg_hba.conf rules — where
hostsslgoes. - Use scram-sha-256, not md5 — protects the credential; this protects the data.