fix: ssl_mode=require validates cert against platform trust store - #47
Merged
Conversation
build_tls_connector's own doc comment already said require should force TLS "without certificate validation", but needs_cert_validation only matched verify-ca/verify-full -- require fell through to the final with_platform_verifier() fallback, which does validate against the OS trust store, defeating the entire point of require vs. verify-full (the standard require use case is self-signed certs or private CAs the user hasn't configured ssl_ca for). Ported NoCertVerifier from the builtin driver's src-tauri/src/pool_manager.rs::NoCertVerifier -- accepts any certificate unconditionally, including bypassing TLS 1.2/1.3 signature verification, not just chain/hostname checks (more permissive than VerifyCaCertVerifier's chain-only bypass, but that's the builtin's own deliberate choice for this mode, not something to improve on silently). Routed require mode to it, threading client_auth through the same pattern already used by the verify-ca/verify-full branches so mTLS + require still work together. Proved the bug and the fix live against a real self-signed-cert SSL-enabled PostgreSQL instance (separate from the non-SSL fixture used for #43): require failed the TLS handshake before this fix, and now connects successfully. Confirmed no regression in verify-ca/ verify-full (still correctly validate -- existing VerifyCaCertVerifier unit tests unaffected) or require against a non-SSL server (still correctly fails, per #43/#45). Separately found while testing this fix that verify-ca without an explicit ssl_ca file silently falls back to platform-trust validation instead of erroring like the builtin does -- a distinct, smaller discrepancy, filed as #46 and left out of scope here. Fixes #44.
Version suggestionBased on this PR's title (
This is informational only — no tag or release is created automatically yet. |
This was referenced Aug 19, 2026
aesslinger
added a commit
that referenced
this pull request
Aug 19, 2026
verify-ca without an explicit ssl_ca silently fell through to
with_platform_verifier() -- full OS-trust-store validation -- instead
of erroring. The builtin driver's build_postgres_tls_connector
requires an explicit CA file for this mode (platform roots aren't
used, since macOS's strict EKU check rejects them) and errors clearly
otherwise ("verify-ca mode requires an explicit CA file...").
Added the same guard, adapted to this plugin's param-name-based error
convention (references ssl_ca by name, since these come from JSON
params rather than a UI form). verify-full without ssl_ca is
unaffected -- that mode is documented to fall back to platform trust
automatically, which is the actual distinction between the two modes.
Proved the fix live against the same self-signed-cert SSL-enabled
PostgreSQL instance used for #44/#47: verify-ca with no ssl_ca
previously failed with a confusing generic "error performing TLS
handshake"; now returns a clear, actionable error. Confirmed
verify-full and require remain unaffected.
Fixes #46.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
build_tls_connector's own doc comment already saidrequireshould force TLS "without certificate validation," butneeds_cert_validationonly matchedverify-ca/verify-full—requirefell through to the finalwith_platform_verifier()fallback, which does validate against the OS trust store. This defeats the entire point ofrequirevs.verify-full: the standardrequireuse case is self-signed certs or private CAs the user hasn't configuredssl_cafor.Fixes #44.
Discovery context
Found while manually verifying #43's fix against a real SSL-enabled PostgreSQL instance with a self-signed cert. Confirmed via
git show 9986b1ethat this predates every PR from this session — distinct from #43 (different root cause: cert validation logic, not protocol-levelSslModeenforcement), even though both live inbuild_tls_connector.Fix
Ported
NoCertVerifierfrom the builtin driver'ssrc-tauri/src/pool_manager.rs::NoCertVerifier— accepts any certificate unconditionally, including bypassing TLS 1.2/1.3 signature verification (not just chain/hostname checks, more permissive thanVerifyCaCertVerifier's chain-only bypass, but that's the builtin's own deliberate choice for this mode — per CLAUDE.md's parity mandate, ported faithfully rather than "improved on"). Routedrequiremode to it, threadingclient_auththrough the same pattern already used by theverify-ca/verify-fullbranches so mTLS +requirestill work together.Testing — exact local setup and results against a real SSL-enabled server
Reused the same two local Podman instances set up for #43/#45's testing (non-SSL fixture on port 54320, real self-signed-cert TLS-enabled instance on port 55432).
Manual repro — the exact command from #44's own issue body, against the fix:
requireagainst SSL-on (55432, self-signed cert){"error":{...,"message":"...error performing TLS handshake"}}(the bug){"id":1,"jsonrpc":"2.0","result":null}✅requireagainst non-SSL (54320)verify-fullagainst SSL-on, nossl_caverify-caagainst SSL-on, nossl_caverify-ca/verify-fullwith a properly-generated CA certverify_ca_cert_verifier_*) still pass, confirmingVerifyCaCertVerifieritself is untouchedAutomated tests:
no_cert_verifier_accepts_a_cert_with_no_matching_hostname_or_chain— constructsNoCertVerifierdirectly, feeds it the existingFIXTURE_SERVER_CERT_PEMwith a deliberately mismatched hostname (proving it does no checking at all, not even the hostname skipVerifyCaCertVerifierdoes).build_tls_connector_require_builds_successfully_with_no_ssl_ca— sanity check the new branch wires up correctly.Related finding, filed separately
While testing this fix, noticed
verify-cawithout an explicitssl_cafile silently falls back to platform-trust validation instead of erroring like the builtin does ("verify-ca mode requires an explicit CA file..."). Distinct, smaller discrepancy — filed as #46, intentionally out of scope for this PR.Test plan
cargo test --lib --bins— 113/113 pass (111 previous + 2 new)cargo clippy --all-targets -- -D warnings— cleancargo fmt --all -- --check— cleancargo build --release— cleannpx markdownlint CHANGELOG.md— clean