How to Configure Custom TLS Settings and Certificate Validation in Iroh
Iroh exposes its underlying rustls configuration through the CaTlsConfig helper and builder methods like tls_client_config, allowing you to customize cipher suites, cryptographic providers, and root certificate stores when building relay clients or endpoints.
Iroh uses rustls for all TLS connections, including relay sessions and DNS-over-HTTPS queries. To configure custom TLS settings and certificate validation in iroh, you construct a rustls::ClientConfig (or use the CaTlsConfig wrapper) and inject it into component builders such as ClientBuilder or Endpoint::builder.
Understanding Iroh's TLS Architecture
The TLS stack follows a clear path from configuration to handshake. First, components obtain a rustls::ClientConfig from the caller. This config is optionally wrapped in CaTlsConfig (defined in iroh-relay/src/tls.rs) to handle root certificate verification. Finally, the MaybeTlsStreamBuilder::connect method in iroh-relay/src/client/tls.rs consumes this configuration to create a tokio_rustls::TlsConnector and perform the handshake.
Key source locations:
iroh-relay/src/tls.rs– DefinesCaTlsConfigand itsclient_configmethod for converting torustls::ClientConfig.iroh-relay/src/client.rs– ImplementsClientBuilder::tls_client_configto store custom TLS configurations.iroh-relay/src/client/tls.rs– ContainsMaybeTlsStreamBuilder::connect, which clones the stored config and initiates the TLS session.iroh/src/tls.rs– Re-exportsCaTlsConfigfor the top-levelirohcrate.
Creating a Custom rustls::ClientConfig
You have full control over the cryptographic provider, cipher suites, and protocol versions. The default provider uses Ring, but you can substitute AWS-LC-RS or another compatible provider.
use std::sync::Arc;
use iroh_relay::client::ClientBuilder;
use rustls::{
client::ClientConfig,
crypto::CryptoProvider,
version::TLS13,
};
// Select the cryptographic provider (Ring is the default).
let provider: Arc<CryptoProvider> = iroh_relay::tls::default_provider();
// Build a custom client configuration.
let mut rustls_cfg = ClientConfig::builder_with_provider(provider.clone())
.with_safe_defaults()
.with_custom_certificate_verifier(Arc::new(
rustls::client::WebPkiServerVerifier::builder_with_provider(
rustls::RootCertStore::empty(),
provider.clone(),
)
.with_allowed_versions(&[TLS13])
.with_cipher_suites(&[rustls::cipher_suite::TLS13_AES_256_GCM_SHA384])
.build()
.unwrap(),
))
.with_no_client_auth();
// Apply to a relay client builder.
let client = ClientBuilder::new(
"https://my.relay.example".parse::<iroh_relay::RelayUrl>().unwrap(),
SecretKey::generate(),
DnsResolver::new(),
)
.tls_client_config(rustls_cfg)
.build()
.await?;
Customizing Root Certificate Validation
For scenarios requiring custom root stores or test environments, use CaTlsConfig to manage certificate authority trust. This helper is located in iroh-relay/src/tls.rs and provides methods for custom roots, OS store integration, or disabling verification.
use iroh_relay::tls::CaTlsConfig;
use rustls::RootCertStore;
use rustls::internal::pemfile::certs;
use std::fs::File;
// Load PEM-encoded root certificates.
let mut file = File::open("my_root.pem").unwrap();
let mut root_store = RootCertStore::empty();
root_store.add_parsable_certificates(
certs(&mut file).unwrap().into_iter().map(|c| c.into()),
);
// Create a CaTlsConfig with custom roots.
let ca_cfg = CaTlsConfig::custom_roots(root_store.roots.clone());
// Convert to rustls::ClientConfig using the default provider.
let client_cfg = ca_cfg.client_config(iroh_relay::tls::default_provider())?;
For testing only, you can disable certificate verification entirely:
let insecure_cfg = CaTlsConfig::insecure_skip_verify();
Applying TLS Configurations to Iroh Components
Different components accept TLS configurations through specific builder methods:
ClientBuilder::tls_client_config– Configures the relay client.EndpointBuilder::tls_client_config– Configures the high-level Iroh endpoint.DnsBuilder::tls_client_config– Configures DNS-over-HTTPS resolution.
When the connection is established, MaybeTlsStreamBuilder::connect (in iroh-relay/src/client/tls.rs) clones the supplied ClientConfig and creates the tokio_rustls::TlsConnector to perform the handshake.
use iroh::{Endpoint, tls::CaTlsConfig};
let endpoint = Endpoint::builder()
.tls_client_config(client_cfg)
.ca_tls_config(CaTlsConfig::insecure_skip_verify()) // Testing only
.build()
.await?;
Summary
- Iroh uses rustls for all TLS operations, exposing configuration through
rustls::ClientConfig. - The
CaTlsConfigstruct iniroh-relay/src/tls.rssimplifies root certificate management and conversion to client configs. - Component builders including
ClientBuilderandEndpoint::builderaccept custom TLS settings via thetls_client_configmethod. - The actual TLS handshake occurs in
MaybeTlsStreamBuilder::connect(iroh-relay/src/client/tls.rs), which uses the provided configuration to initialize the connection. - You can select cryptographic providers (Ring or AWS-LC-RS), restrict cipher suites, and customize root certificate stores.
Frequently Asked Questions
How do I disable certificate verification for local testing?
Use CaTlsConfig::insecure_skip_verify() when building your endpoint or client. This creates a configuration that accepts any server certificate. According to the source in iroh-relay/src/tls.rs, this bypasses all verification checks and should only be used in development environments.
Can I use a cryptographic provider other than Ring?
Yes. Construct an Arc<CryptoProvider> using the aws-lc-rs crate (or another compatible provider) and pass it to CaTlsConfig::client_config() or use it with ClientConfig::builder_with_provider(). The default provider is Ring, but the architecture supports pluggable cryptography.
Where does the actual TLS handshake happen in the code?
The handshake is performed in MaybeTlsStreamBuilder::connect within iroh-relay/src/client/tls.rs. This method clones the stored rustls::ClientConfig, creates a tokio_rustls::TlsConnector, and initiates the TLS session when establishing relay connections.
How do I add custom root certificates for a private CA?
Load your PEM-encoded certificates into a rustls::RootCertStore, then pass the roots to CaTlsConfig::custom_roots(). Call the client_config method on the resulting CaTlsConfig to generate a rustls::ClientConfig that trusts only your specified certificates, then supply this config to your component builder.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →