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}