Summary
On Windows, enabling the platform-verifier feature and building an
endpoint with CaTlsConfig::system() makes every default relay fail
TLS with NotValidForName — including relays that verify fine with the
default embedded roots.
The cause is that iroh's default relay hostnames are absolute FQDNs, with
the root label's trailing dot (iroh-1.0.3/src/defaults.rs:27):
pub const NA_EAST_RELAY_HOSTNAME: &str = "use1-1.relay.n0.iroh.link.";
rustls-platform-verifier passes that name to CryptoAPI verbatim, which
compares aps1-1.relay.n0.iroh.link. against the certificate's dot-less
SAN and returns CERT_E_INVALID_NAME. webpki drops the root label during
name matching and never sees a problem.
I filed the underlying bug as
rustls/rustls-platform-verifier#240 — it reproduces with plain
example.com. and no iroh involved. Opening this separately because the
combination is what makes the feature unusable in practice, and iroh is
where the two halves meet.
Repro
let endpoint = Endpoint::builder(iroh::endpoint::presets::N0)
.ca_tls_config(CaTlsConfig::system())
.alpns(vec![b"my/alpn/1".to_vec()])
.bind()
.await?;
with iroh = { version = "1.0.3", features = ["platform-verifier"] }, on
Windows:
ERROR endpoint:actor:reportgen-actor:run-probe{proto=Https relay=https://aps1-1.relay.n0.iroh.link./}:
failed to verify TLS certificate: invalid peer certificate: NotValidForName
WARN endpoint:actor:reportgen-actor:run-probe{proto=Https relay=https://aps1-1.relay.n0.iroh.link./}:
probe failed: Failed to run HTTPS probe: HTTP request failed:
error sending request for url (https://aps1-1.relay.n0.iroh.link./ping):
client error (Connect): invalid peer certificate: NotValidForName
Every region, on a loop. Dropping back to the default
CaTlsConfig::embedded() makes the same relays connect immediately, so
it is not the certificates and not the network.
Why this matters more than the feature being niche
platform-verifier is exactly what a machine behind a TLS-inspecting
proxy (Cato SASE, Zscaler, any corporate MITM appliance) needs: the
interception CA is in the OS store, and webpki-roots reads neither
SSL_CERT_FILE nor SSL_CERT_DIR, so there is no way to widen trust
from outside a shipped binary. Those users are on Windows more often than
not, and Windows is where the feature does not work.
CaTlsConfig is otherwise well-shaped for this — the escape hatch below
only exists because with_extra_roots is there.
Workaround, for anyone who finds this
Take the roots from the OS but leave the name matching to webpki:
let found = rustls_native_certs::load_native_certs();
let ca = CaTlsConfig::embedded().with_extra_roots(found.certs);
rustls-native-certs reads the same stores platform-verifier would
(CryptoAPI on Windows, Keychain on macOS, the OS bundle on Linux), and
honours SSL_CERT_FILE / SSL_CERT_DIR as a bonus. Keeping embedded()
underneath rather than custom_roots also means a host with no CA bundle
at all still binds instead of failing at bind() with
InvalidCaRootConfig.
Possible fixes on this side
Whichever you prefer, and none of them are urgent if the upstream fix
lands:
- Nothing here — wait for
rustls-platform-verifier to strip the root
label, then bump.
- Normalise the server name where the relay URL becomes a
ServerName,
so the trailing dot never reaches the verifier.
- Document on
CaTlsConfig::system() that it is currently incompatible
with the default relay map on Windows. Right now the feature reads as
the supported answer for this problem, and following it makes things
worse than doing nothing.
Environment
- Windows 11 Pro 26200
- iroh 1.0.3,
rustls-platform-verifier 0.7.0
- Reproduced against the default
presets::N0 relay map
Summary
On Windows, enabling the
platform-verifierfeature and building anendpoint with
CaTlsConfig::system()makes every default relay failTLS with
NotValidForName— including relays that verify fine with thedefault embedded roots.
The cause is that iroh's default relay hostnames are absolute FQDNs, with
the root label's trailing dot (
iroh-1.0.3/src/defaults.rs:27):rustls-platform-verifierpasses that name to CryptoAPI verbatim, whichcompares
aps1-1.relay.n0.iroh.link.against the certificate's dot-lessSAN and returns
CERT_E_INVALID_NAME. webpki drops the root label duringname matching and never sees a problem.
I filed the underlying bug as
rustls/rustls-platform-verifier#240 — it reproduces with plain
example.com.and no iroh involved. Opening this separately because thecombination is what makes the feature unusable in practice, and iroh is
where the two halves meet.
Repro
with
iroh = { version = "1.0.3", features = ["platform-verifier"] }, onWindows:
Every region, on a loop. Dropping back to the default
CaTlsConfig::embedded()makes the same relays connect immediately, soit is not the certificates and not the network.
Why this matters more than the feature being niche
platform-verifieris exactly what a machine behind a TLS-inspectingproxy (Cato SASE, Zscaler, any corporate MITM appliance) needs: the
interception CA is in the OS store, and
webpki-rootsreads neitherSSL_CERT_FILEnorSSL_CERT_DIR, so there is no way to widen trustfrom outside a shipped binary. Those users are on Windows more often than
not, and Windows is where the feature does not work.
CaTlsConfigis otherwise well-shaped for this — the escape hatch belowonly exists because
with_extra_rootsis there.Workaround, for anyone who finds this
Take the roots from the OS but leave the name matching to webpki:
rustls-native-certsreads the same storesplatform-verifierwould(CryptoAPI on Windows, Keychain on macOS, the OS bundle on Linux), and
honours
SSL_CERT_FILE/SSL_CERT_DIRas a bonus. Keepingembedded()underneath rather than
custom_rootsalso means a host with no CA bundleat all still binds instead of failing at
bind()withInvalidCaRootConfig.Possible fixes on this side
Whichever you prefer, and none of them are urgent if the upstream fix
lands:
rustls-platform-verifierto strip the rootlabel, then bump.
ServerName,so the trailing dot never reaches the verifier.
CaTlsConfig::system()that it is currently incompatiblewith the default relay map on Windows. Right now the feature reads as
the supported answer for this problem, and following it makes things
worse than doing nothing.
Environment
rustls-platform-verifier0.7.0presets::N0relay map