Skip to content

fix: build with a TLS backend so sslmode=require works (#6) - #7

Open
pcallewaert wants to merge 1 commit into
nbari:mainfrom
pcallewaert:fix/tls-backend
Open

fix: build with a TLS backend so sslmode=require works (#6)#7
pcallewaert wants to merge 1 commit into
nbari:mainfrom
pcallewaert:fix/tls-backend

Conversation

@pcallewaert

Copy link
Copy Markdown

Fixes #6.

Root cause

Cargo.toml set default-features = false on sqlx and never re-enabled a TLS feature:

sqlx = { version = "0.9", default-features = false, features = ["runtime-tokio", "postgres", "chrono"] }

sqlx 0.9 gates all TLS behind tls-native-tls / tls-rustls*. With none enabled, sqlx-core compiles in a stub that returns Error::Tls on any upgrade attempt — confirmed by Cargo.lock, which contained no rustls, native-tls or openssl entries at all.

Two notes beyond what the issue reported:

  • Not musl-specific. The macOS archives, the .deb/.rpm and cargo install pgmon were equally affected. The musl tarball is just where it was hit.
  • It fails quietly first. sqlx defaults sslmode to Prefer, which downgrades to plaintext rather than erroring, so a hostssl-only server looks like a connection refusal rather than a missing feature.

No connection code needed changing — PgConnectOptions::from_str already parses sslmode/sslrootcert/sslcert/sslkey, build_pool_key already keys the pool on them, and classify_connect_error already handles sqlx::Error::Tls. Only the backend was absent.

Changes

The fix — a [features] table selecting the backend:

feature roots
tls-rustls-ring (default) Mozilla set bundled via webpki-roots
tls-rustls-ring-native-roots host OS trust store

Routed through pgmon features rather than hardcoded on the sqlx line so the version string reports what is actually compiled in instead of a constant that can drift.

rustls+ring over tls-native-tls (the musl targets would need a vendored OpenSSL) and over tls-rustls-aws-lc-rs (needs cmake at build time). Bundled roots keep the static musl releases self-contained; private CAs are supplied per connection with sslrootcert=.

Visible TLS status — a new src/tls.rs reports backend and root store. --version gains a TLS: line and check-config a TLS section, so a binary's capability is answerable without a server:

$ pgmon --version
pgmon 0.7.1 - 6c62ba9ad11674382f33c7fe484ab15431fd897c
TLS: rustls (ring), bundled Mozilla roots (webpki-roots)

Build guardbuild.rs fails the build unless exactly one backend feature is enabled. No test can catch this configuration, so it is pinned there instead.

Cargo.lock — regenerated. Additions only (147 lines, no unrelated version churn), which matters because CI builds with --locked.

Docs — a TLS / SSL README section (sslmode examples, the prefer-downgrades-silently warning, root store trade-off, how to check a binary) plus a CHANGELOG entry.

Verification

cargo clippy --all-targets clean, 153 tests pass, and --no-default-features now fails with the guard's message rather than silently producing another TLS-less binary.

End-to-end against PostgreSQL 16 with ssl=on, both binaries connecting with sslmode=require, measured server-side via pg_stat_ssl:

binary server-side result
released 0.7.1 no connection reached the server; TUI renders an empty dashboard
this branch ssl=true version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384, live TUI

Worth noting from that test: the old binary surfaces no error banner, just zeros — the failure is quieter than the issue suggests.

Built for x86_64-unknown-linux-musl in a container mirroring the release workflow (musl-tools + --locked). One caveat: that container also needed protobuf-compiler for pg_query, which the GitHub runner image provides — a gap in my local harness, not a change in this branch, but worth watching on the first CI run.

The version is left at 0.7.1; users only get this once a new tag is cut.

🤖 Generated with Claude Code

The sqlx dependency set `default-features = false` and never re-enabled a
TLS feature, so sqlx-core compiled in its no-TLS stub and every published
binary returned `Error::Tls` on any upgrade attempt. This affected the
macOS archives, the .deb/.rpm packages and `cargo install pgmon` just as
much as the musl tarball reported in nbari#6.

It also failed quietly: sqlx defaults sslmode to `prefer`, which downgrades
to plaintext instead of erroring, so a server that only accepts TLS looked
like a connection refusal rather than a missing feature.

Select the backend through pgmon features rather than hardcoding it on the
sqlx line, so the version string reports what is actually compiled in:

  tls-rustls-ring (default)     rustls + ring, Mozilla roots bundled
  tls-rustls-ring-native-roots  rustls + ring, host OS trust store

rustls+ring over native-tls because the musl targets would otherwise need
a vendored OpenSSL, and over aws-lc-rs because that needs cmake at build
time. Bundled roots keep the static musl releases self-contained; private
CAs are supplied per connection with sslrootcert=.

Report the backend in `--version` and `check-config` so a binary's TLS
capability is answerable without a server to try it against, and fail the
build in build.rs unless exactly one backend is enabled — no test can
catch this configuration, so it is pinned there instead.

Cargo.lock is regenerated; CI builds with --locked.

Fixes nbari#6

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No TLS upgrade possible in pre-built Linux binary

1 participant