How to Configure TLS Settings for iroh Endpoints Using the tls Module
You configure TLS settings for iroh endpoints by constructing a CaTlsConfig instance, converting it to a rustls::ClientConfig via the client_config() method, and attaching it to endpoint builders such as RelayBuilder.
The iroh crate uses the rustls TLS stack for all encrypted connections outside the iroh network, including communication with relays, PKARR servers, and DNS-over-HTTPS resolvers. The public API for managing these settings resides in the tls module of the iroh-relay crate, which is re-exported by the top-level iroh crate for convenient access.
Core TLS Types in iroh
The tls module exposes two primary configuration types that control how your application handles TLS certificates.
CaTlsConfig
The CaTlsConfig struct defines how a client verifies server certificate chains. Implemented in iroh-relay/src/tls.rs, this type stores a mode enum and an optional list of additional root certificates. It supports five distinct verification modes:
- EmbeddedWebPki: Uses Mozilla's built-in root certificates (
webpki_roots::TLS_SERVER_ROOTS) - System: Delegates verification to the OS certificate store (requires the
platform-verifierfeature) - ExtraRootsOnly: Trusts only explicitly supplied root certificates, ignoring system defaults
- CustomServerCertVerifier: Accepts a closure implementing
rustls::client::ServerCertVerifierfor custom validation logic - InsecureSkipVerify: Disables all certificate verification (restricted to test builds)
TlsConfig
The TlsConfig struct bundles a socket address with either a CaTlsConfig (for clients) or a self-signed CertConfig (for servers). You typically attach this to iroh endpoints using the TlsConfig::new(addr, cert_config) constructor.
Configuring Client TLS Verification
To establish TLS connections from your iroh application, you must first create a CaTlsConfig and convert it to a rustls::ClientConfig.
Using Default Mozilla Roots
The most common configuration uses Mozilla's embedded root store:
use iroh::tls::{CaTlsConfig, default_provider};
let ca_cfg = CaTlsConfig::embedded();
let client_cfg = ca_cfg
.client_config(default_provider())
.expect("TLS client config creation failed");
The default_provider() function returns the global rustls::crypto::CryptoProvider (either ring or aws-lc-rs depending on your feature flags), which is required for the client_config() method.
Trusting the System Certificate Store
On platforms supporting the platform-verifier feature, delegate to the OS certificate store:
let ca_cfg = CaTlsConfig::system();
let client_cfg = ca_cfg.client_config(default_provider()).unwrap();
Adding Custom Root Certificates
For private CA infrastructure, supply DER-encoded certificates directly:
use iroh::tls::CaTlsConfig;
use rustls::pki_types::CertificateDer;
// Load your PEM and convert to DER
let my_root: CertificateDer<'static> = load_and_convert_cert();
let ca_cfg = CaTlsConfig::custom_roots(vec![my_root]);
let client_cfg = ca_cfg.client_config(default_provider()).unwrap();
When system() is unavailable (such as on Android), the EmbeddedWebPki mode automatically loads Mozilla's roots and appends your extra_roots, ensuring your custom certificates remain trusted.
Attaching TLS Config to Endpoints
Once you have a rustls::ClientConfig, attach it to iroh endpoint builders. For relay clients, use RelayBuilder:
use iroh::util::RelayBuilder;
let relay = RelayBuilder::new()
.tls_config(client_cfg)
.build();
The relay client stores this configuration internally and uses it for all underlying QUIC and HTTP transports. The iroh/src/util.rs file demonstrates how this ClientConfig integrates with reqwest::ClientBuilder for HTTP-based connections.
Configuring Server TLS
For server endpoints (such as custom relay servers), create a TlsConfig containing a CertConfig::Manual with a rustls::ServerConfig:
// Example pattern from iroh/tests/patchbay/util.rs
let tls_config = TlsConfig::new(addr, cert_config);
// Store in the Relay struct: relay.tls = Some(tls_config);
The test utilities in iroh/tests/patchbay/util.rs demonstrate this pattern using self_signed_tls_certs_and_config for test scenarios.
Test-Only Verification Bypass
For integration testing, you can disable certificate verification using CaTlsConfig::insecure_skip_verify() or the convenience function make_dangerous_client_config():
use iroh::tls::{CaTlsConfig, make_dangerous_client_config};
// Option 1: Direct configuration
let ca_cfg = CaTlsConfig::insecure_skip_verify();
let client_cfg = ca_cfg.client_config(default_provider()).unwrap();
// Option 2: Convenience helper
let dangerous_cfg = make_dangerous_client_config();
⚠️ Security Warning: These modes are gated behind #[cfg(test)] or the test-utils feature flag. Never use this in production code, as it exposes your application to man-in-the-middle attacks.
Summary
CaTlsConfigcontrols client certificate verification with five modes ranging from Mozilla roots to custom verifiersclient_config()convertsCaTlsConfiginto arustls::ClientConfigusing the crypto provider fromdefault_provider()- Custom roots are supported via
CaTlsConfig::custom_roots()for private CA infrastructure - Endpoint integration occurs through builders like
RelayBuilder::tls_config()which accept the configuredClientConfig - Server TLS uses
TlsConfigwithCertConfig::Manualcontaining arustls::ServerConfig - Test helpers like
make_dangerous_client_config()andinsecure_skip_verify()provide safe testing utilities but are restricted to test builds
Frequently Asked Questions
How do I configure iroh to use my organization's private CA certificates?
Use CaTlsConfig::custom_roots() and pass a vector of CertificateDer<'static> instances containing your DER-encoded root certificates. This method works even when the system verifier is unavailable, as it appends your certificates to the Mozilla root store.
Why does my iroh application fail to connect on Android but works on desktop?
Android typically lacks the platform-verifier feature. Use CaTlsConfig::embedded() combined with custom_roots() for your specific certificates, rather than CaTlsConfig::system(), which relies on OS-specific certificate stores that may not be accessible on mobile platforms.
Where is the TLS configuration actually implemented in the iroh source code?
The implementation resides in iroh-relay/src/tls.rs, which defines CaTlsConfig, default_provider(), and make_dangerous_client_config(). The top-level iroh crate re-exports these symbols from iroh/src/tls/mod.rs for public API access.
Can I use a custom certificate validation callback instead of standard root verification?
Yes. Use CaTlsConfig::custom_server_cert_verifier() and provide a closure that returns a rustls::client::ServerCertVerifier implementation. This allows you to implement pinning, custom revocation checks, or other validation logic while still using the iroh TLS framework.
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 →