security: harden gRPC server — disable by default, bind to localhost#2078
Conversation
- Add 'Enabled' field to GRPCConfig (default: false), requiring explicit --grpc.enabled flag to start the gRPC server - Change default bind address from 0.0.0.0:3131 to 127.0.0.1:3131 - Guard gRPC server startup with Enabled check in NewServer() - Add --grpc.enabled CLI flag in flags.go - Fix GetGrpcAddr() to use net.SplitHostPort instead of string slice - Update docs and test helper for compatibility The gRPC server currently starts unconditionally on all interfaces (0.0.0.0:3131) with no authentication, no TLS, and no way to disable it. This is inconsistent with HTTP-RPC and WS-RPC which are disabled by default. An attacker with network access can invoke sensitive RPCs including ChainSetHead (reorg), PeersAdd/Remove (eclipse attacks), and StatusBorStatus (reconnaissance) without credentials.
|
|
This PR is stale because it has been open 21 days with no activity. Remove stale label or comment or this will be closed in 14 days. |
manav2401
left a comment
There was a problem hiding this comment.
Hi, thanks for the PR. Overall it makes sense but it changes the default behavior for node operators.
What I'd suggest is that
- Have a flag called
grpc.disabledto disable it instead and keep it enabled by default. - Default addr is changed to "127.0.0.1:3131" which anyways prevents public exposure to the cli.
- As this is only used for cli, maybe worth adding a log suggesting that grpc is disabled.
- Updating tests according to the new changes.
- Ensure
GetGrpcAddrhas nil server checks and returns full "host:port" instead of just port. It's only used in tests which is easy to change.
Let me know if you have any concerns or suggestions. Thanks!
|
I've made some simplifications to get this merged. IMO, binding the grpc server to "127.0.0.1" should pretty much suffice and solve the core issue. I think changing the flag can be friction point for node operators relying on cli commands (served by grpc). |
|
A config related unit test is failing |
|



Summary
The gRPC server in Bor currently starts unconditionally on all network interfaces (
0.0.0.0:3131) with:This is inconsistent with HTTP-RPC and WS-RPC, which are disabled by default and bind to
localhost. Any node with port 3131 reachable from the network exposes sensitive RPCs without credentials.Impact
An unauthenticated attacker with network access to port 3131 can:
ChainSetHead(number)PeersAdd(enode)/PeersRemove(enode)PeersList/PeersStatusStatusBorStatusDebugPprofThe most significant risk is Denial of Service: repeatedly calling
ChainSetHead(0)forces the node to re-sync from genesis, effectively taking it offline. While this cannot steal funds or halt the Polygon network (requires multiple validators), it can degrade individual node availability.CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:H → 8.6 High
Changes
internal/cli/server/config.goEnabled boolfield toGRPCConfig; default tofalse; change bind address from:3131to127.0.0.1:3131internal/cli/server/server.goconfig.GRPC.Enabledcheck; fixGetGrpcAddr()to usenet.SplitHostPortinstead of brittle[1:]string sliceinternal/cli/server/flags.go--grpc.enabledCLI boolean flaginternal/cli/server/helper.goconfig.GRPC.Enabled = truein test mock so existing tests passdocs/cli/server.md--grpc.enabledflag and updated default addressBehavior Change
--grpc.enabled0.0.0.0:3131(all interfaces)127.0.0.1:3131(localhost only)--grpc.enabledThis is a breaking change for anyone relying on the gRPC server being on by default. Operators who need gRPC must now explicitly pass
--grpc.enabled. This is the same pattern used by--httpand--wsand is the secure default.How to Test
For questions or discussion, feel free to reach out: hi@harshinsecurity.in