Skip to content

beacon: relay budget keyed on transport source, WSS peer-map ownership, bounded WSS writes, gossip peer-set gate - #50

Merged
TeoSlayer merged 1 commit into
mainfrom
sec/findings-beacon
Jul 26, 2026
Merged

beacon: relay budget keyed on transport source, WSS peer-map ownership, bounded WSS writes, gossip peer-set gate#50
TeoSlayer merged 1 commit into
mainfrom
sec/findings-beacon

Conversation

@TeoSlayer

Copy link
Copy Markdown
Contributor

Security findings L6, H4, M11, M10. Recreated against main (the original PR auto-closed when its base branch was merged and deleted).

  • L6 relay budget was keyed on senderID, a field of the frame body — now keyed on the observed UDP source endpoint, or the bridge-authenticated peer id for WSS-delivered frames. Keyed on IP:port rather than IP alone so a host running many co-located daemons isn't collapsed into one budget.
  • H4 dropPeer deleted whatever was registered under the node id, so a replaced connection's read-loop exit evicted its live replacement — now compare-and-deletes the specific peer.
  • M11 WriteFrame used the 90s idle timeout as its write deadline, pinning a relay worker on a stalled peer — added Config.WriteTimeout (default 2s).
  • M10 gossip peer-set gate behind default-off -strict-gossip; route expiry is unconditional.

🤖 Generated with Claude Code

…p, bounded WSS writes, gossip peer-set gate

L6 — dispatchRelay charged the per-source relay budget against the
senderID field of the frame body, which the sender writes. One origin
could therefore open an unbounded number of independent budgets by
varying that field per packet. The budget is now keyed on the observed
datagram endpoint, or, for frames handed over by the compat WSS bridge
(which have no datagram source of their own), on the peer id the bridge
verified against the node's registered key at connect time. The key is a
comparable value type built without allocating, so the relay hot path
keeps its per-packet cost.

Keying on the full source endpoint rather than the address alone keeps
co-located fleets — hosts running hundreds of daemons behind one address
— on per-daemon budgets instead of collapsing them onto one.

H4 — wss dropPeer removed whatever connection was registered for a node
id. A node that reconnects installs a fresh connection under the same
key while the previous connection's read loop is still unwinding, and
that loop's cleanup then deregistered the live replacement. dropPeer now
takes the *wssPeer and compare-and-deletes.

M11 — wss WriteFrame bounded the write with IdleTimeout (90s). It is
called from the beacon's relay workers, so a peer that stopped draining
its socket could hold a worker for that whole window. Added
Config.WriteTimeout (default 2s) for the per-frame deadline; the idle
window keeps governing connection lifetime. A peer whose write expires
is dropped, so later calls return immediately instead of re-entering the
same stall.

M10 — handleSync rewrites the node-id → beacon routing table the relay
workers read, and accepted a sync from any source. Added an opt-in
peer-set gate (SetStrictGossip / -strict-gossip /
BEACON_STRICT_GOSSIP=1) restricting syncs to addresses in the peer set
learned from -peers and the registry. Default off, because a beacon
behind DNAT egresses from an address other than the one it advertises
and enabling the check there would stop the mesh converging. Route
expiry (gossipPeerTTL, swept from reapStaleNodes) is unconditional:
routes published by a beacon that stops gossiping used to stand forever.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@TeoSlayer
TeoSlayer force-pushed the sec/findings-beacon branch from f3af12c to 422bf7f Compare July 26, 2026 14:14
@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@TeoSlayer
TeoSlayer merged commit 7fb0fcc into main Jul 26, 2026
5 checks passed
@TeoSlayer
TeoSlayer deleted the sec/findings-beacon branch July 26, 2026 14:17
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.

2 participants