feat(mesh): mutually-authenticated TLS for the peer transport#557
Merged
Conversation
Adds peer mTLS to the mesh cache transport. A new transport::tls module builds a rustls TlsAcceptor/TlsConnector (pinned to the ring provider, peer certs verified against a shared CA via WebPkiClientVerifier) from PEM cert/key/CA. The TCP transport is wired to use it: - TransportServer::start_with_security wraps each accepted connection in a TLS session before any frame is read, so a peer without a CA-signed cert is dropped before it can issue an RPC; the handshake runs in the per-connection task so it can't stall the accept loop. - PeerClient::with_security wraps outbound connections in TLS. - handle_connection is now generic over the stream, and PeerClient holds a plain-or-TLS connection enum (the frame codec was already stream-agnostic). Verified end to end by an in-crate test: a PeerClient does a put/get roundtrip to a TransportServer over a mutual-TLS session, plus loopback handshake tests (a valid mutual handshake transfers bytes; an untrusted client cert is rejected). All 416 mesh tests pass with no plaintext-path regression. This is the transport mechanism; reading cert/key/CA from the mesh config and enabling it at bootstrap is the immediate follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X19S6eQzKKExZ9RUPAHuGy
rickcrawford
added a commit
that referenced
this pull request
Jun 27, 2026
…558) Wires the mesh peer-mTLS transport (the mechanism landed in #557) through to operator config. A new `peer_tls` block under `key_management.cache.mesh` takes `cert_file` / `key_file` / `ca_file`, plus an optional `server_name` (default `sbproxy-mesh`). Flow across the layers: - sbproxy-config gains MeshPeerTlsConfig on MeshClusterConfig (config schema regenerated). - key_plane reads the PEM files (fail-closed on a read error) into a PeerTlsParams on BootstrapConfig. - bootstrap builds the rustls acceptor + connector from it and threads them into TransportServer::start_with_security and TransportClientPool::with_security (which now creates TLS PeerClients). Unset keeps the plaintext transport. The transport mechanism and its end-to-end handshake tests are already in place; this is the operator-facing toggle. Claude-Session: https://claude.ai/code/session_01X19S6eQzKKExZ9RUPAHuGy Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Real peer mTLS for the mesh cache transport. Until now the mesh TCP transport was plaintext and
peer_authwas an unwired X.509 verifier; this gives the transport an actual mutually-authenticated TLS layer.How
transport/tls.rs(new):build_acceptor/build_connectorbuild a rustlsTlsAcceptor/TlsConnectorfrom PEMcert/key/ca. Both sides present a cert and verify the peer against the shared CA using rustls's built-inWebPkiClientVerifier(server) and root-store server verification (client). The crypto provider is pinned toringto match the rest of the build.server.rs):start_with_security(port, cache, cipher, tls)wraps each accepted TCP stream in a TLS session inside the per-connection task before reading frames; a failed handshake drops the peer without stalling the accept loop.start_with_cipherdelegates withtls: None.handle_connectionis now generic over the stream type.client.rs):PeerClient::with_security(addr, cipher, tls)dials, then wraps in TLS via aMeshConnenum (Plain(TcpStream)/Tls(...)) that delegatesAsyncRead/AsyncWrite. Theread_frame/write_framecodec was already generic over the stream.The existing optional
Cipher(AES-GCM framing) threads through unchanged; mTLS is the alternative transport-security layer.Tests
mtls_server_and_peer_client_roundtrip— aPeerClientdoes a put then get to aTransportServer, both configured for mTLS, and the value round-trips through the mutually-authenticated session and the server's cache.-D warningsand rustdoc-D warnings -D missing_docsclean. New dep:tokio-rustls(ring features, matchingrustls).Scope
This is the transport mechanism, proven end to end. Reading
cert/key/cafrom the mesh config block and enabling mTLS at bootstrap (so an operator turns it on fromsb.yml) is the immediate follow-up. Streaming/QUIC transports and a default-on policy are out of scope here.