Skip to content

[Networking] Add unicast stream pre-authorization by sender/receiver role#8524

Merged
peterargue merged 10 commits into
leo/228-networkingfrom
leo/228-networking-unicast-authorization
Apr 8, 2026
Merged

[Networking] Add unicast stream pre-authorization by sender/receiver role#8524
peterargue merged 10 commits into
leo/228-networkingfrom
leo/228-networking-unicast-authorization

Conversation

@zhangchiqing
Copy link
Copy Markdown
Member

@zhangchiqing zhangchiqing commented Apr 7, 2026

Currently, the networking layer will perform authorization of unicast messages only after fully receiving the message and parsing it's type. This mean unauthorized peers are able to tie up resources of peers that they have no business sending unicast messages to.

This PR adds an initial coarse grained authorization based on role before reading any data from the stream.

https://github.com/onflow/flow-go-internal/pull/7178

Summary by CodeRabbit

Release Notes

  • New Features

    • Added role-based authorization for unicast network messages, controlling which node roles can communicate with each other.
    • Enhanced node role identification across network components.
  • Tests

    • Added comprehensive test coverage for unicast sender authorization validation and cross-validation scenarios.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 2089dae.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 7, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b59eaa4b-0ff7-40fa-9f6d-eb43c0ca6dde

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces role-based unicast stream authorization to the network layer. The module.Local interface gains a Role() method, implemented across local node and mock implementations. A new IsAuthorizedUnicastSender() function defines allowed sender-receiver role pairs. Network streams now validate sender/receiver roles before accepting connections.

Changes

Cohort / File(s) Summary
Documentation
AGENTS.md
Added abbreviations section defining short forms for node roles (AN, LN, SN, EN, VN, ON).
Interface & Role Method
module/local.go, module/local/me.go, module/local/me_nokey.go, engine/testutil/mocklocal/local.go, module/mock/local.go
Added Role() flow.Role method to module.Local interface and implemented it across concrete and mock implementations. MockLocal now stores and exposes role (defaults to RoleConsensus).
Unicast Authorization
network/message/authorization.go, network/message/authorization_test.go
Introduced IsAuthorizedUnicastSender() function and unicastRoleAuthorization map defining allowed sender→receiver role pairs. Added comprehensive tests including cross-validation against message auth configs.
Network Stream Handling
network/underlay/network.go
Added unicastStreamAuthorizer callback to validate incoming unicast streams by sender/receiver roles. Integrated early peer identity resolution, authorization checks before reading stream data, and role-based timeout/message-size selection (large thresholds for verification↔execution). Added ErrUnauthorizedUnicastSender error constant.
Test Utilities & Integration Tests
network/internal/testutils/testUtil.go, network/test/cohort1/network_test.go, network/test/cohort2/unicast_authorization_test.go
Updated network config fixture to support Role() calls and explicit unicast authorizer. Modified TestLargeMessageSize_SendDirect to use an explicit Verification Node. Added TestUnicastAuthorization_UnauthorizedSenderRole to verify rejection of unauthorized sender roles.

Sequence Diagram

sequenceDiagram
    actor Sender as Sender<br/>(with role)
    participant NetLayer as Network Layer<br/>(Receiver)
    participant Auth as Authorization<br/>Service
    actor Receiver as Receiver<br/>(with role)

    Sender->>NetLayer: Initiate unicast stream
    NetLayer->>NetLayer: Accept incoming stream
    NetLayer->>NetLayer: Resolve remote peer identity
    NetLayer->>Auth: IsAuthorizedUnicastSender<br/>(senderRole, receiverRole)
    
    alt Authorized
        Auth-->>NetLayer: true
        NetLayer->>NetLayer: Proceed with stream<br/>reading/handling
        NetLayer->>Receiver: ✓ Stream active
    else Unauthorized
        Auth-->>NetLayer: false
        NetLayer->>NetLayer: Log violation
        NetLayer->>Receiver: Report OnUnauthorizedUnicastOnChannel
        NetLayer->>Sender: ✗ Reject stream
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • jordanschalm
  • janezpodhostnik
  • vishalchangrani

Poem

🐰✨ With roles now flowing through the streams,
Each node checks who may venture nigh,
Authorization guards the network's dreams,
And keeps the streams secure and dry!
Hoppy reviewing, my dear network friends! 🌸

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately captures the main change: adding unicast stream pre-authorization based on sender/receiver roles to prevent unauthorized peers from consuming resources.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch leo/228-networking-unicast-authorization

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 7, 2026

Codecov Report

❌ Patch coverage is 10.84337% with 74 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
network/underlay/network.go 0.00% 39 Missing ⚠️
module/mock/local.go 0.00% 23 Missing ⚠️
engine/testutil/mocklocal/local.go 0.00% 6 Missing ⚠️
module/local/me.go 0.00% 2 Missing ⚠️
module/local/me_nokey.go 0.00% 2 Missing ⚠️
network/internal/testutils/testUtil.go 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@blacksmith-sh
Copy link
Copy Markdown

blacksmith-sh Bot commented Apr 7, 2026

Found 24 test failures on Blacksmith runners:

Failures

Test View Logs
github.com/onflow/flow-go/integration/tests/access/cohort3/TestConsensusFollower View Logs
github.com/onflow/flow-go/integration/tests/access/cohort3/TestConsensusFollower View Logs
github.com/onflow/flow-go/integration/tests/access/cohort3/TestConsensusFollower View Logs
github.com/onflow/flow-go/integration/tests/access/cohort3/TestConsensusFollower View Logs
github.com/onflow/flow-go/integration/tests/access/cohort3/TestConsensusFollower View Logs
github.com/onflow/flow-go/integration/tests/access/cohort3/TestConsensusFollower/
TestReceiveBlocks/consensus_follower_follows_the_chain
View Logs
github.com/onflow/flow-go/integration/tests/access/cohort3/TestConsensusFollower/
TestReceiveBlocks/consensus_follower_follows_the_chain
View Logs
github.com/onflow/flow-go/integration/tests/access/cohort3/TestConsensusFollower/
TestReceiveBlocks/consensus_follower_follows_the_chain
View Logs
github.com/onflow/flow-go/integration/tests/access/cohort3/TestConsensusFollower/
TestReceiveBlocks/consensus_follower_sync_up_with_the_chain
View Logs
github.com/onflow/flow-go/integration/tests/access/cohort3/TestConsensusFollower/
TestReceiveBlocks/consensus_follower_sync_up_with_the_chain
View Logs
github.com/onflow/flow-go/integration/tests/access/cohort4/TestExecutionDataPruning View Logs
github.com/onflow/flow-go/integration/tests/access/cohort4/TestExecutionDataPruning View Logs
github.com/onflow/flow-go/integration/tests/access/cohort4/TestExecutionDataPruning/
TestHappyPath
View Logs
github.com/onflow/flow-go/integration/tests/access/cohort4/TestExecutionDataPruning/
TestHappyPath
View Logs
github.com/onflow/flow-go/integration/tests/epochs/cohort1/TestDynamicBootstrapInEFM View Logs
github.com/onflow/flow-go/integration/tests/epochs/cohort1/TestDynamicBootstrapInEFM View Logs
github.com/onflow/flow-go/integration/tests/epochs/cohort1/TestDynamicBootstrapInEFM View Logs
github.com/onflow/flow-go/integration/tests/epochs/cohort1/TestDynamicBootstrapInEFM View Logs
github.com/onflow/flow-go/integration/tests/epochs/cohort1/TestDynamicBootstrapInEFM View Logs
github.com/onflow/flow-go/integration/tests/epochs/cohort1/TestDynamicBootstrapInEFM/
TestDynamicBootstrapInEFM
View Logs
github.com/onflow/flow-go/integration/tests/epochs/cohort1/TestDynamicBootstrapInEFM/
TestDynamicBootstrapInEFM
View Logs
github.com/onflow/flow-go/integration/tests/epochs/cohort1/TestDynamicBootstrapInEFM/
TestDynamicBootstrapInEFM
View Logs
github.com/onflow/flow-go/integration/tests/epochs/cohort1/TestDynamicBootstrapInEFM/
TestDynamicBootstrapInEFM
View Logs
github.com/onflow/flow-go/integration/tests/epochs/cohort1/TestDynamicBootstrapInEFM/
TestDynamicBootstrapInEFM
View Logs

Fix in Cursor

@zhangchiqing zhangchiqing marked this pull request as ready for review April 7, 2026 23:53
@zhangchiqing zhangchiqing requested a review from a team as a code owner April 7, 2026 23:53
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
AGENTS.md (1)

89-96: Use a markdown subsection heading for abbreviations.

Line 89 currently uses plain text (Abbreviations:). Consider ### Abbreviations to match the document’s heading structure and improve scanability.

Proposed doc-only tweak
-Abbreviations:
+### Abbreviations
 - **AN**: Access Node
 - **LN**: Collection Node
 - **SN**: Consensus Node
 - **EN**: Execution Node
 - **VN**: Verification Node
 - **ON**: Observer Node
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 89 - 96, Replace the plain text "Abbreviations:"
heading with a markdown subsection heading to match the document structure —
change the line containing Abbreviations: to a header like "### Abbreviations"
so the abbreviations block (AN, LN, SN, EN, VN, ON) is a proper subsection and
improves scanability.
engine/testutil/mocklocal/local.go (1)

23-29: Don't make RoleConsensus the implicit mock role.

RoleConsensus is the most permissive sender in the new unicast matrix. Making it the silent default means role-sensitive tests can pass without ever exercising the intended role. Prefer taking the role in NewMockLocal (or adding an explicit setter) so tests have to opt into the behavior they want.

Suggested change
-func NewMockLocal(sk crypto.PrivateKey, id flow.Identifier, t mock.TestingT) *MockLocal {
+func NewMockLocal(sk crypto.PrivateKey, id flow.Identifier, role flow.Role, t mock.TestingT) *MockLocal {
 	return &MockLocal{
 		sk:   sk,
 		t:    t,
 		id:   id,
-		role: flow.RoleConsensus,
+		role: role,
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@engine/testutil/mocklocal/local.go` around lines 23 - 29, NewMockLocal
currently sets MockLocal.role to flow.RoleConsensus by default which hides
role-sensitive bugs; change NewMockLocal to accept a role parameter (e.g.,
NewMockLocal(sk, id, t, role flow.Role) or add an explicit SetRole method on
MockLocal) and remove the implicit flow.RoleConsensus assignment in the
constructor, update all callers to pass the intended role (or call SetRole) so
tests must opt into permissive consensus behavior; ensure the MockLocal struct's
role field is initialized only from the provided argument or setter.
network/internal/testutils/testUtil.go (1)

222-222: Keep the production pre-authorizer as the fixture default.

Hardwiring an allow-all function here makes every test built on NetworkConfigFixture silently bypass the new stream gate, so regressions in the default authorization path will only show up if a caller explicitly overrides this back. Prefer leaving this unset in the shared fixture and only installing the permissive authorizer in the specific tests that need role-agnostic wiring.

Suggested change
-		UnicastStreamAuthorizer: func(_, _ flow.Role) bool { return true },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@network/internal/testutils/testUtil.go` at line 222, The fixture currently
hardwires an allow-all UnicastStreamAuthorizer (UnicastStreamAuthorizer: func(_,
_ flow.Role) bool { return true }) which bypasses the stream gate for every
test; remove or leave UnicastStreamAuthorizer unset/default in
NetworkConfigFixture so the production pre-authorizer is used by default, and
instead install the permissive authorizer only in individual tests that need it
(set the authorizer explicitly in those tests). Ensure references to
UnicastStreamAuthorizer in NetworkConfigFixture are cleared or nil so default
authorization behavior applies.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@network/underlay/network.go`:
- Around line 1001-1013: The code currently reports a channel-scoped violation
via n.slashingViolationsConsumer.OnUnauthorizedUnicastOnChannel when
n.unicastStreamAuthorizer rejects the stream before any channel/frame exists and
without setting stable identity fields; instead, call the
pre-auth/unauthorized-sender handler (e.g., a method like
OnUnauthorizedUnicastPreAuth or a non-channel-specific consumer API) or populate
the network.Violation with stable identity fields (set OriginID from
remoteIdentity.NodeID, fill Identity/PeerID/Protocol and Err with
ErrUnauthorizedUnicastSender) before emitting; update the call site that
currently invokes OnUnauthorizedUnicastOnChannel to use the appropriate pre-auth
consumer method or construct the full network.Violation including OriginID so
the slashing/audit path has a complete record.
- Around line 1017-1024: The receive-side timeout is hardcoded to
DefaultUnicastTimeout causing inbound deadlines to ignore the configured
NetworkConfig.UnicastMessageTimeout; update the code that sets unicastTimeout
(the variable used to create ctx via context.WithTimeout with n.ctx) to default
to the configured value (NetworkConfig.UnicastMessageTimeout on your node config
object) and only override it to LargeMsgUnicastTimeout when the
n.me.Role()/remoteIdentity.Role branch selects large messages, so inbound
deadlines match the configured unicast timeout used elsewhere.

---

Nitpick comments:
In `@AGENTS.md`:
- Around line 89-96: Replace the plain text "Abbreviations:" heading with a
markdown subsection heading to match the document structure — change the line
containing Abbreviations: to a header like "### Abbreviations" so the
abbreviations block (AN, LN, SN, EN, VN, ON) is a proper subsection and improves
scanability.

In `@engine/testutil/mocklocal/local.go`:
- Around line 23-29: NewMockLocal currently sets MockLocal.role to
flow.RoleConsensus by default which hides role-sensitive bugs; change
NewMockLocal to accept a role parameter (e.g., NewMockLocal(sk, id, t, role
flow.Role) or add an explicit SetRole method on MockLocal) and remove the
implicit flow.RoleConsensus assignment in the constructor, update all callers to
pass the intended role (or call SetRole) so tests must opt into permissive
consensus behavior; ensure the MockLocal struct's role field is initialized only
from the provided argument or setter.

In `@network/internal/testutils/testUtil.go`:
- Line 222: The fixture currently hardwires an allow-all UnicastStreamAuthorizer
(UnicastStreamAuthorizer: func(_, _ flow.Role) bool { return true }) which
bypasses the stream gate for every test; remove or leave UnicastStreamAuthorizer
unset/default in NetworkConfigFixture so the production pre-authorizer is used
by default, and instead install the permissive authorizer only in individual
tests that need it (set the authorizer explicitly in those tests). Ensure
references to UnicastStreamAuthorizer in NetworkConfigFixture are cleared or nil
so default authorization behavior applies.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c83a67b8-f420-4d5b-9b5c-a34080839445

📥 Commits

Reviewing files that changed from the base of the PR and between 7ce557d and 2089dae.

📒 Files selected for processing (12)
  • AGENTS.md
  • engine/testutil/mocklocal/local.go
  • module/local.go
  • module/local/me.go
  • module/local/me_nokey.go
  • module/mock/local.go
  • network/internal/testutils/testUtil.go
  • network/message/authorization.go
  • network/message/authorization_test.go
  • network/test/cohort1/network_test.go
  • network/test/cohort2/unicast_authorization_test.go
  • network/underlay/network.go

Comment on lines +1001 to +1013
if !n.unicastStreamAuthorizer(remoteIdentity.Role, n.me.Role()) {
log.Warn().
Str("remote_peer", remotePeer.String()).
Str("remote_role", remoteIdentity.Role.String()).
Str("local_role", n.me.Role().String()).
Bool(logging.KeySuspicious, true).
Msg("rejecting unicast stream from unauthorized sender role")
n.slashingViolationsConsumer.OnUnauthorizedUnicastOnChannel(&network.Violation{
Identity: remoteIdentity,
PeerID: p2plogging.PeerId(remotePeer),
Protocol: message.ProtocolTypeUnicast,
Err: ErrUnauthorizedUnicastSender,
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't report a channel-scoped violation before any channel exists.

This branch rejects the stream before a frame is read, so Channel and MsgType are unknowable here, but it still goes through OnUnauthorizedUnicastOnChannel. The emitted network.Violation also drops OriginID even though remoteIdentity.NodeID is available, which leaves the slashing/audit path with a partial record. Please route this through a pre-auth/unauthorized-sender violation instead, or at least populate the stable identity fields here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@network/underlay/network.go` around lines 1001 - 1013, The code currently
reports a channel-scoped violation via
n.slashingViolationsConsumer.OnUnauthorizedUnicastOnChannel when
n.unicastStreamAuthorizer rejects the stream before any channel/frame exists and
without setting stable identity fields; instead, call the
pre-auth/unauthorized-sender handler (e.g., a method like
OnUnauthorizedUnicastPreAuth or a non-channel-specific consumer API) or populate
the network.Violation with stable identity fields (set OriginID from
remoteIdentity.NodeID, fill Identity/PeerID/Protocol and Err with
ErrUnauthorizedUnicastSender) before emitting; update the call site that
currently invokes OnUnauthorizedUnicastOnChannel to use the appropriate pre-auth
consumer method or construct the full network.Violation including OriginID so
the slashing/audit path has a complete record.

Comment thread network/underlay/network.go
@peterargue peterargue changed the base branch from master to leo/228-networking April 8, 2026 17:08
…tion-public-network

Override role based unicast stream authorization on public network
@peterargue peterargue merged commit 7862800 into leo/228-networking Apr 8, 2026
@peterargue peterargue deleted the leo/228-networking-unicast-authorization branch April 8, 2026 21:19
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.

4 participants