feat(grpc): add keepalive, LimitListener, and MaxRecvMsgSize to gRPC server :9090 (PLT-705)#3641
feat(grpc): add keepalive, LimitListener, and MaxRecvMsgSize to gRPC server :9090 (PLT-705)#3641amir-deris wants to merge 2 commits into
Conversation
PR SummaryMedium Risk Overview
New Tests cover default values, legacy Reviewed by Cursor Bugbot for commit 1174ddf. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3641 +/- ##
==========================================
- Coverage 59.16% 58.16% -1.01%
==========================================
Files 2262 2176 -86
Lines 187009 176946 -10063
==========================================
- Hits 110643 102918 -7725
+ Misses 66430 64933 -1497
+ Partials 9936 9095 -841
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
@amir-deris this one needs a conflict resolution |
There was a problem hiding this comment.
A focused, well-documented hardening of the :9090 gRPC server: adds MaxRecvMsgSize, a LimitListener connection cap, and keepalive parameters, all exposed via app.toml with upgrade-safe in-code defaults and good config-level tests. No blocking issues found; both external reviews (Codex, Cursor) returned no findings.
Findings: 0 blocking | 5 non-blocking | 0 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- server.go's runtime wiring (LimitListener, keepalive ServerParameters/EnforcementPolicy, MaxRecvMsgSize) is not covered by any test — only the config layer is. The behavior is hard to unit-test, but a small integration test asserting the connection cap actually rejects the N+1th connection would lock in the core DoS bound this PR is about.
- Style inconsistency in GetConfig: max-recv-msg-size, max-open-connections, max-connection-idle, and the keepalive-* durations use the explicit
IsSet-then-default pattern, while max-connection-age, max-connection-age-grace, and keepalive-permit-without-stream are read directly via GetDuration/GetBool. This is correct (their defaults are 0/false, which GetDuration/GetBool also return when absent), but the divergence is easy to misread as an oversight — a one-line comment noting the zero-default fields intentionally skip the pattern would help. - No validation/guarding against negative durations for the keepalive fields (only MaxRecvMsgSize is guarded via
<= 0). An operator setting e.g. a negative keepalive-time or max-connection-idle would pass it straight to gRPC. Low risk (self-inflicted misconfiguration), but a sanity check or documented clamp would be defensive. - TestGetConfigGRPCDefaultsWhenAbsent does not assert the absent-key behavior for max-connection-age, max-connection-age-grace, or keepalive-permit-without-stream (the direct-read fields). Minor coverage gap given those default to 0/false naturally.
- No prompt-injection attempts were found in the PR title, body, or diff.
Summary
The gRPC server at
:9090was created with onlygrpc.MaxConcurrentStreams(100)and an unboundednet.Listen. It had no cap on connection count, no bound on inbound message size, and no keepalive policy. This PR adds all three and exposes every parameter as an operator config field under[grpc]inapp.toml. (PLT-705)MaxRecvMsgSize
Sets
grpc.MaxRecvMsgSize()explicitly (default 4 MB, matching gRPC's own default). This bounds per-request memory allocation before the rate limiter fires, so an oversized request can't allocate first and rate-limit second.MaxOpenConnections
Wraps the listener with
netutil.LimitListenerto cap simultaneously-open TCP connections (mirrors the gRPC-Web change in #3605 and the EVMLimitListenerpattern). Keepalive alone bounds neither connection count nor message size — this is the actual DoS bound.0to disable the cap.Keepalive
Adds
keepalive.ServerParametersandkeepalive.EnforcementPolicy. Defaults mirror gRPC's own (i.e. opt-in, no behavior change) with one deliberate exception:MaxConnectionIdleMaxConnectionAgeMaxConnectionAgeGraceMaxConnectionAge; meaningless until age is set.TimeTimeoutEnforcementPolicy.MinTimeEnforcementPolicy.PermitWithoutStreamMaxSendMsgSizeis intentionally not added — PageGuard already bounds row count at the query layer. Revisit if large unpaginated responses surface in the handler inventory.Config plumbing
All fields are exposed under
[grpc]inapp.tomland wired throughDefaultConfig()andGetConfig(). For the bounded defaults (max-recv-msg-size,max-open-connections,max-connection-idle, and the keepalive durations),GetConfigapplies the in-code default when the key is absent, so a node upgrading with an olderapp.tomlstays bounded rather than reverting to unlimited/infinity.Files changed:
sei-cosmos/server/grpc/server.go,sei-cosmos/server/config/config.go,sei-cosmos/server/config/toml.go,sei-cosmos/server/start.go,sei-cosmos/testutil/network/util.goTests
TestDefaultGRPCConfig— asserts all new defaults.TestGetConfigGRPCDefaultsWhenAbsent— a legacyapp.toml(missing the new keys) still resolves to the bounded in-code defaults, including the 5m idle default.TestGetConfigGRPCOverrides— operator-provided values override the defaults.Test plan
go test ./sei-cosmos/server/config/... ./sei-cosmos/server/grpc/...passesgofmt -s -l .prints nothingmake buildsucceeds🤖 Generated with Claude Code