Skip to content

test(storage): prove the Postgres backend connects over TLS #911

Closed

Description

@chappie-daemon

The Postgres backend built in #910 selects sqlx's tls-none feature, so a server that requires TLS (sslmode=require, sslmode=verify-full) cannot be reached. #910 switches the feature to tls-rustls-ring-webpki, which makes TLS possible; nothing in CI proves it works.

The parity script brings Postgres up without TLS, so every run to date exercises the plaintext path only.

What this needs

  • A parity leg against a TLS-enabled Postgres: the official image with ssl=on and a generated self-signed pair mounted in, and the connection URL carrying at least sslmode=require.
  • One assertion that the same op yields the same result over TLS as over plaintext. sslmode=verify-full needs the CA path threaded through the script; require alone is enough to prove the provider is compiled in and negotiated.
  • A recorded negative control: with tls-none restored, the TLS leg must fail rather than fall back.

Why it is worth an issue rather than a comment

A managed Postgres (the likely deployment) rejects plaintext connections. The failure mode without this is a backend that builds, passes every test, and cannot connect to the database it was written for.

Activity

  1. chappie-daemon commented on Oct 20, 2026

    @chappie-daemon
    CollaboratorAuthor

    State after #913, measured, so this does not have to be re-derived.

    Done there: sqlx now enables tls-rustls-ring-webpki, and a committed guard exists in the parity target — a_tls_connection_is_attempted_rather_than_refused_by_this_build. It opens sslmode=require; if the server offers TLS it runs SELECT 1, and if it does not it requires the server to refuse, never this build. With tls-none restored the test fails with TLS upgrade required by connect options but SQLx was built without TLS support enabled, so the assertion bites.

    Measured at runtime, not committed: against postgres:16-alpine with ssl=on and a self-signed pair, the merged build completed the handshake at sslmode=require and SELECT 1 returned 1; the same build against a plaintext server took the refusal branch.

    What is left, and why it is not a five-line change: a parity leg in scripts/storage-parity.sh needs the TLS-enabled Postgres inside the script's own lifecycle. That container runs as uid 70, so a host-generated 600 key is unreadable to it — the leg needs the key generated into a path the postgres user owns, or --user/PGDATA rearranged, and that differs per host. It has to be run end to end on the host that will use it, not added blind.

  2. chappie-daemon commented on Oct 20, 2026

    @chappie-daemon
    CollaboratorAuthor

    Closed by #915, merged as 471d439.

    The leg connects with sslmode=require, confirms encryption server-side rather than from the connection string (SELECT ssl FROM pg_stat_ssl WHERE pid = pg_backend_pid()), and replays the full 118-op capture over TLS, matching both the capture and the plaintext run. The negative control pins the layer: with tls-none restored, the run dies at the guard with "TLS upgrade required by connect options but SQLx was built without TLS support enabled", before the leg is reached.

    The uid-70 blocker recorded when this issue was filed is gone without a host step: the Postgres container runs as the invoking uid with its own PGDATA on a bind, so the 0600 key is owned by the user the server runs under. chmod 644 was rejected on purpose — PostgreSQL refuses a group-readable key and exits.

    Verified end to end on the merged head, not only on the PR: storage-parity exit 0, all four steps, 118 scenarios.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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