Skip to content

fix: limit incoming gRPC connections - #19

Open
GuipaiQigong111 wants to merge 2 commits into
xxo1shine:fix/discard-unknown-fields-for-p2p-messagesfrom
GuipaiQigong111:codex/limit-grpc-connections
Open

fix: limit incoming gRPC connections#19
GuipaiQigong111 wants to merge 2 commits into
xxo1shine:fix/discard-unknown-fields-for-p2p-messagesfrom
GuipaiQigong111:codex/limit-grpc-connections

Conversation

@GuipaiQigong111

@GuipaiQigong111 GuipaiQigong111 commented Jul 30, 2026

Copy link
Copy Markdown

Summary

  • add a JVM-wide active gRPC connection limiter shared by the FullNode, Solidity, and PBFT RPC ports
  • enforce both a global connection cap and a per-source-IP cap before HTTP/2 request processing
  • release permits from the Netty channel close future, including idempotent release handling
  • add validated configuration with secure defaults (maxConnections = 512, maxConnectionsPerIp = 32)
  • document multi-port behavior and the reverse-proxy source-address caveat

Security rationale

The existing HTTP/2 stream cap limits concurrent streams on one connection, but it does not bound an attacker opening many connections and a small number of streams on each. That pattern can consume the process file-descriptor budget when ulimit -n is low.

This change adds application-level admission control as defense in depth:

  • the global cap bounds active gRPC sockets across all three RPC servers, instead of applying a separate budget to every port
  • the per-IP cap prevents one directly connected source from consuming the whole global budget
  • an over-limit connection is closed before the delegated gRPC/HTTP2 handler is installed
  • closing an admitted channel immediately returns both global and per-IP capacity

Configuration

node.rpc.maxConnections = 512
node.rpc.maxConnectionsPerIp = 32

Both values must be non-negative, and the per-IP limit must not exceed the global limit. A value of 0 selects the secure default rather than disabling the protection. Operators behind a reverse proxy should enforce the client-IP limit at the proxy, because java-tron sees the proxy source address unless the original source address is preserved at the network layer.

Validation

Passed:

  • :common:compileJava, :framework:compileJava, and :framework:compileTestJava
  • NodeConfigTest, including defaults, zero fallback, invalid values, and explicit overrides
  • GrpcConnectionLimiterTest, including global/per-IP limits, idempotent release, replacement admission, and 64-way concurrent admission against a cap of 8
  • GrpcNettyMaxConcurrentStreamsLimiterTest
  • RpcServiceHttp2SecurityTest, including real TCP/HTTP2 connections, immediate rejection, permit release, replacement admission, and one shared limit across two live RPC servers
  • ArgsTest
  • JsonFormatTest, including strict-JSON round trips for chain-controlled name/URL control characters, backslash/quote escaping, and invalid UTF-8 fallback
  • :framework:checkstyleMain and :framework:checkstyleTest
  • git diff --check

A full :common:test :framework:test sweep was also started. It reached the large RocksDB/Spring integration-test section, but the host temporary volume became full and RocksDB began failing with No space left on device, so the sweep was stopped. Before disk exhaustion, the failures reported were in unchanged Jetty/VM tests (SizeLimitHandlerTest, AllowTvmLondonTest, and ValidateMultiSignContractTest); none of the changed gRPC/configuration tests failed.

An expanded org.tron.core.services.http.* sweep was attempted after the JSON review fix. It passed the serializer tests and many servlet tests, but the same nearly-full host volume eventually prevented Gradle and RocksDB from writing test results. The focused JsonFormatTest suite was then rerun independently and passed in full.

Review follow-up: HTTP JSON byte-field escaping

The self-type HTTP formatter previously escaped only double quotes for name-like bytes fields and then validated the result with a deliberately permissive JSON parser. Raw control characters could therefore reach HTTP JSON responses, while invalid UTF-8 was decoded with the platform default replacement behavior.

The follow-up commit now:

  • validates UTF-8 before rendering chain-controlled name, URL, and description bytes as text
  • applies the existing complete JSON string escaping for control characters, backslashes, and quotes
  • falls back to the original hexadecimal representation for invalid UTF-8
  • removes the permissive parse-as-validation path

Scope and operational note

This is application-level connection admission, not a replacement for OS and edge controls. A TCP socket is necessarily accepted before the child channel can be rejected, so connection-rate floods should still be handled with an appropriate ulimit, firewall/load-balancer connection limits, backlog tuning, and monitoring.

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.

2 participants