Skip to content

Commit ce43a62

Browse files
committed
fix(cluster): guard rustls crypto provider install with Once
rustls 0.23 panics if a TLS config is built before a process-level CryptoProvider has been registered. Production code initialised the provider through the bootstrap listener, but callers that touch TLS before bootstrap (e.g. transport-only integration tests) could hit the panic. Install the ring provider exactly once via a static Once guard in the transport config module so every code path that builds certs or TLS configs is covered regardless of call order.
1 parent cfe63c6 commit ce43a62

1 file changed

Lines changed: 24 additions & 0 deletions

File tree

nodedb-cluster/src/transport/config.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
11
//! QUIC and TLS configuration for Raft RPCs.
22
33
use std::sync::Arc;
4+
use std::sync::Once;
45
use std::time::Duration;
56

67
use nodedb_types::config::tuning::ClusterTransportTuning;
78

89
use crate::error::{ClusterError, Result};
910

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+
1029
/// ALPN protocol identifier for NodeDB Raft RPCs.
1130
pub const ALPN_NODEDB_RAFT: &[u8] = b"nodedb-raft/1";
1231

@@ -53,6 +72,7 @@ pub fn raft_transport_config(tuning: &ClusterTransportTuning) -> quinn::Transpor
5372
pub(crate) fn make_raft_server_config(
5473
tuning: &ClusterTransportTuning,
5574
) -> Result<quinn::ServerConfig> {
75+
ensure_rustls_crypto_provider();
5676
let (cert, key) = nexar::transport::tls::generate_self_signed_cert().map_err(|e| {
5777
ClusterError::Transport {
5878
detail: format!("generate cert: {e}"),
@@ -93,6 +113,7 @@ pub(crate) fn make_raft_server_config(
93113
pub(crate) fn make_raft_client_config(
94114
tuning: &ClusterTransportTuning,
95115
) -> Result<quinn::ClientConfig> {
116+
ensure_rustls_crypto_provider();
96117
let provider = rustls::crypto::ring::default_provider();
97118
let mut tls_config = rustls::ClientConfig::builder_with_provider(Arc::new(provider))
98119
.with_safe_default_protocol_versions()
@@ -149,6 +170,7 @@ pub fn make_raft_server_config_mtls(
149170
creds: &TlsCredentials,
150171
tuning: &ClusterTransportTuning,
151172
) -> Result<quinn::ServerConfig> {
173+
ensure_rustls_crypto_provider();
152174
let mut root_store = rustls::RootCertStore::empty();
153175
root_store
154176
.add(creds.ca_cert.clone())
@@ -211,6 +233,7 @@ pub fn make_raft_client_config_mtls(
211233
creds: &TlsCredentials,
212234
tuning: &ClusterTransportTuning,
213235
) -> Result<quinn::ClientConfig> {
236+
ensure_rustls_crypto_provider();
214237
let mut root_store = rustls::RootCertStore::empty();
215238
root_store
216239
.add(creds.ca_cert.clone())
@@ -270,6 +293,7 @@ pub fn generate_node_credentials(
270293
pub fn generate_node_credentials_multi_san(
271294
sans: &[&str],
272295
) -> Result<(nexar::transport::tls::ClusterCa, TlsCredentials)> {
296+
ensure_rustls_crypto_provider();
273297
let ca = nexar::transport::tls::ClusterCa::generate().map_err(|e| ClusterError::Transport {
274298
detail: format!("generate cluster CA: {e}"),
275299
})?;

0 commit comments

Comments
 (0)