Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Networking] GossipSub Publish Message Validation (Unknown Topic Spam) #4720

Merged
merged 44 commits into from
Oct 31, 2023

Conversation

kc1116
Copy link
Contributor

@kc1116 kc1116 commented Sep 15, 2023

This PR adds RPC Publish messages validation to the control message validation inspector. When a node receives messages via a direct RPC from a remote peer, it processes them sequentially. However, if a message is on a topic the node has not subscribed, it is discarded. While this may seem harmless, an attacker can exploit this behavior to stage a Denial-of-Service attack by spamming a victim with a high volume of messages on unknown/invalid topics. This forces the victim to consume resources processing each message only to discard them. The control message validation inspector will now inspect a sample of the RPC publish messages and disseminate an invalid control message notification when the number of invalid messages exceeds the configured threshold.

This PR

  • Adds 2 new configuration values and corresponding CLI Flags
    • RPCMessageMaxSampleSize gossipsub-rpc-message-max-sample-size : The max sample size used for RPC message validation. If the total number of RPC messages exceeds this value a sample will be taken but messages will not be truncated
    • RPCMessageErrorThreshold gossipsub-rpc-message-error-threshold: The threshold at which an error will be returned if the number of invalid RPC messages exceeds this value
  • Inspects a sample of RPC publish messages after all control message validation has succeeded
    • Disseminates a single invalid control message notification for all errors encountered during validation

ref: https://github.com/dapperlabs/flow-go/issues/6798

@codecov-commenter
Copy link

codecov-commenter commented Sep 15, 2023

Codecov Report

Attention: 52 lines in your changes are missing coverage. Please review.

Comparison is base (d86b4f4) 52.96% compared to head (8890d75) 55.86%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4720      +/-   ##
==========================================
+ Coverage   52.96%   55.86%   +2.90%     
==========================================
  Files         754      945     +191     
  Lines       68045    88259   +20214     
==========================================
+ Hits        36037    49304   +13267     
- Misses      29289    35243    +5954     
- Partials     2719     3712     +993     
Flag Coverage Δ
unittests 55.86% <52.29%> (+2.90%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Δ
network/p2p/inspector/validation/errors.go 95.91% <100.00%> (+0.68%) ⬆️
network/p2p/scoring/registry.go 90.12% <100.00%> (+0.18%) ⬆️
network/p2p/test/fixtures.go 35.92% <0.00%> (+0.70%) ⬆️
network/netconf/flags.go 38.02% <40.00%> (+0.04%) ⬆️
utils/unittest/unittest.go 8.67% <0.00%> (-0.31%) ⬇️
...validation/control_message_validation_inspector.go 77.39% <78.84%> (+0.12%) ⬆️
utils/unittest/fixtures.go 0.00% <0.00%> (ø)

... and 199 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@kc1116 kc1116 marked this pull request as ready for review September 22, 2023 16:33
Copy link
Contributor

@yhassanzadeh13 yhassanzadeh13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, I have a suggestion about the dependency inversion that is imposed through SetSubscriptions. Please find my comment here.

@@ -310,7 +322,8 @@ func (g *Builder) Build(ctx irrecoverable.SignalerContext) (p2p.PubSubAdapter, e
g.metricsCfg.Metrics,
g.metricsCfg.HeroCacheFactory,
g.networkType,
g.idProvider)
g.idProvider,
g.subscriptions)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ I recommend not passing a p2p.Subscriptions object to the GossipSub RPC inspector. Doing so violates the Dependency Inversion Principle and the Interface Segregation Principle. The p2p.Subscriptions interface is higher-level and should not be depended upon by a lower-level component like the RPC inspector. This design choice risks circular dependencies and also breaks encapsulation, as methods irrelevant to the RPC inspector get exposed.

Moreover, using p2p.Subscription to query active subscriptions is unreliable, as it queries the Flow node than the GossipSub router; it's more accurate to consult the underlying pubsub router to avoid any false-positive due to glitches. To address this, I suggest refactoring to use the PubSubAdapter.GetTopics() method.

Dependency injection is required between the RPC inspector and the GossipSub adapter. To solve this, inject gossipSub.GetTopics() as a topic oracle within the RPC inspector by adding a method SetTopicOracle(topicOracle func()[]string) to the GossipSubInspectorSuite interface:

type GossipSubInspectorSuite interface {
	// existing methods ...

	// SetTopicOracle sets the topic oracle of the gossipsub inspector suite.
	// The topic oracle is used to determine the list of topics that the node is subscribed to.
	// If an oracle is not set, the node will not be able to determine the list of topics that the node is subscribed to.
	// If an oracle is already set, a fatal error is logged.
	SetTopicOracle(topicOracle func()[]string)
}

Inject this dependency in the codebase like so:

	gossipSub, err := g.gossipSubFactory(ctx, g.logger, g.h, gossipSubConfigs, inspectorSuite)
	if err != nil {
		return nil, fmt.Errorf("could not create gossipsub: %w", err)
	}
	inspectorSuite.SetTopicOracle(gossipSub.GetTopics())

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for applying the comment. However, there is still a minor concern. In reviewing the interfaces GossipSubInspectorSuite and InspectorTopicOracle, it's apparent that the encapsulation of SetTopicOracle within a separate interface does not provide a coherent or meaningful abstraction.

The InspectorTopicOracle has a single method SetTopicOracle, which is more of a configuration aspect rather than a functional capability deserving a separate interface. This violates the Single Responsibility Principle as it doesn’t represent a distinct responsibility that warrants a separate abstraction. Also, the name InspectorTopicOracle implies a broader functionality than merely setting a topic oracle. However, the sole method it encapsulates doesn't justify the semantics of an oracle. In this case, the oracle interface generally implies a source of information or decision-making capability, however, the capability this interface abstracts does not behave as a source of information.

My suggestion is to directly integrate the SetTopicOracle method into GossipSubInspectorSuite, which makes the design becomes more intuitive:

type GossipSubInspectorSuite interface {
	// existing methods ...

	// SetTopicOracle sets the topic oracle of the gossipsub inspector suite.
	// The topic oracle is used to determine the list of topics that the node is subscribed to.
	// If an oracle is not set, the node will not be able to determine the list of topics that the node is subscribed to.
	// If an oracle is already set, a fatal error is logged.
	SetTopicOracle(topicOracle func()[]string)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network/p2p/p2pbuilder/gossipsub/gossipSubBuilder.go Outdated Show resolved Hide resolved
network/p2p/p2pconf/gossipsub_rpc_inspectors.go Outdated Show resolved Hide resolved
network/p2p/p2pconf/gossipsub_rpc_inspectors.go Outdated Show resolved Hide resolved
utils/unittest/fixtures.go Outdated Show resolved Hide resolved
kc1116 and others added 8 commits October 5, 2023 19:34
…spector_test.go

Co-authored-by: Misha <15269764+gomisha@users.noreply.github.com>
…spector_test.go

Co-authored-by: Misha <15269764+gomisha@users.noreply.github.com>
…spector_test.go

Co-authored-by: Misha <15269764+gomisha@users.noreply.github.com>
…spector_test.go

Co-authored-by: Misha <15269764+gomisha@users.noreply.github.com>
….com:onflow/flow-go into khalil/6798-gossiping-unknown-invalid-topics
@yhassanzadeh13 yhassanzadeh13 changed the title Khalil/6798 RPC Publish Messages Validation [Networking] GossipSub Publish Message Validation (Unknown Topic Spam) Oct 20, 2023
Copy link
Contributor

@yhassanzadeh13 yhassanzadeh13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good, please address this comment before merging.

Copy link
Contributor

@gomisha gomisha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing all my comments.

@kc1116 kc1116 added this pull request to the merge queue Oct 31, 2023
Merged via the queue into master with commit 6ac44f5 Oct 31, 2023
36 checks passed
@kc1116 kc1116 deleted the khalil/6798-gossiping-unknown-invalid-topics branch October 31, 2023 01:41
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants