Skip to content

Trust platform root certificates for relay WebSocket TLS#2517

Open
ilkonovalov wants to merge 1 commit into
block:mainfrom
ilkonovalov:ws-client-native-root-certs
Open

Trust platform root certificates for relay WebSocket TLS#2517
ilkonovalov wants to merge 1 commit into
block:mainfrom
ilkonovalov:ws-client-native-root-certs

Conversation

@ilkonovalov

Copy link
Copy Markdown

Problem

crates/buzz-ws-client/src/connection.rs connects to the relay with
tokio_tungstenite::connect_async(...), which uses the default TLS
configuration. The workspace enables:

tokio-tungstenite = { version = "0.29", features = ["rustls-tls-webpki-roots"] }

rustls-tls-webpki-roots compiles in a fixed copy of the Mozilla public root
list. It does not consult the OS trust store or SSL_CERT_FILE, and there is
no code path to inject an extra trust anchor. As a result, a client/agent
cannot connect (wss://) to a relay whose certificate chains up to a private
or internal CA — the handshake terminates with invalid peer certificate: UnknownIssuer. This blocks any self-hosted deployment that terminates TLS
with an organization-issued certificate.

Change

Switch the TLS feature to rustls-tls-native-roots:

-tokio-tungstenite = { version = "0.29", features = ["rustls-tls-webpki-roots"] }
+tokio-tungstenite = { version = "0.29", features = ["rustls-tls-native-roots"] }

The client now trusts the platform trust store, which includes the public CAs
and any operator-installed CA. Public relays keep working; relays behind a
private CA start working without any client code change.

Deployment note (containers / Helm): because trust now comes from the
platform store, operators of a containerized relay can add a private CA the
standard way — provision it into the container's trust store (e.g. mount the
CA bundle into /etc/ssl/certs via the chart) — with no rebuild of the binary.

Caveat / alternative

native-roots requires the runtime to actually have a trust store. Minimal
images (scratch / distroless) that ship no CA bundle would start with an empty
root set and fail TLS even to public relays; such images must include
ca-certificates.

If a zero-regression path is preferred, an alternative is to build an explicit
rustls::ClientConfig in connection.rs that seeds roots from webpki-roots
(so public CAs always work) and adds native certs plus an optional CA file
(e.g. from an env var), then pass it via
connect_async_tls_with_config(..., Some(Connector::Rustls(...))). Happy to
switch to that approach if maintainers prefer it.

@ilkonovalov
ilkonovalov requested a review from a team as a code owner July 23, 2026 13:55
connect_async uses the default TLS config, which via rustls-tls-webpki-roots trusts only the bundled Mozilla roots. Clients cannot reach a relay served behind a certificate signed by a private/internal CA (handshake fails with UnknownIssuer), and there is no runtime way to add a trusted root. Switch tokio-tungstenite to rustls-tls-native-roots so the client trusts the platform trust store (public CAs plus any operator-installed CA); in container/Helm deployments the CA can be provisioned into the container trust store with no rebuild.

Signed-off-by: ilkonovalov <ilkonovalov@users.noreply.github.com>
@ilkonovalov
ilkonovalov force-pushed the ws-client-native-root-certs branch from a0ce96f to 2ecec7f Compare July 23, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant