Skip to content

fix(ssl): sslmode=prefer does not upgrade to TLS on TLS-capable server #722

@NikolayS

Description

@NikolayS

Problem

With sslmode=prefer (the default), rpg connects to a TLS-capable server but does not negotiate TLS — ssl=f in pg_stat_ssl.

psql with the same settings connects with TLS enabled (ssl=t).

Repro

# Server has TLS enabled (self-signed cert)
PGPASSWORD=testpass rpg -h localhost -p 15436 -U postgres \
  -c "SELECT ssl FROM pg_stat_ssl WHERE pid = pg_backend_pid()"
# => ssl=f

PGPASSWORD=testpass psql -h localhost -p 15436 -U postgres \
  -c "SELECT ssl FROM pg_stat_ssl WHERE pid = pg_backend_pid()"
# => ssl=t

Expected

sslmode=prefer should attempt TLS first and only fall back to plaintext if TLS is unavailable. rpg appears to fall back to plaintext even when TLS is available.

Context

The self-signed cert causes a rustls verification failure, which rpg treats as "TLS unavailable" and falls back. But psql (OpenSSL) is more permissive — with prefer it accepts self-signed certs without verification (just like require). rpg should do the same: for prefer, use the no-verify TLS config (NoVerifier, same as require), not the webpki roots config.

Fix hint

In connect_one(), the SslMode::Prefer branch calls connect_tls_default() which uses webpki roots. It should use connect_tls_with_config() with make_tls_config_require() (no-verify) — same as sslmode=require. TLS without cert verification is strictly better than no TLS.

Discovered

Full connection matrix audit, 2026-03-24 (Section D).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions