How to Configure Custom TLS Settings in Iroh: A Complete Guide to rustls Integration
Iroh leverages the rustls library for all TLS operations and exposes custom configuration through ClientBuilder::tls_client_config and the CaTlsConfig helper, enabling precise control over cryptographic providers, cipher suites, and root certificate verification.
Iroh, the open-source distributed networking toolkit developed by n0-computer, delegates Transport Layer Security (TLS) to the memory-safe rustls crate, providing Rust-native implementations of TLS 1.2 and 1.3. When you need to configure custom TLS settings in Iroh—whether to comply with strict security policies, integrate with private PKI infrastructure, or optimize connection performance—you interact directly with rustls::ClientConfig objects passed through component builders. This guide examines the specific implementation paths in the n0-computer/iroh repository that enable fine-grained TLS customization.
Understanding Iroh's TLS Architecture
Iroh's TLS implementation follows a layered architecture where high-level builders accept raw rustls::ClientConfig instances and propagate them to underlying connection code. According to the source code in iroh-relay/src/tls.rs, the CaTlsConfig struct serves as a wrapper for root-certificate verification logic, converting to a standard rustls::ClientConfig via its client_config method.
The architecture separates concerns between:
- Cryptographic provider selection (Ring vs. AWS-LC-RS)
- Certificate verification (root store management via
CaTlsConfig) - Connection establishment (TLS handshake execution in
MaybeTlsStreamBuilder)
In iroh-relay/src/client/tls.rs, the MaybeTlsStreamBuilder::connect method clones the stored ClientConfig and creates a tokio_rustls::TlsConnector to perform the handshake, ensuring your custom settings apply to every encrypted relay connection.
Creating a Custom rustls::ClientConfig
To begin configuring custom TLS settings, you instantiate a rustls::ClientConfig with your chosen cryptographic provider and security parameters. The default configuration uses the Ring provider, though you can substitute AWS-LC-RS for FIPS compliance or specific hardware acceleration.
The configuration process involves selecting a CryptoProvider, defining allowed TLS versions and cipher suites, and configuring certificate verification behavior. In iroh-relay/src/client.rs, the ClientBuilder::tls_client_config method stores your custom configuration, which later flows into the MaybeTlsStreamBuilder when establishing connections.
Managing Root Certificates with CaTlsConfig
For scenarios requiring custom root certificate stores—such as connecting to private relay servers or adding enterprise CA certificates—Iroh provides the CaTlsConfig utility defined in iroh-relay/src/tls.rs. This helper abstracts root store management while producing standard rustls::ClientConfig instances through its conversion method.
The CaTlsConfig offers several factory methods:
CaTlsConfig::custom_roots– Supplies additional certificates to trust alongside the system storeCaTlsConfig::insecure_skip_verify– Disables verification for testing (available iniroh/src/tls.rs)CaTlsConfig::from_der– Loads certificates from DER-encoded bytes
When you invoke ca_cfg.client_config(provider), the method returns a fully configured rustls::ClientConfig that incorporates your root certificate specifications with the cryptographic provider's implemented algorithms.
Applying Custom TLS to Iroh Components
Once you have constructed a rustls::ClientConfig or CaTlsConfig, you apply it to specific Iroh components through their respective builder methods.
Relay Client Configuration
For the relay client implemented in iroh-relay/src/client.rs, use ClientBuilder::tls_client_config to inject your custom configuration:
use iroh_relay::client::ClientBuilder;
use rustls::ClientConfig;
let client = ClientBuilder::new(
"https://relay.example.com".parse::<iroh_relay::RelayUrl>().unwrap(),
secret_key,
dns_resolver,
)
.tls_client_config(client_cfg)
.build()
.await?;
High-Level Endpoint Configuration
In the top-level iroh crate, EndpointBuilder::tls_client_config accepts your custom configuration. The iroh/src/tls.rs module re-exports CaTlsConfig for convenient access, while iroh/src/client.rs demonstrates how the high-level client forwards TLS settings to the underlying relay implementation.
For DNS-over-HTTPS resolution, the DnsBuilder::tls_client_config method similarly accepts custom configurations, ensuring consistent TLS behavior across all external network calls.
Complete Configuration Examples
Custom Cipher Suites and TLS Versions
This example demonstrates restricting the relay client to TLS 1.3 with specific cipher suites using the Ring provider:
use std::sync::Arc;
use iroh_relay::client::ClientBuilder;
use rustls::{
client::ClientConfig,
crypto::CryptoProvider,
version::TLS13,
};
let provider: Arc<CryptoProvider> = iroh_relay::tls::default_provider();
let 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();
let client = ClientBuilder::new(
"https://my.relay.example".parse::<iroh_relay::RelayUrl>().unwrap(),
SecretKey::generate(),
DnsResolver::new(),
)
.tls_client_config(rustls_cfg)
.build()
.await?;
Loading Custom Root Certificates
To connect to a relay using a private CA, load the root certificate and wrap it in CaTlsConfig as implemented in iroh-relay/src/tls.rs:
use iroh_relay::tls::CaTlsConfig;
use rustls::RootCertStore;
use rustls::internal::pemfile::certs;
use std::fs::File;
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()),
);
let ca_cfg = CaTlsConfig::custom_roots(root_store.roots.clone());
let client_cfg = ca_cfg.client_config(iroh_relay::tls::default_provider())?;
Endpoint with Insecure Verification (Testing Only)
For local development or testing against self-signed certificates, use CaTlsConfig::insecure_skip_verify exposed in iroh/src/tls.rs:
use iroh::{Endpoint, tls::CaTlsConfig};
let endpoint = Endpoint::builder()
.tls_client_config(client_cfg)
.ca_tls_config(CaTlsConfig::insecure_skip_verify())
.build()
.await?;
Summary
- Iroh uses rustls for all TLS operations, exposing configuration through standard
rustls::ClientConfigobjects passed to builder methods. - The
CaTlsConfighelper iniroh-relay/src/tls.rssimplifies root certificate management and converts torustls::ClientConfigvia theclient_configmethod. - Component builders including
ClientBuilder::tls_client_configandEndpointBuilder::tls_client_configaccept custom TLS configurations that propagate toMaybeTlsStreamBuilder::connectiniroh-relay/src/client/tls.rs. - Cryptographic flexibility allows selection between Ring and AWS-LC-RS providers, with control over cipher suites and TLS versions through the
CryptoProviderAPI. - Security customization ranges from adding enterprise root certificates to complete verification bypass for testing scenarios using
CaTlsConfig::insecure_skip_verify.
Frequently Asked Questions
What cryptographic providers can I use when configuring TLS in Iroh?
Iroh supports both the Ring and AWS-LC-RS cryptographic providers for rustls. The Ring provider is used by default and provides excellent performance for most use cases. The AWS-LC-RS provider offers FIPS 140-2 compliance for environments requiring validated cryptography. You pass your chosen provider to ClientConfig::builder_with_provider and to CaTlsConfig::client_config when converting the helper to a client configuration.
How do I disable TLS certificate verification for local testing?
Use CaTlsConfig::insecure_skip_verify() when building your endpoint or client, as re-exported in iroh/src/tls.rs. This creates a configuration that accepts any server certificate, which is useful for testing against self-signed certificates or local development servers. Never use this in production environments, as it completely disables protection against man-in-the-middle attacks.
Can I configure different TLS settings for the relay client and the main endpoint?
Yes. Each component builder maintains its own TLS configuration. You can create distinct rustls::ClientConfig instances—one for the relay client via ClientBuilder::tls_client_config in iroh-relay/src/client.rs and another for the endpoint via EndpointBuilder::tls_client_config. This allows you to enforce stricter cipher requirements for relay connections while using different parameters for direct peer connections.
Where does Iroh apply the custom TLS configuration during connection establishment?
According to iroh-relay/src/client/tls.rs, the MaybeTlsStreamBuilder::connect method clones the stored ClientConfig and creates a tokio_rustls::TlsConnector to execute the TLS handshake. This occurs when the relay client establishes WebSocket connections or when the endpoint negotiates secure channels, ensuring your custom certificate verification and cipher preferences are active for all encrypted traffic.
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 →