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 store
  • CaTlsConfig::insecure_skip_verify – Disables verification for testing (available in iroh/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::ClientConfig objects passed to builder methods.
  • The CaTlsConfig helper in iroh-relay/src/tls.rs simplifies root certificate management and converts to rustls::ClientConfig via the client_config method.
  • Component builders including ClientBuilder::tls_client_config and EndpointBuilder::tls_client_config accept custom TLS configurations that propagate to MaybeTlsStreamBuilder::connect in iroh-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 CryptoProvider API.
  • 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:

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 →