Skip to main content

prax_postgres/
tls.rs

1//! TLS connector construction for the connection pool, via rustls.
2//!
3//! Enabled by the `tls` cargo feature (on by default). Certificates are
4//! verified against the Mozilla root store ([`webpki_roots`]) — chain and
5//! hostname — for every TLS-capable [`crate::config::SslMode`]. This is
6//! deliberately stricter than libpq, whose `sslmode=require` performs no
7//! certificate verification; there is currently no encrypt-without-verify
8//! mode.
9
10use std::sync::Arc;
11
12use postgres_rustls::MakeTlsConnector;
13use rustls::{ClientConfig, RootCertStore};
14
15/// Build the TLS connector handed to deadpool's `Manager`.
16///
17/// The same connector serves every TLS mode: `Prefer` (tokio-postgres falls
18/// back to plaintext only when the *server* declines TLS), and
19/// `Require`/`VerifyCa`/`VerifyFull` (driver-level `SslMode::Require`, so a
20/// server that refuses TLS fails the connection). Verification is always
21/// webpki chain + hostname — see the module docs.
22///
23/// This is the workspace's shared rustls connector, exposed so downstream
24/// tooling (e.g. `prax-cli`'s introspector) can reuse the same certificate
25/// verification behavior. Available only when the `tls` cargo feature is
26/// enabled (on by default).
27pub fn make_tls_connector() -> MakeTlsConnector {
28    let mut roots = RootCertStore::empty();
29    roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
30
31    // Select aws-lc-rs explicitly: workspace feature unification can enable
32    // both rustls providers (aws-lc-rs via sqlx, ring via other crates), in
33    // which case rustls cannot auto-determine a process-level default.
34    let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
35    let client_config = ClientConfig::builder_with_provider(provider)
36        .with_safe_default_protocol_versions()
37        .expect("safe default protocol versions are available")
38        .with_root_certificates(roots)
39        .with_no_client_auth();
40
41    MakeTlsConnector::new(tokio_rustls::TlsConnector::from(Arc::new(client_config)))
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use tokio_postgres::tls::MakeTlsConnect;
48
49    #[test]
50    fn connector_is_constructible_and_cloneable() {
51        // deadpool requires MakeTlsConnect + Clone + Sync + Send + 'static;
52        // construction also proves the rustls provider and root store load.
53        let connector = make_tls_connector();
54        let mut cloned = connector.clone();
55        // A syntactically invalid domain must fail in make_tls_connect
56        // (DNS name parsing) without any network I/O.
57        assert!(
58            <MakeTlsConnector as MakeTlsConnect<tokio::net::TcpStream>>::make_tls_connect(
59                &mut cloned,
60                "invalid..domain"
61            )
62            .is_err()
63        );
64    }
65}