Skip to main content

autopush_common/
tls.rs

1//! TLS / rustls process setup.
2
3/// Install a process-level rustls [`CryptoProvider`] so that rustls 0.23 has an
4/// unambiguous default.
5///
6/// In the bigtable build our dependency graph unifies onto a single `rustls`
7/// 0.23 build that has *both* the `ring` and `aws-lc-rs` provider features
8/// compiled in (`ring` via tonic's `tls-ring` and gcp_auth; `aws-lc-rs` via
9/// reqwest). When more than one provider is present, any library that builds a
10/// rustls config through the default path — e.g. reqwest, on the actix arbiter
11/// threads at startup — panics because rustls can't pick a provider. (tonic
12/// itself doesn't panic: it falls back to `ring`, but it adopts whatever
13/// default we install here.)
14///
15/// We install `aws-lc-rs` because reqwest pulls it into every build, so this
16/// never introduces a second provider into the otherwise single-provider
17/// redis/postgres builds.
18///
19/// Call this once, as early as possible in `main()`, before any TLS use. It is
20/// idempotent: a second call (or a provider installed by another crate) is
21/// ignored.
22///
23/// [`CryptoProvider`]: rustls::crypto::CryptoProvider
24pub fn install_crypto_provider() {
25    if rustls::crypto::aws_lc_rs::default_provider()
26        .install_default()
27        .is_err()
28    {
29        // A provider was already installed for this process; nothing to do.
30    }
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    /// Guards the fix for the ambiguous-`CryptoProvider` startup panic.
38    ///
39    /// In any build that compiles in more than one rustls provider (the
40    /// `bigtable` build pulls `ring` via tonic *and* `aws-lc-rs` via reqwest),
41    /// `rustls::ClientConfig::builder()` resolves `CryptoProvider::get_default()`
42    /// and panics with "Could not automatically determine the process-level
43    /// CryptoProvider" unless a default has been installed first.
44    ///
45    /// This asserts that [`install_crypto_provider`] makes that resolution
46    /// succeed, so the clients that use rustls's default-provider path (reqwest,
47    /// sqlx) no longer panic. tonic doesn't hit this path — it falls back to
48    /// `ring` — but it adopts whatever default we install for the bigtable
49    /// connection.
50    #[test]
51    fn install_resolves_crypto_provider() {
52        install_crypto_provider();
53
54        // Idempotent: a second call must not panic.
55        install_crypto_provider();
56
57        assert!(
58            rustls::crypto::CryptoProvider::get_default().is_some(),
59            "no process-level CryptoProvider after install"
60        );
61
62        // The exact call that panics pre-fix when multiple providers are
63        // compiled in. It must not panic now.
64        let _ = rustls::ClientConfig::builder();
65    }
66}