You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Component:src/cluster/Transport.ts Severity (assessment): MEDIUM — see the note at the end, it is arguable CWE: CWE-287 (improper authentication)
The hello frame carries a NodeAddress and nothing else. Even on a fully
mTLS-configured cluster, nothing ties the identity a peer claims in hello
to the identity its certificate proves. mTLS answers "is this peer allowed
in the cluster"; it never answers "is this peer the node it says it is".
Why this matters now
The gossip-authority rules landed in #562/#564/#572 are all keyed on the
connection's peer — deliberately, because the payload's from is the one
field an attacker fully controls. Those rules are exactly as strong as the
connection identity underneath them, and today that identity is self-declared.
So a single compromised or malicious CA-signed node can still:
announce itself under another member's address and, once the leader
promotes it, satisfy the "sender must be an active member" rule for
third-party claims;
receive traffic addressed to the impersonated node, because TcpTransport.byPeer is keyed on the claimed address.
The duplicate-identity guard (Transport.ts:299-313) only blocks claiming an
address that is already mapped. A fresh claim, or a claim made after the
real holder's connection drops, is accepted.
Evidence
src/cluster/Transport.ts — onMessage accepts any hello and registers it:
if(message.kind==='hello'){constpeer=NodeAddress.fromJSON(message.self);constpeerKey=peer.toString();// ... hijack guard: only rejects an ALREADY-mapped peerKey ...connection.peer=peer;this.byPeer.set(peerKey,connection);
TcpSocketLike (src/runtime/tcp/TcpBackend.ts:44-48) exposes only write / end / remoteAddress — the peer certificate is not surfaced by
the abstraction at all, so no code above the adapters could check it today.
This is also stated as a known residual in the #565 verifier notes ("the only
check is a duplicate-identity guard … not a fresh claimed identity") but was
never filed on its own.
When the listener required a client certificate, reject a hello whose
claimed systemName@host is not covered by the certificate's CN or a SAN
entry. Log and drop the connection, the same way the frame guards do.
Leave plain-TCP clusters as they are — the transport is documented as
unauthenticated there, and this check has nothing to work with.
Acceptance criteria
A peer presenting a valid CA-signed certificate for node-a cannot
complete a hello claiming to be node-b.
A matching certificate still completes the handshake unchanged.
With no client certificate required, behaviour is unchanged.
docs/.../operations/security/cluster-security.mdx (EN + DE) drops the
"This is not authentication" caveat added alongside the gossip-authority
rules, since it would no longer be true for mTLS clusters.
Severity note
Filed medium to match how the tracker already calibrates "one authenticated
member can impersonate another" (#574, #582, #719 are all medium). An argument
for high exists: this is the assumption every one of the new authority rules
rests on, so it is less a finding of its own than the floor beneath several
others. Retag if you read the boundary that way.
Component:
src/cluster/Transport.tsSeverity (assessment): MEDIUM — see the note at the end, it is arguable
CWE: CWE-287 (improper authentication)
The
helloframe carries aNodeAddressand nothing else. Even on a fullymTLS-configured cluster, nothing ties the identity a peer claims in
helloto the identity its certificate proves. mTLS answers "is this peer allowed
in the cluster"; it never answers "is this peer the node it says it is".
Why this matters now
The gossip-authority rules landed in #562/#564/#572 are all keyed on the
connection's peer — deliberately, because the payload's
fromis the onefield an attacker fully controls. Those rules are exactly as strong as the
connection identity underneath them, and today that identity is self-declared.
So a single compromised or malicious CA-signed node can still:
promotes it, satisfy the "sender must be an active member" rule for
third-party claims;
TcpTransport.byPeeris keyed on the claimed address.The duplicate-identity guard (
Transport.ts:299-313) only blocks claiming anaddress that is already mapped. A fresh claim, or a claim made after the
real holder's connection drops, is accepted.
Evidence
src/cluster/Transport.ts—onMessageaccepts anyhelloand registers it:TcpSocketLike(src/runtime/tcp/TcpBackend.ts:44-48) exposes onlywrite/end/remoteAddress— the peer certificate is not surfaced bythe abstraction at all, so no code above the adapters could check it today.
This is also stated as a known residual in the #565 verifier notes ("the only
check is a duplicate-identity guard … not a fresh claimed identity") but was
never filed on its own.
Proposed approach
TcpSocketLike— Node exposestlsSocket.getPeerCertificate(), Bun has an equivalent; on Deno thelistener half of mTLS is unavailable anyway ([Security] DenoTcpBackend silently drops
ca,requestClientCertand the client cert/key — cluster mTLS cannot be enabled at all on Deno #576), so the field isundefinedthere and the check degrades to today's behaviour.hellowhoseclaimed
systemName@hostis not covered by the certificate's CN or a SANentry. Log and drop the connection, the same way the frame guards do.
unauthenticated there, and this check has nothing to work with.
Acceptance criteria
node-acannotcomplete a
helloclaiming to benode-b.docs/.../operations/security/cluster-security.mdx(EN + DE) drops the"This is not authentication" caveat added alongside the gossip-authority
rules, since it would no longer be true for mTLS clusters.
Severity note
Filed
mediumto match how the tracker already calibrates "one authenticatedmember can impersonate another" (#574, #582, #719 are all medium). An argument
for
highexists: this is the assumption every one of the new authority rulesrests on, so it is less a finding of its own than the floor beneath several
others. Retag if you read the boundary that way.
Related
requestClientCertdefaults tofalse, so the documented mTLS recipe gives zero peer authentication #565 — made the listener actually request a client certificate (closed); thisis the half that verifies what the certificate says.
mergeMemberlets any peer assert any status for any node, including the receiver's own record — no origin/authority rule on gossip #562, [Security] Forgedleaveframe lets any unauthenticated wire peer tombstone (evict) any cluster member for 24 h, and the eviction spreads via gossip #564, [Security] Forgedheartbeat.fromkeeps a dead node "healthy" forever (blocks singleton/shard failover) and makes the node dial an attacker-chosen host:port #572 — the authority rules that depend on the connection identity.ca,requestClientCertand the client cert/key — cluster mTLS cannot be enabled at all on Deno #576 — Deno cannot host an mTLS listener, so step 1 is a no-op there.hellois a protocolchange and should be sequenced with it.