Skip to content

Commit 92ea689

Browse files
committed
feat(iroh-relay)!: Allow to customize rustls config
1 parent 295a715 commit 92ea689

1 file changed

Lines changed: 71 additions & 25 deletions

File tree

iroh-relay/src/tls.rs

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub struct CaRootsConfig {
2424
extra_roots: Vec<CertificateDer<'static>>,
2525
}
2626

27-
#[derive(Debug, Clone)]
27+
#[derive(derive_more::Debug, Clone)]
2828
enum Mode {
2929
/// Use a compiled-in copy of the root certificates trusted by Mozilla.
3030
///
@@ -42,6 +42,12 @@ enum Mode {
4242
/// May only be used in tests or local development setups.
4343
#[cfg(any(test, feature = "test-utils"))]
4444
InsecureSkipVerify,
45+
/// Use a callback to create a [`ClientConfig`] used in all TLS requests.
46+
Custom {
47+
#[debug("Arc<dyn Fn>")]
48+
make_client_config:
49+
Arc<dyn 'static + Send + Sync + Fn(Arc<CryptoProvider>) -> io::Result<ClientConfig>>,
50+
},
4551
}
4652

4753
impl Default for CaRootsConfig {
@@ -90,13 +96,52 @@ impl CaRootsConfig {
9096
}
9197

9298
/// Only trust the explicitly set root certificates.
93-
pub fn custom(roots: impl IntoIterator<Item = CertificateDer<'static>>) -> Self {
99+
pub fn custom_roots(roots: impl IntoIterator<Item = CertificateDer<'static>>) -> Self {
94100
Self {
95101
mode: Mode::ExtraRootsOnly,
96102
extra_roots: roots.into_iter().collect(),
97103
}
98104
}
99105

106+
/// Creates a [`CaRootsConfig`] that uses a callback function to create a [`ClientConfig`].
107+
///
108+
/// This is an advanced feature and you should only use this if none of the other constructor
109+
/// functions cover your needs. Wrongly implementing the callback may lead to insecure connections
110+
/// being accepted.
111+
///
112+
/// The [`CryptoProvider`] passed to the callback should be used for all cryptographic operations.
113+
///
114+
/// ## Example
115+
///
116+
/// This example implements the behavior of [`Self::embedded`] via [`Self::custom`].
117+
///
118+
/// ```rust
119+
/// # use std::sync::Arc;
120+
/// # use iroh_relay::tls::CaRootsConfig;
121+
/// let root_store = Arc::new(rustls::RootCertStore {
122+
/// roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
123+
/// });
124+
/// let ca_roots_config =
125+
/// CaRootsConfig::custom_client_config_builder(Arc::new(move |crypto_provider| {
126+
/// let client_config = rustls::ClientConfig::builder_with_provider(crypto_provider)
127+
/// .with_safe_default_protocol_versions()
128+
/// .expect("protocols supported by crypto provider")
129+
/// .with_root_certificates(root_store.clone())
130+
/// .with_no_client_auth();
131+
/// Ok(client_config)
132+
/// }));
133+
/// ```
134+
pub fn custom_client_config_builder(
135+
make_client_config: Arc<
136+
dyn 'static + Send + Sync + Fn(Arc<CryptoProvider>) -> io::Result<ClientConfig>,
137+
>,
138+
) -> Self {
139+
Self {
140+
mode: Mode::Custom { make_client_config },
141+
extra_roots: Vec::new(),
142+
}
143+
}
144+
100145
/// Add additional root certificates to the list of trusted certificates.
101146
pub fn with_extra_roots(
102147
mut self,
@@ -106,49 +151,50 @@ impl CaRootsConfig {
106151
self
107152
}
108153

109-
/// Builds a [`ServerCertVerifier`] from this config.
110-
pub fn server_cert_verifier(
111-
&self,
112-
crypto_provider: Arc<CryptoProvider>,
113-
) -> io::Result<Arc<dyn ServerCertVerifier>> {
114-
Ok(match self.mode {
154+
/// Build a [`ClientConfig`] from this config.
155+
pub fn client_config(&self, crypto_provider: Arc<CryptoProvider>) -> io::Result<ClientConfig> {
156+
let verifier: Arc<dyn ServerCertVerifier> = match self.mode {
115157
#[cfg(feature = "platform-verifier")]
116158
Mode::System => {
117159
#[cfg(not(target_os = "android"))]
118160
let verifier = rustls_platform_verifier::Verifier::new_with_extra_roots(
119161
self.extra_roots.clone(),
120-
crypto_provider,
162+
crypto_provider.clone(),
121163
);
122164
#[cfg(target_os = "android")]
123-
let verifier = rustls_platform_verifier::Verifier::new(crypto_provider);
165+
let verifier = rustls_platform_verifier::Verifier::new(crypto_provider.clone());
124166
Arc::new(verifier.map_err(io::Error::other)?)
125167
}
126168
Mode::EmbeddedWebPki => {
127169
let mut root_store = rustls::RootCertStore {
128170
roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
129171
};
130172
root_store.add_parsable_certificates(self.extra_roots.clone());
131-
WebPkiServerVerifier::builder_with_provider(Arc::new(root_store), crypto_provider)
132-
.build()
133-
.map_err(io::Error::other)?
173+
WebPkiServerVerifier::builder_with_provider(
174+
Arc::new(root_store),
175+
crypto_provider.clone(),
176+
)
177+
.build()
178+
.map_err(io::Error::other)?
134179
}
135180
Mode::ExtraRootsOnly => {
136181
let mut root_store = rustls::RootCertStore { roots: vec![] };
137182
root_store.add_parsable_certificates(self.extra_roots.clone());
138-
WebPkiServerVerifier::builder_with_provider(Arc::new(root_store), crypto_provider)
139-
.build()
140-
.map_err(io::Error::other)?
183+
WebPkiServerVerifier::builder_with_provider(
184+
Arc::new(root_store),
185+
crypto_provider.clone(),
186+
)
187+
.build()
188+
.map_err(io::Error::other)?
141189
}
142190
#[cfg(any(test, feature = "test-utils"))]
143-
Mode::InsecureSkipVerify => {
144-
Arc::new(no_cert_verifier::NoCertVerifier { crypto_provider })
145-
}
146-
})
147-
}
148-
149-
/// Build a [`ClientConfig`] from this config.
150-
pub fn client_config(&self, crypto_provider: Arc<CryptoProvider>) -> io::Result<ClientConfig> {
151-
let verifier = self.server_cert_verifier(crypto_provider.clone())?;
191+
Mode::InsecureSkipVerify => Arc::new(no_cert_verifier::NoCertVerifier {
192+
crypto_provider: crypto_provider.clone(),
193+
}),
194+
Mode::Custom {
195+
ref make_client_config,
196+
} => return make_client_config(crypto_provider),
197+
};
152198
let config = ClientConfig::builder_with_provider(crypto_provider)
153199
.with_safe_default_protocol_versions()
154200
.expect("protocols supported by ring")

0 commit comments

Comments
 (0)