multi: add --protocol.no-onion-messages flag
What changed, and why it matters
This commit adds a new user-configurable switch that lets an LND node operator turn off support for "onion messages" (a type of Lightning network control message). When the switch is enabled, the node stops advertising that it supports the feature, does not create the internal component that handles those messages, and ignores incoming onion messages. It is a defensive feature addition, not a fix for an active bug or vulnerability, and it does not change default behavior.
No urgent action is required. Operators concerned about onion-message-related resource consumption or attack surface may choose to set `--protocol.no-onion-messages=true` after reviewing release notes. Security reviewers should monitor whether future commits add related hardening or whether this flag was added in response to a disclosed issue.
Security signals we found
Adds an opt-in denial-of-service mitigation by allowing operators to disable an optional network feature
Does not change default behavior; nodes must explicitly enable the flag
No removal of existing validation, authentication, or resource limits for nodes that do not set the flag
No CVE, security advisory, or incident disclosure is referenced in the commit
Evidence from the diff
The patch introduces a --protocol.no-onion-messages configuration flag. It threads the setting through lncfg.ProtocolOptions (both build and integration variants), passes it to feature.Config as NoOnionMessages, and uses it in feature/manager.go to unset the OnionMessagesOptional and OnionMessagesRequired feature bits. In server.go, when the flag is set, Start() skips instantiating the onionmessage.OnionActorFactory, so no onion-message handler is registered with peers. The default remains enabled (false).
Changed components
lncfg/protocol.golncfg/protocol_integration.gofeature/manager.goserver.gosample-lnd.confInspect captured patch +40 / −9
diff --git a/feature/manager.go b/feature/manager.go
index baef440..9a0950d 100644
--- a/feature/manager.go
+++ b/feature/manager.go
@@ -77,6 +77,10 @@ type Config struct {
// coop close.
NoRbfCoopClose bool
+ // NoOnionMessages unsets any bits that signal support for onion
+ // messaging.
+ NoOnionMessages bool
+
// CustomFeatures is a set of custom features to advertise in each
// set.
CustomFeatures map[Set][]lnwire.FeatureBit
@@ -221,6 +225,10 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
raw.Unset(lnwire.RbfCoopCloseOptionalStaging)
raw.Unset(lnwire.RbfCoopCloseOptional)
}
+ if cfg.NoOnionMessages {
+ raw.Unset(lnwire.OnionMessagesOptional)
+ raw.Unset(lnwire.OnionMessagesRequired)
+ }
for _, custom := range cfg.CustomFeatures[set] {
if custom > set.Maximum() {
diff --git a/lncfg/protocol.go b/lncfg/protocol.go
index b130d1b..73fc7da 100644
--- a/lncfg/protocol.go
+++ b/lncfg/protocol.go
@@ -71,6 +71,9 @@ type ProtocolOptions struct {
// NoRouteBlindingOption disables forwarding of payments in blinded routes.
NoRouteBlindingOption bool `long:"no-route-blinding" description:"do not forward payments that are a part of a blinded route"`
+ // NoOnionMessagesOption disables onion message forwarding.
+ NoOnionMessagesOption bool `long:"no-onion-messages" description:"disable support for onion messaging"`
+
// NoExperimentalAccountabilityOption disables experimental accountability.
NoExperimentalAccountabilityOption bool `long:"no-experimental-accountability" description:"do not forward experimental accountability signals"`
@@ -144,6 +147,11 @@ func (l *ProtocolOptions) NoRouteBlinding() bool {
return l.NoRouteBlindingOption
}
+// NoOnionMessages returns true if onion messaging is disabled.
+func (l *ProtocolOptions) NoOnionMessages() bool {
+ return l.NoOnionMessagesOption
+}
+
// NoExpAccountability returns true if experimental accountability should be
// disabled. It also checks the deprecated NoExperimentalEndorsementOption for
// backwards compatibility.
diff --git a/lncfg/protocol_integration.go b/lncfg/protocol_integration.go
index 752a1a8..0961b94 100644
--- a/lncfg/protocol_integration.go
+++ b/lncfg/protocol_integration.go
@@ -74,6 +74,9 @@ type ProtocolOptions struct {
// NoRouteBlindingOption disables forwarding of payments in blinded routes.
NoRouteBlindingOption bool `long:"no-route-blinding" description:"do not forward payments that are a part of a blinded route"`
+ // NoOnionMessagesOption disables onion message forwarding.
+ NoOnionMessagesOption bool `long:"no-onion-messages" description:"disable support for onion messaging"`
+
// NoExperimentalAccountabilityOption disables experimental accountability.
NoExperimentalAccountabilityOption bool `long:"no-experimental-accountability" description:"do not forward experimental accountability signals"`
@@ -142,6 +145,11 @@ func (l *ProtocolOptions) NoRouteBlinding() bool {
return l.NoRouteBlindingOption
}
+// NoOnionMessages returns true if onion messaging is disabled.
+func (l *ProtocolOptions) NoOnionMessages() bool {
+ return l.NoOnionMessagesOption
+}
+
// NoExpAccountability returns true if experimental accountability should be
// disabled. It also checks the deprecated NoExperimentalEndorsementOption for
// backwards compatibility.
diff --git a/sample-lnd.conf b/sample-lnd.conf
index a487565..f874fad 100644
--- a/sample-lnd.conf
+++ b/sample-lnd.conf
@@ -1450,6 +1450,9 @@
; Set to enable support for RBF based coop close.
; protocol.rbf-coop-close=false
+; set to disable onion message support.
+; protocol.no-onion-messages=false
+
; 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 d6f25a9..54dc6dd 100644
--- a/server.go
+++ b/server.go
@@ -668,6 +668,7 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
NoTaprootChans: !cfg.ProtocolOptions.TaprootChans,
NoTaprootOverlay: !cfg.ProtocolOptions.TaprootOverlayChans,
NoRouteBlinding: cfg.ProtocolOptions.NoRouteBlinding(),
+ NoOnionMessages: cfg.ProtocolOptions.NoOnionMessages(),
NoExperimentalAccountability: cfg.ProtocolOptions.NoExpAccountability(),
NoQuiescence: cfg.ProtocolOptions.NoQuiescence(),
NoRbfCoopClose: !cfg.ProtocolOptions.RbfCoopClose,
@@ -2370,15 +2371,18 @@ func (s *server) Start(ctx context.Context) error {
}
// Create the onion message actor factory that will be used to
- // spawn per-peer actors for handling onion messages.
- resolver := &onionmessage.GraphNodeResolver{
- Graph: s.graphDB,
- OurPub: s.identityECDH.PubKey(),
- }
- s.onionActorFactory = onionmessage.NewOnionActorFactory(
- s.sphinxOnionMsg, resolver, s,
- s.onionMessageServer,
- )
+ // spawn per-peer actors for handling onion messages. Skip if
+ // onion messaging is disabled via config.
+ if !s.cfg.ProtocolOptions.NoOnionMessages() {
+ resolver := &onionmessage.GraphNodeResolver{
+ Graph: s.graphDB,
+ OurPub: s.identityECDH.PubKey(),
+ }
+ s.onionActorFactory = onionmessage.NewOnionActorFactory(
+ s.sphinxOnionMsg, resolver, s,
+ s.onionMessageServer,
+ )
+ }
cleanup = cleanup.add(s.chanStatusMgr.Stop)
if err := s.chanStatusMgr.Start(); err != nil {
Why this scored 19/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.