|
1 | 1 | //! QUIC and TLS configuration for Raft RPCs. |
2 | 2 |
|
3 | 3 | use std::sync::Arc; |
| 4 | +use std::sync::Once; |
4 | 5 | use std::time::Duration; |
5 | 6 |
|
6 | 7 | use nodedb_types::config::tuning::ClusterTransportTuning; |
7 | 8 |
|
8 | 9 | use crate::error::{ClusterError, Result}; |
9 | 10 |
|
| 11 | +/// Install rustls' `ring` CryptoProvider exactly once per process. |
| 12 | +/// |
| 13 | +/// rustls 0.23 refuses to build any `ServerConfig` / `ClientConfig` until a |
| 14 | +/// process-level provider is registered. In production the bootstrap |
| 15 | +/// listener installs it early, but any caller that generates certs or |
| 16 | +/// builds a TLS config before the bootstrap runs (e.g. integration tests |
| 17 | +/// that exercise just the transport layer) hits a panic. Doing the |
| 18 | +/// install here means every code path that ends up touching rustls |
| 19 | +/// through this module is covered. `install_default` returns `Err` on |
| 20 | +/// the second attempt, which we intentionally ignore via the `Once` |
| 21 | +/// guard — it has already succeeded. |
| 22 | +pub(crate) fn ensure_rustls_crypto_provider() { |
| 23 | + static ONCE: Once = Once::new(); |
| 24 | + ONCE.call_once(|| { |
| 25 | + let _ = rustls::crypto::ring::default_provider().install_default(); |
| 26 | + }); |
| 27 | +} |
| 28 | + |
10 | 29 | /// ALPN protocol identifier for NodeDB Raft RPCs. |
11 | 30 | pub const ALPN_NODEDB_RAFT: &[u8] = b"nodedb-raft/1"; |
12 | 31 |
|
@@ -53,6 +72,7 @@ pub fn raft_transport_config(tuning: &ClusterTransportTuning) -> quinn::Transpor |
53 | 72 | pub(crate) fn make_raft_server_config( |
54 | 73 | tuning: &ClusterTransportTuning, |
55 | 74 | ) -> Result<quinn::ServerConfig> { |
| 75 | + ensure_rustls_crypto_provider(); |
56 | 76 | let (cert, key) = nexar::transport::tls::generate_self_signed_cert().map_err(|e| { |
57 | 77 | ClusterError::Transport { |
58 | 78 | detail: format!("generate cert: {e}"), |
@@ -93,6 +113,7 @@ pub(crate) fn make_raft_server_config( |
93 | 113 | pub(crate) fn make_raft_client_config( |
94 | 114 | tuning: &ClusterTransportTuning, |
95 | 115 | ) -> Result<quinn::ClientConfig> { |
| 116 | + ensure_rustls_crypto_provider(); |
96 | 117 | let provider = rustls::crypto::ring::default_provider(); |
97 | 118 | let mut tls_config = rustls::ClientConfig::builder_with_provider(Arc::new(provider)) |
98 | 119 | .with_safe_default_protocol_versions() |
@@ -149,6 +170,7 @@ pub fn make_raft_server_config_mtls( |
149 | 170 | creds: &TlsCredentials, |
150 | 171 | tuning: &ClusterTransportTuning, |
151 | 172 | ) -> Result<quinn::ServerConfig> { |
| 173 | + ensure_rustls_crypto_provider(); |
152 | 174 | let mut root_store = rustls::RootCertStore::empty(); |
153 | 175 | root_store |
154 | 176 | .add(creds.ca_cert.clone()) |
@@ -211,6 +233,7 @@ pub fn make_raft_client_config_mtls( |
211 | 233 | creds: &TlsCredentials, |
212 | 234 | tuning: &ClusterTransportTuning, |
213 | 235 | ) -> Result<quinn::ClientConfig> { |
| 236 | + ensure_rustls_crypto_provider(); |
214 | 237 | let mut root_store = rustls::RootCertStore::empty(); |
215 | 238 | root_store |
216 | 239 | .add(creds.ca_cert.clone()) |
@@ -270,6 +293,7 @@ pub fn generate_node_credentials( |
270 | 293 | pub fn generate_node_credentials_multi_san( |
271 | 294 | sans: &[&str], |
272 | 295 | ) -> Result<(nexar::transport::tls::ClusterCa, TlsCredentials)> { |
| 296 | + ensure_rustls_crypto_provider(); |
273 | 297 | let ca = nexar::transport::tls::ClusterCa::generate().map_err(|e| ClusterError::Transport { |
274 | 298 | detail: format!("generate cluster CA: {e}"), |
275 | 299 | })?; |
|
0 commit comments