fix(agent): hand-roll wss:// relay dial with pinned-CA TLS (VA-76) #81
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/VA-76-relay-wss-tls-dial"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
The Files tab (and every other relay-backed usage: terminal, desktop, vnc, rdp, mcp) failed with
tunnel WS connect failed: URL error: TLS support not compiled in.Root cause:
host/tunnel.rs::opendialed the relay withtokio_tungstenite::connect_async, but the crate buildstokio-tungstenitewithdefault-features = false, features = ["connect"](no TLS backend). Awss://target therefore returnstungstenite::error::UrlError::TlsFeatureNotEnabled. The control channel never hit this becausenet::run_connectionbuilds the TLS stream by hand and passes it toclient_async_with_config; the tunnel path just never adopted that pattern.Trust anchor
Probing the relay shows its leaf is issued by the internal
CN=VervainRootCA (subjectCN=api.vervain.a8n.systems, SANDNS:api.vervain.a8n.systems), not a publicly-trusted CA. So enabling a public-roots TLS feature onconnect_asyncwould compile but still fail verification. The correct anchor is the same internal Vervain PKI the control channel already pins (the enrolledSERVER_CA_KEY); the relay leaf is signed by the same root.Changes
net.rs: newpub(crate) fn relay_tls_config(server_ca_pem), mirroringbuild_steady_state_configbutwith_no_client_auth()(the relay authenticates the agent via therauthtoken in the rendezvous URL, not a client cert). Reusesload_roots+PinnedServerVerifier(pins by trust anchor, skips server-name validation, consistent with the control channel).host/tunnel.rs: replaceconnect_asyncwith hand-rolleddial_relay(parse URI, TCP connect,set_nodelay;wss://-> rustls viarelay_tls_config+TlsConnector::connect+client_async_with_config;ws://-> plain). NewRelayStream { Tls, Plain }enum keeps the per-usage run loop generic over one stream type. Stale docstring corrected.dispatch.rs:Dispatchergainsserver_ca+set_server_ca;handle_msg_tunnelpasses it through.Session::new(net.rs) sets it from the store alongsideset_server_url.No
rustls-tls-*feature added; aws-lc-rs is not pulled in (crate stays ring-only). Devws://loop keeps working. No tests calltunnel::spawn/opendirectly, so the signature changes are internal.Testing
just pre-commitgreen (fmt, clippy--deny warnings, build, host + windows targets, lib tests, tpm tests).Fixes VA-76.
🤖 Generated with Claude Code
The relay tunnel dialed with `tokio_tungstenite::connect_async`, but the crate builds tokio-tungstenite with no TLS backend feature, so a `wss://` target returned `UrlError::TlsFeatureNotEnabled` ("TLS support not compiled in"). This broke every relay-backed usage (files, terminal, desktop, vnc, rdp, mcp), surfacing first on the Files tab. Replace `connect_async` with a hand-rolled `dial_relay` that mirrors the control channel and `wsproxy`: TCP connect, then for `wss://` wrap in rustls and run `client_async_with_config`; for `ws://` (dev) use the plain stream. The relay leaf is issued by the internal `VervainRoot` CA (not a public CA), so verification reuses the pinned server CA the control channel already trusts via a new `net::relay_tls_config` (no client cert; the relay authenticates the agent via the rendezvous `rauth` token). A small `RelayStream { Tls, Plain }` enum keeps the per-usage run loop generic over one stream type. Thread the pinned CA `Session::new` -> `Dispatcher::set_server_ca` -> `handle_msg_tunnel` -> `tunnel::spawn`/`open`. No `rustls-tls-*` feature is added and aws-lc-rs is not pulled in. Stale tunnel.rs docstring corrected. #VA-76