Skip to content

fix: fold TLS params into pool cache key - #37

Merged
aesslinger merged 1 commit into
mainfrom
fix/pool-key-tls-params
Aug 19, 2026
Merged

fix: fold TLS params into pool cache key#37
aesslinger merged 1 commit into
mainfrom
fix/pool-key-tls-params

Conversation

@aesslinger

Copy link
Copy Markdown
Collaborator

Summary

connection_key (src/client.rs) matched only on host:port:database:user:startup_script, ignoring ssl_mode/ssl_ca/ssl_cert/ssl_key entirely. So two connections to the same target differing only in TLS configuration — require vs. verify-full, or two different client certs — could incorrectly share a cached pool and its already-negotiated TLS setup.

Fixes #36.

Not a builtin-parity gap — a plugin-specific miss

Surfaced while reviewing #34/#35's client-cert fix, which touches the same TLS config path but not the cache key.

This one is not inherited from the builtin driver. The builtin's build_connection_key already keyed on ssl_mode/ssl_ca starting tabularis#278 (2026-06-04). This plugin's client.rs wasn't staged until 2026-08-11 — over two months later — so the gap was introduced during extraction, not something that arrived upstream afterward. Full timeline in #36.

Changes

  • Folded ssl_mode, ssl_ca, ssl_cert, ssl_key into connection_key's format string, matching the builtin's TLS-param keying shape.
  • Updated connection_key's doc comment and client.rs's module-level "Pool caching" doc comment.
  • Updated CLAUDE.md's description of the pool cache key so it doesn't go stale.
  • Added a CHANGELOG entry.

Relationship to #35

Independent of #35 (client cert not honored in the TLS connector) — different function (connection_key vs. build_tls_connector), no functional overlap. Either can merge first; both are intended for the same upcoming beta tag.

TDD approach

Added connection_key_differs_by_ssl_mode/_ssl_ca/_ssl_cert/_ssl_key_alone to src/client_tests.rs first (mirroring the builtin's postgres_pool_key_changes_when_ssl_*_changes test names), confirmed all four failed against the old connection_key (same key regardless of TLS params), then implemented the fix until green.

Test plan

  • cargo test --lib --bins — 92/92 pass (88 previous + 4 new)
  • cargo clippy --all-targets -- -D warnings — clean
  • cargo fmt --all -- --check — clean
  • npx markdownlint CHANGELOG.md CLAUDE.md — clean

@aesslinger aesslinger added the prerelease:beta Version suggestion targets a beta prerelease label Aug 19, 2026
@github-actions

Copy link
Copy Markdown

Version suggestion

Based on this PR's title (fix) and the prerelease:beta label:

Current 1.0.0-beta.7
Suggested next tag v1.0.0-beta.8

This is informational only — no tag or release is created automatically yet.

connection_key matched only on host:port:database:user:startup_script,
ignoring ssl_mode/ssl_ca/ssl_cert/ssl_key entirely. So two connections
to the same target differing only in TLS configuration -- require vs.
verify-full, or two different client certs -- could incorrectly share
a cached pool and its already-negotiated TLS setup.

This wasn't inherited from the builtin driver: the builtin's
build_connection_key already keyed on ssl_mode/ssl_ca before this
plugin's client.rs was even staged (2026-08-11, over two months after
tabularis#278 added that keying upstream on 2026-06-04). So this was a
parity miss during extraction, not a later upstream change -- surfaced
while reviewing #34/#35's client-cert fix, which touches the same TLS
config but not the cache key.

Folded all four TLS params into connection_key, matching the builtin's
TLS-param keying. Updated CLAUDE.md's description of the pool cache
key to match.

Fixes #36.
@aesslinger
aesslinger force-pushed the fix/pool-key-tls-params branch from 3a35631 to 560cdbb Compare August 19, 2026 13:27
@aesslinger
aesslinger merged commit 392e6d9 into main Aug 19, 2026
7 checks passed
@aesslinger
aesslinger deleted the fix/pool-key-tls-params branch August 19, 2026 13:30
aesslinger added a commit that referenced this pull request Aug 19, 2026
build_tls_connector wrapped rustls::client::WebPkiServerVerifier for
verify-ca, whose verify_server_cert unconditionally checks the
hostname via verify_server_name with no way to opt out -- making
verify-ca behave identically to verify-full. verify-ca is supposed to
validate the certificate chain but skip hostname verification --
that's the entire distinction from verify-full, matching libpq
sslmode=verify-ca semantics.

Ported VerifyCaCertVerifier from the builtin driver's
src-tauri/src/pool_manager.rs, which deliberately avoids
WebPkiServerVerifier for this exact reason and instead calls
rustls::client::verify_server_cert_signed_by_trust_anchor directly
(never reads server_name at all).

Proved the bug and the fix with a chain-valid cert whose hostname
deliberately doesn't match the connection target -- rejected before
this fix, accepted after -- while confirming a CA-untrusted cert is
still correctly rejected, and verify-full still correctly rejects the
same hostname mismatch.

Rebasing onto main (after #35 and #37 merged) surfaced a real merge
hazard: naively combining #38's verify-ca branch with #35's client-auth
logic would have dropped client_auth entirely on the verify-ca path,
always calling .with_no_client_auth() regardless of ssl_cert/ssl_key.
Fixed during this rebase so verify-ca now attaches a configured client
cert the same way verify-full and the default branch already do. Added
build_tls_connector_verify_ca_attaches_a_configured_client_cert,
checking ClientConfig::client_auth_cert_resolver.has_certs() (not just
"builds successfully", which the buggy version also did) -- confirmed
it fails against the naive merge and passes against this fix.

Fixes #38.
aesslinger added a commit that referenced this pull request Aug 19, 2026
build_tls_connector wrapped rustls::client::WebPkiServerVerifier for
verify-ca, whose verify_server_cert unconditionally checks the
hostname via verify_server_name with no way to opt out -- making
verify-ca behave identically to verify-full. verify-ca is supposed to
validate the certificate chain but skip hostname verification --
that's the entire distinction from verify-full, matching libpq
sslmode=verify-ca semantics.

Ported VerifyCaCertVerifier from the builtin driver's
src-tauri/src/pool_manager.rs, which deliberately avoids
WebPkiServerVerifier for this exact reason and instead calls
rustls::client::verify_server_cert_signed_by_trust_anchor directly
(never reads server_name at all).

Proved the bug and the fix with a chain-valid cert whose hostname
deliberately doesn't match the connection target -- rejected before
this fix, accepted after -- while confirming a CA-untrusted cert is
still correctly rejected, and verify-full still correctly rejects the
same hostname mismatch.

Rebasing onto main (after #35 and #37 merged) surfaced a real merge
hazard: naively combining #38's verify-ca branch with #35's client-auth
logic would have dropped client_auth entirely on the verify-ca path,
always calling .with_no_client_auth() regardless of ssl_cert/ssl_key.
Fixed during this rebase so verify-ca now attaches a configured client
cert the same way verify-full and the default branch already do. Added
build_tls_connector_verify_ca_attaches_a_configured_client_cert,
checking ClientConfig::client_auth_cert_resolver.has_certs() (not just
"builds successfully", which the buggy version also did) -- confirmed
it fails against the naive merge and passes against this fix.

Fixes #38.
aesslinger added a commit that referenced this pull request Aug 19, 2026
build_tls_connector wrapped rustls::client::WebPkiServerVerifier for
verify-ca, whose verify_server_cert unconditionally checks the
hostname via verify_server_name with no way to opt out -- making
verify-ca behave identically to verify-full. verify-ca is supposed to
validate the certificate chain but skip hostname verification --
that's the entire distinction from verify-full, matching libpq
sslmode=verify-ca semantics.

Ported VerifyCaCertVerifier from the builtin driver's
src-tauri/src/pool_manager.rs, which deliberately avoids
WebPkiServerVerifier for this exact reason and instead calls
rustls::client::verify_server_cert_signed_by_trust_anchor directly
(never reads server_name at all).

Proved the bug and the fix with a chain-valid cert whose hostname
deliberately doesn't match the connection target -- rejected before
this fix, accepted after -- while confirming a CA-untrusted cert is
still correctly rejected, and verify-full still correctly rejects the
same hostname mismatch.

Rebasing onto main (after #35 and #37 merged) surfaced a real merge
hazard: naively combining #38's verify-ca branch with #35's client-auth
logic would have dropped client_auth entirely on the verify-ca path,
always calling .with_no_client_auth() regardless of ssl_cert/ssl_key.
Fixed during this rebase so verify-ca now attaches a configured client
cert the same way verify-full and the default branch already do. Added
build_tls_connector_verify_ca_attaches_a_configured_client_cert,
checking ClientConfig::client_auth_cert_resolver.has_certs() (not just
"builds successfully", which the buggy version also did) -- confirmed
it fails against the naive merge and passes against this fix.

Fixes #38.
aesslinger added a commit that referenced this pull request Aug 19, 2026
Continuation of the beta line after PRs #45/#47/#48 (ssl_mode=require/
verify-ca/verify-full silently allowing plaintext, ssl_mode=require
validating against the platform trust store instead of skipping
validation, and verify-ca silently falling back to platform trust
instead of requiring an explicit CA file).

Also corrected three README claims that had gone stale across this
session's TLS work: the ssl_mode value list was missing allow/prefer
in two places, ssl_ca's description didn't note it's now required
(not just optional) for verify-ca, and the pool-caching description
still said host:port:database:user, missing startup_script and every
TLS param folded in by #37.

Verified: .tabularium re-validated clean against the live registry
schema; cargo build/test (114/114)/clippy/fmt all pass; markdownlint
clean; manual live-TLS smoke tests against real non-SSL and
self-signed-cert SSL-enabled PostgreSQL instances covering every
ssl_mode value and error path all behave as documented.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

prerelease:beta Version suggestion targets a beta prerelease

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pool cache key ignores all TLS params (ssl_mode/ssl_ca/ssl_cert/ssl_key)

1 participant