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-verifier feature 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.rs contains the implementation; iroh/src/tls/mod.rs provides public re-exports.
  • Client configuration: Call client_config() with default_provider() to generate a rustls::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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →