server+docs: construct onion message rate limiters and document config
What changed, and why it matters
This commit is a straightforward feature-completion patch: it wires up newly added onion-message rate-limiting code, documents the new configuration options in the sample config file, and passes the limiter into peer setup. There is no bug fix, no security vulnerability being patched, and no indication of an active attack or prior weakness. It is a defensive resource-control feature being enabled, not a response to a disclosed incident.
No security action required; treat as normal feature code review. Operators who enable onion messaging should review the new rate-limiting defaults and adjust per-peer/global kbps and burst values based on peer count and bandwidth expectations as described in sample-lnd.conf.
Security signals we found
Adds resource-limiting controls for onion message ingress bandwidth
Documents configuration defaults and starvation behavior for operators
Only allocates limiters when onion messaging is enabled
No vulnerability disclosure, CVE, or incident reference present in commit or materials
Evidence from the diff
The change constructs per-peer and global token-bucket ingress limiters for BOLT 1.3 onion messages inside server.Start when onion messaging is enabled, composes them via onionmessage.NewIngressLimiter, stores the result in server.onionLimiter, and adds it to peer.Config as OnionLimiter. sample-lnd.conf is updated with commented-out defaults and explanations for protocol.onion-msg-peer-kbps, protocol.onion-msg-peer-burst-bytes, protocol.onion-msg-global-kbps, and protocol.onion-msg-global-burst-bytes. The limiters are only allocated when onion messaging is enabled. No existing behavior is removed or weakened.
Changed components
server.go onion messaging setup blockpeer.Config OnionLimiter fieldsample-lnd.conf protocol options documentationInspect captured patch +76 / −0
diff --git a/sample-lnd.conf b/sample-lnd.conf
index f3a9c51..4644b98 100644
--- a/sample-lnd.conf
+++ b/sample-lnd.conf
@@ -1458,6 +1458,50 @@
; set to disable onion message support.
; protocol.no-onion-messages=false
+; Maximum sustained onion message ingress bandwidth from any single peer,
+; in decimal kilobits per second (1 Kbps = 1000 bits/s). Tokens in the
+; underlying bucket are bytes, so small onion messages pay less of the
+; budget than spec-max ones. To disable the per-peer limiter, set both
+; this and protocol.onion-msg-peer-burst-bytes to 0; setting only one of
+; the pair to 0 is rejected at startup as a configuration error. Defaults
+; to ~0.5 Mbps (roughly two spec-max onion messages per second).
+; protocol.onion-msg-peer-kbps=512
+
+; Token bucket depth for the per-peer onion message rate limiter, in
+; bytes. Must be at least 65535 (max on-the-wire onion message size:
+; 2-byte type prefix + lnwire.MaxMsgBody) so that a single maximum-sized
+; wire message can always fit in the bucket; otherwise the limiter would
+; reject every call and silently disable onion forwarding.
+; The default is 8 * 32 KiB = 262144, enough to absorb a small burst of
+; spec-max messages. To disable the per-peer limiter, set both this and
+; protocol.onion-msg-peer-kbps to 0.
+; protocol.onion-msg-peer-burst-bytes=262144
+
+; Maximum sustained aggregate onion message ingress bandwidth across all
+; peers combined, in decimal kilobits per second. To disable the global
+; limiter, set both this and protocol.onion-msg-global-burst-bytes to 0;
+; setting only one of the pair to 0 is rejected at startup as a
+; configuration error. The default of ~5 Mbps is sized so that onion
+; message traffic cannot dwarf a typical routing node's payment traffic.
+;
+; Note on starvation: the global limit is shared across all peers, so if
+; you run a routing node with many well-behaved peers simultaneously
+; sending at their full per-peer allowance the global budget can be
+; saturated. With the default 0.5 Mbps peer and 5 Mbps global, 10 peers
+; at their full per-peer rate exactly fill the global budget. If you
+; expect many concurrent onion-message-active peers, scale this value up
+; (or the per-peer rate down) to preserve headroom.
+; protocol.onion-msg-global-kbps=5120
+
+; Token bucket depth for the global onion message rate limiter, in
+; bytes. Must be at least 65535 (max on-the-wire onion message size:
+; 2-byte type prefix + lnwire.MaxMsgBody). The default is
+; 50 * 32 KiB = 1638400, enough to absorb a burst of spec-max messages
+; while keeping the long-term rate bounded by onion-msg-global-kbps. To
+; disable the global limiter, set both this and
+; protocol.onion-msg-global-kbps to 0.
+; protocol.onion-msg-global-burst-bytes=1638400
+
; Set to handle messages of a particular type that falls outside of the
; custom message number range (i.e. 513 is onion messages). Note that you can
; set this option as many times as you want to support more than one custom
diff --git a/server.go b/server.go
index cba68de..d50eb2f 100644
--- a/server.go
+++ b/server.go
@@ -442,6 +442,15 @@ type server struct {
*onionmessage.Request, *onionmessage.Response,
]
+ // onionLimiter is the combined per-peer + global onion message
+ // ingress limiter. It hides the split between the two underlying
+ // buckets behind a single interface so peer.Config only needs to
+ // carry one field and brontide.readHandler only needs one call
+ // per incoming onion message. Nil means onion message rate
+ // limiting is disabled (e.g. when onion messaging itself is
+ // turned off).
+ onionLimiter onionmessage.IngressLimiter
+
// txPublisher is a publisher with fee-bumping capability.
txPublisher *sweep.TxPublisher
@@ -2410,6 +2419,28 @@ func (s *server) Start(ctx context.Context) error {
s.defaultOnionActorOpts = onionmessage.
DefaultOnionActorOpts()
+
+ // Build the global and per-peer onion message rate
+ // limiters from the configured values, then compose
+ // them behind a single IngressLimiter so the peer
+ // package only needs to carry one field. A zero
+ // kbps or a zero burst-bytes disables the
+ // corresponding bucket; rates are expressed in
+ // decimal kilobits per second and bursts in bytes
+ // so operators can reason about onion message
+ // ingress in terms of bandwidth rather than raw
+ // message counts.
+ onionPeerLim := onionmessage.NewPeerRateLimiter(
+ s.cfg.ProtocolOptions.OnionMsgPeerKbps,
+ s.cfg.ProtocolOptions.OnionMsgPeerBurstBytes,
+ )
+ onionGlobalLim := onionmessage.NewGlobalLimiter(
+ s.cfg.ProtocolOptions.OnionMsgGlobalKbps,
+ s.cfg.ProtocolOptions.OnionMsgGlobalBurstBytes,
+ )
+ s.onionLimiter = onionmessage.NewIngressLimiter(
+ onionPeerLim, onionGlobalLim,
+ )
}
cleanup = cleanup.add(s.chanStatusMgr.Stop)
@@ -4479,6 +4510,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,
RoutingPolicy: s.cc.RoutingPolicy,
SphinxPayment: s.sphinxPayment,
SpawnOnionActor: s.onionActorFactory,
+ OnionLimiter: s.onionLimiter,
OnionActorOpts: func(_ [33]byte) []actor.ActorOption[
*onionmessage.Request, *onionmessage.Response,
] {
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.