# How to Configure Custom TLS Verifiers in Iroh Transport Configuration

> Configure custom TLS verifiers in Iroh transport with rustls ClientConfig and CaTlsConfig. Secure your connections by implementing specific certificate verification requirements for advanced root certificate management.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-06-18

---

**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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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.

```rust
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`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/tls.rs), this helper allows you to add custom roots, use the operating system certificate store, or disable verification for testing.

```rust
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`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/client.rs):

```rust
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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs):

```rust
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:

```rust
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:

```rust
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.

```rust
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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/client.rs) for relay connections and [`iroh/src/client.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/client.rs) for high-level endpoint configuration.
- **Underlying implementation** uses `MaybeTlsStreamBuilder::connect` in [`iroh-relay/src/client/tls.rs`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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.