How to Configure Custom TLS Verifiers in Iroh Transport Configuration

You configure custom TLS verifiers in Iroh by constructing a rustls::ClientConfig with your specific certificate verification requirements and passing it to component builders via tls_client_config() methods, utilizing the CaTlsConfig helper for advanced root certificate management.

Iroh uses rustls as its underlying TLS implementation, providing full control over certificate verification, cipher suites, and cryptographic providers. To configure custom TLS verifiers in Iroh transport configuration, you construct a rustls::ClientConfig and supply it to the appropriate builder methods in the n0-computer/iroh codebase, or wrap it in CaTlsConfig for simplified root certificate handling.

Understanding Iroh's TLS Architecture

Iroh's transport layer relies on rustls for all TLS connections, including relay clients, DNS-over-HTTPS resolution, and direct peer connections. The codebase provides a CaTlsConfig wrapper in iroh-relay/src/tls.rs that simplifies root certificate management and converts to rustls::ClientConfig instances.

When you need to configure custom TLS verifiers in Iroh, you typically work with two key patterns: directly modifying a rustls::ClientConfig for low-level control, or using CaTlsConfig for convenient root certificate handling. The CaTlsConfig::client_config method in iroh-relay/src/tls.rs generates a configured rustls::ClientConfig using your selected CryptoProvider.

Creating a Custom rustls::ClientConfig

To implement custom certificate verification, you construct a rustls::ClientConfig with a custom verifier. This allows you to specify allowed TLS versions, cipher suites, and validation logic.

use std::sync::Arc;
use rustls::{
    client::ClientConfig,
    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 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();

This configuration restricts connections to TLS 1.3 and uses only the specified cipher suite. You can further customize the WebPkiServerVerifier or implement your own ServerCertVerifier trait for completely custom validation logic.

Using CaTlsConfig for Root Certificate Management

The CaTlsConfig struct provides a higher-level interface for managing trusted root certificates. Located in iroh-relay/src/tls.rs, this helper allows you to add custom roots, use the operating system certificate store, or disable verification for testing.

use iroh_relay::tls::CaTlsConfig;
use rustls::RootCertStore;
use std::fs::File;

// Load custom root certificates from a PEM file
let mut file = File::open("custom_root.pem")?;
let mut root_store = RootCertStore::empty();
root_store.add_parsable_certificates(
    rustls::internal::pemfile::certs(&mut file)?
        .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
let client_cfg = ca_cfg.client_config(iroh_relay::tls::default_provider())?;

The CaTlsConfig::client_config method handles the conversion to rustls::ClientConfig, ensuring your custom roots are properly integrated with the selected cryptographic provider.

Applying Custom TLS Configurations to Iroh Components

Once you have configured your TLS verifier, you apply it to specific Iroh components through their respective builders.

Configuring the Relay Client

For relay connections, use ClientBuilder::tls_client_config in iroh-relay/src/client.rs:

use iroh_relay::client::ClientBuilder;

let client = ClientBuilder::new(
    "https://relay.example.com".parse()?,
    secret_key,
    dns_resolver,
)
.tls_client_config(rustls_cfg)  // Apply custom TLS config
.build()
.await?;

The tls_client_config method stores your configuration in the builder, which MaybeTlsStreamBuilder::connect in iroh-relay/src/client/tls.rs later uses to create the tokio_rustls::TlsConnector.

Configuring the Iroh Endpoint

For the high-level Iroh endpoint, re-exported via iroh/src/tls.rs:

use iroh::Endpoint;

let endpoint = Endpoint::builder()
    .tls_client_config(client_cfg)
    .build()
    .await?;

DNS-over-HTTPS Configuration

For DNS resolution over HTTPS, pass the configuration via DnsBuilder::tls_client_config, utilizing the same pattern of supplying a pre-configured rustls::ClientConfig.

Complete Implementation Examples

Example 1: Strict TLS 1.3 with Custom Cipher Suite

This example demonstrates how to configure custom TLS verifiers in Iroh with specific cryptographic constraints:

use std::sync::Arc;
use iroh_relay::client::ClientBuilder;
use rustls::{client::ClientConfig, version::TLS13};

let provider = iroh_relay::tls::default_provider();

let strict_config = 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()?,
    ))
    .with_no_client_auth();

let client = ClientBuilder::new(relay_url, secret_key, dns_resolver)
    .tls_client_config(strict_config)
    .build()
    .await?;

Example 2: Custom Root Certificate Authority

When connecting to infrastructure using private CAs:

use iroh_relay::tls::CaTlsConfig;
use rustls::RootCertStore;
use std::fs;

let pem = fs::read_to_string("internal-ca.pem")?;
let mut root_store = RootCertStore::empty();
root_store.add_parsable_certificates(
    rustls::internal::pemfile::certs(&mut pem.as_bytes())?
        .into_iter()
        .map(|c| c.into()),
);

let ca_config = CaTlsConfig::custom_roots(root_store.roots);
let tls_config = ca_config.client_config(iroh_relay::tls::default_provider())?;

let endpoint = iroh::Endpoint::builder()
    .tls_client_config(tls_config)
    .build()
    .await?;

Example 3: Development Configuration with Insecure Verification

Warning: Only use this for testing.

use iroh::Endpoint;

let endpoint = Endpoint::builder()
    .ca_tls_config(iroh::tls::CaTlsConfig::insecure_skip_verify())
    .build()
    .await?;

Summary

  • Iroh uses rustls for all TLS operations, exposed through ClientConfig and CaTlsConfig types.
  • Configure custom TLS verifiers by constructing a rustls::ClientConfig with with_custom_certificate_verifier and passing it to ClientBuilder::tls_client_config or Endpoint::builder().tls_client_config().
  • Manage root certificates using CaTlsConfig in iroh-relay/src/tls.rs, which provides custom_roots(), system_roots(), and insecure_skip_verify() options.
  • Component-specific application occurs in iroh-relay/src/client.rs for relay connections and iroh/src/client.rs for high-level endpoint configuration.
  • Underlying implementation uses MaybeTlsStreamBuilder::connect in iroh-relay/src/client/tls.rs to apply the configuration during the TLS handshake.

Frequently Asked Questions

Can I use a custom cryptographic provider instead of Ring?

Yes, you can configure any CryptoProvider implementation supported by rustls. Pass your provider to ClientConfig::builder_with_provider() and CaTlsConfig::client_config(). AWS-LC-RS is a common alternative to the default Ring provider.

How do I disable certificate verification for testing?

Use CaTlsConfig::insecure_skip_verify() available in iroh/src/tls.rs (re-exported from iroh-relay). This creates a configuration that accepts any certificate. Never use this in production environments.

Where is the TLS configuration actually used during connection?

The stored configuration is used in iroh-relay/src/client/tls.rs within MaybeTlsStreamBuilder::connect, which clones the ClientConfig and creates a tokio_rustls::TlsConnector to perform the handshake when establishing relay connections.

Can I configure custom TLS settings for DNS-over-HTTPS resolution?

Yes, the DNS builder accepts custom TLS configurations via tls_client_config() methods, following the same pattern as the relay client. The configuration is used when establishing HTTPS connections to DNS resolvers.

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 →