Configure TLS Settings for iroh Endpoints: A Complete Guide to Certificate Verification
To configure TLS settings for iroh endpoints, use the CaTlsConfig type from the iroh::tls module to define certificate verification behavior, then convert it to a rustls::ClientConfig using the client_config() method and pass it to endpoint builders such as RelayBuilder.
The iroh crate leverages the rustls TLS stack for all encrypted connections outside the iroh network, including relay communication, PKARR servers, and DNS-over-HTTPS resolvers. All public TLS APIs are centralized in the tls module of the iroh-relay crate and re-exported at the top level of the iroh crate for convenient access.
Understanding the iroh TLS Architecture
Iroh delegates all cryptographic operations to rustls, using Mozilla's root certificate store via webpki-roots as the default trust anchor. The implementation in iroh-relay/src/tls.rs defines a CaTlsConfig struct that stores a mode enum and an optional list of extra root certificates. This architecture allows you to configure certificate verification for relay clients, DNS resolvers, and other external endpoints without modifying low-level TLS code.
Core TLS Configuration Types
CaTlsConfig
CaTlsConfig is the primary configuration struct that controls how clients verify server certificate chains. It supports five verification modes through internal enum variants:
- EmbeddedWebPki – Uses the built-in Mozilla root store (
webpki_roots::TLS_SERVER_ROOTS) - System – Delegates to the OS certificate store when the
platform-verifierfeature is enabled - ExtraRootsOnly – Trusts only explicitly supplied root certificates
- CustomServerCertVerifier – Accepts a closure that creates a
rustls::client::ServerCertVerifier - InsecureSkipVerify – Disables verification entirely for test builds only
TlsConfig
TlsConfig bundles a socket address with certificate configuration. For clients, this wraps a CaTlsConfig; for servers, it may contain a CertConfig::Manual with a rustls::ServerConfig. In iroh-relay/src/tls.rs, the TlsConfig struct is attached to Relay instances via relay.tls = Some(tls).
Default Crypto Provider
The default_provider() function returns the global rustls::crypto::CryptoProvider, selecting either the ring or aws-lc-rs implementation based on feature flags. This provider is required when building client configurations.
Configuring Client TLS Verification
Using Embedded Mozilla Roots
The default configuration trusts Mozilla's root certificates and works for most production scenarios:
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");
Trusting System Certificates
When the platform-verifier feature is available, delegate to the OS certificate store:
let ca_cfg = CaTlsConfig::system();
let client_cfg = ca_cfg.client_config(default_provider()).unwrap();
Supplying Custom Root Certificates
For private CA infrastructure, provide 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_my_root_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 always loads the Mozilla set and appends any extra_roots you supply.
Applying TLS Config to Endpoints
Pass the rustls::ClientConfig to endpoint builders when constructing relay clients:
use iroh::{tls::{CaTlsConfig, default_provider}, util::RelayBuilder};
let ca_cfg = CaTlsConfig::embedded();
let client_cfg = ca_cfg.client_config(default_provider()).unwrap();
let relay = RelayBuilder::new()
.tls_config(client_cfg)
.build();
The DNS-over-HTTPS resolver in iroh-dns-server/src/http/tls.rs uses the same pattern, building a rustls::ClientConfig via CaTlsConfig methods.
Server TLS Configuration
For server endpoints (such as relay servers), create a TlsConfig containing a CertConfig::Manual with a rustls::ServerConfig. The test utilities in iroh/tests/patchbay/util.rs demonstrate this pattern using self_signed_tls_certs_and_config() to generate certificates for testing.
Testing and Development Modes
For integration tests, use the dangerous configuration that skips verification:
use iroh::tls::CaTlsConfig;
let ca_cfg = CaTlsConfig::insecure_skip_verify();
let client_cfg = ca_cfg.client_config(default_provider()).unwrap();
⚠️ Critical: This mode is gated behind #[cfg(test)] or the test-utils feature flag. The make_dangerous_client_config() helper in iroh/src/test_utils.rs provides a convenient wrapper, but this must never ship in production code.
Summary
- CaTlsConfig controls certificate verification via five modes: embedded Mozilla roots, system trust, custom roots, custom verifiers, or test-only insecure mode.
- Source files:
iroh-relay/src/tls.rscontains the implementation;iroh/src/tls/mod.rsprovides public re-exports. - Client configuration: Call
client_config()withdefault_provider()to generate arustls::ClientConfig. - Endpoint integration: Pass the configuration to
RelayBuilder::tls_config()or similar endpoint constructors. - Custom roots: Use
CaTlsConfig::custom_roots()with DER-encoded certificates for private CA infrastructure. - Testing: Use
insecure_skip_verify()only behind test gates, not in production.
Frequently Asked Questions
How do I configure iroh to trust a corporate self-signed certificate?
Use CaTlsConfig::custom_roots() and pass a vector of CertificateDer objects representing your corporate root CA. Convert your PEM certificates to DER format, then build the client config with ca_cfg.client_config(default_provider()).
What is the difference between CaTlsConfig::embedded() and CaTlsConfig::system()?
embedded() uses Mozilla's root certificate bundle compiled into the binary via webpki-roots, while system() delegates verification to the operating system's certificate store when the platform-verifier feature is enabled. System verification is preferred on desktop platforms but may be unavailable on embedded or Android targets.
Where does iroh store TLS configuration for the relay client?
The Relay struct stores an optional TlsConfig in its tls field. When building a relay client, you provide the rustls::ClientConfig via RelayBuilder::tls_config(), which the relay then uses for all HTTPS connections to the relay server.
Can I use a custom certificate verifier instead of the standard WebPKI?
Yes. Use CaTlsConfig::custom_server_cert_verifier() and supply a closure that creates a rustls::client::ServerCertVerifier. This allows you to implement custom pinning logic, certificate transparency checking, or other validation policies beyond the standard root-of-trust model.
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 →