# How to Configure TLS Settings for iroh Endpoints Using the tls Module

> Easily configure TLS settings for iroh endpoints with the tls module. Learn how to build CaTlsConfig and attach it to endpoint builders for secure connections.

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

---

**You configure TLS settings for iroh endpoints by constructing a `CaTlsConfig` instance, converting it to a `rustls::ClientConfig` via the `client_config()` method, and attaching it to endpoint builders such as `RelayBuilder`.**

The `iroh` crate uses the `rustls` TLS stack for all encrypted connections outside the iroh network, including communication with relays, PKARR servers, and DNS-over-HTTPS resolvers. The public API for managing these settings resides in the **tls** module of the `iroh-relay` crate, which is re-exported by the top-level `iroh` crate for convenient access.

## Core TLS Types in iroh

The `tls` module exposes two primary configuration types that control how your application handles TLS certificates.

### CaTlsConfig

The **`CaTlsConfig`** struct defines how a client verifies server certificate chains. Implemented in [`iroh-relay/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/tls.rs), this type stores a `mode` enum and an optional list of additional root certificates. It supports five distinct verification modes:

- **EmbeddedWebPki**: Uses Mozilla's built-in root certificates (`webpki_roots::TLS_SERVER_ROOTS`)
- **System**: Delegates verification to the OS certificate store (requires the `platform-verifier` feature)
- **ExtraRootsOnly**: Trusts only explicitly supplied root certificates, ignoring system defaults
- **CustomServerCertVerifier**: Accepts a closure implementing `rustls::client::ServerCertVerifier` for custom validation logic
- **InsecureSkipVerify**: Disables all certificate verification (restricted to test builds)

### TlsConfig

The **`TlsConfig`** struct bundles a socket address with either a `CaTlsConfig` (for clients) or a self-signed `CertConfig` (for servers). You typically attach this to iroh endpoints using the `TlsConfig::new(addr, cert_config)` constructor.

## Configuring Client TLS Verification

To establish TLS connections from your iroh application, you must first create a `CaTlsConfig` and convert it to a `rustls::ClientConfig`.

### Using Default Mozilla Roots

The most common configuration uses Mozilla's embedded root store:

```rust
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");

```

The `default_provider()` function returns the global `rustls::crypto::CryptoProvider` (either `ring` or `aws-lc-rs` depending on your feature flags), which is required for the `client_config()` method.

### Trusting the System Certificate Store

On platforms supporting the `platform-verifier` feature, delegate to the OS certificate store:

```rust
let ca_cfg = CaTlsConfig::system();
let client_cfg = ca_cfg.client_config(default_provider()).unwrap();

```

### Adding Custom Root Certificates

For private CA infrastructure, supply DER-encoded certificates directly:

```rust
use iroh::tls::CaTlsConfig;
use rustls::pki_types::CertificateDer;

// Load your PEM and convert to DER
let my_root: CertificateDer<'static> = load_and_convert_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 automatically loads Mozilla's roots and appends your `extra_roots`, ensuring your custom certificates remain trusted.

## Attaching TLS Config to Endpoints

Once you have a `rustls::ClientConfig`, attach it to iroh endpoint builders. For relay clients, use `RelayBuilder`:

```rust
use iroh::util::RelayBuilder;

let relay = RelayBuilder::new()
    .tls_config(client_cfg)
    .build();

```

The relay client stores this configuration internally and uses it for all underlying QUIC and HTTP transports. The [`iroh/src/util.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/util.rs) file demonstrates how this `ClientConfig` integrates with `reqwest::ClientBuilder` for HTTP-based connections.

## Configuring Server TLS

For server endpoints (such as custom relay servers), create a `TlsConfig` containing a `CertConfig::Manual` with a `rustls::ServerConfig`:

```rust
// Example pattern from iroh/tests/patchbay/util.rs
let tls_config = TlsConfig::new(addr, cert_config);
// Store in the Relay struct: relay.tls = Some(tls_config);

```

The test utilities in [`iroh/tests/patchbay/util.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/tests/patchbay/util.rs) demonstrate this pattern using `self_signed_tls_certs_and_config` for test scenarios.

## Test-Only Verification Bypass

For integration testing, you can disable certificate verification using `CaTlsConfig::insecure_skip_verify()` or the convenience function `make_dangerous_client_config()`:

```rust
use iroh::tls::{CaTlsConfig, make_dangerous_client_config};

// Option 1: Direct configuration
let ca_cfg = CaTlsConfig::insecure_skip_verify();
let client_cfg = ca_cfg.client_config(default_provider()).unwrap();

// Option 2: Convenience helper
let dangerous_cfg = make_dangerous_client_config();

```

**⚠️ Security Warning:** These modes are gated behind `#[cfg(test)]` or the `test-utils` feature flag. Never use this in production code, as it exposes your application to man-in-the-middle attacks.

## Summary

- **`CaTlsConfig`** controls client certificate verification with five modes ranging from Mozilla roots to custom verifiers
- **`client_config()`** converts `CaTlsConfig` into a `rustls::ClientConfig` using the crypto provider from `default_provider()`
- **Custom roots** are supported via `CaTlsConfig::custom_roots()` for private CA infrastructure
- **Endpoint integration** occurs through builders like `RelayBuilder::tls_config()` which accept the configured `ClientConfig`
- **Server TLS** uses `TlsConfig` with `CertConfig::Manual` containing a `rustls::ServerConfig`
- **Test helpers** like `make_dangerous_client_config()` and `insecure_skip_verify()` provide safe testing utilities but are restricted to test builds

## Frequently Asked Questions

### How do I configure iroh to use my organization's private CA certificates?

Use `CaTlsConfig::custom_roots()` and pass a vector of `CertificateDer<'static>` instances containing your DER-encoded root certificates. This method works even when the system verifier is unavailable, as it appends your certificates to the Mozilla root store.

### Why does my iroh application fail to connect on Android but works on desktop?

Android typically lacks the `platform-verifier` feature. Use `CaTlsConfig::embedded()` combined with `custom_roots()` for your specific certificates, rather than `CaTlsConfig::system()`, which relies on OS-specific certificate stores that may not be accessible on mobile platforms.

### Where is the TLS configuration actually implemented in the iroh source code?

The implementation resides in [`iroh-relay/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/tls.rs), which defines `CaTlsConfig`, `default_provider()`, and `make_dangerous_client_config()`. The top-level `iroh` crate re-exports these symbols from [`iroh/src/tls/mod.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/mod.rs) for public API access.

### Can I use a custom certificate validation callback instead of standard root verification?

Yes. Use `CaTlsConfig::custom_server_cert_verifier()` and provide a closure that returns a `rustls::client::ServerCertVerifier` implementation. This allows you to implement pinning, custom revocation checks, or other validation logic while still using the iroh TLS framework.