Skip to content

feat(rpc): harden RPC server#4909

Open
Wondertan wants to merge 1 commit intomainfrom
feat/rpc-server-hardening
Open

feat(rpc): harden RPC server#4909
Wondertan wants to merge 1 commit intomainfrom
feat/rpc-server-hardening

Conversation

@Wondertan
Copy link
Copy Markdown
Member

@Wondertan Wondertan commented Apr 6, 2026

  • Set WithMaxRequestSize(5 MiB) on go-jsonrpc server (down from 100 MiB default)
  • Add http.Server timeouts: ReadTimeout, WriteTimeout, IdleTimeout, MaxHeaderBytes
  • Add per-IP rate limiting middleware (100 req/s sustained, 200 burst)
  • Add concurrent connection limiting middleware (500 max)

Those are not configurable atm and we could add them to configs if requested.


Open with Devin

@Wondertan Wondertan self-assigned this Apr 6, 2026
@Wondertan Wondertan requested a review from a team as a code owner April 6, 2026 14:52
@Wondertan Wondertan requested a review from vgonkivs April 6, 2026 14:52
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment on lines +40 to +52
go func() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
mu.Lock()
for ip, e := range limiters {
if time.Since(e.lastSeen) > 10*time.Minute {
delete(limiters, ip)
}
}
mu.Unlock()
}
}()
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot Apr 6, 2026

Choose a reason for hiding this comment

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

🟡 Goroutine leak: rate limiter cleanup goroutine cannot be stopped if Start() fails or is never called

The rateLimit function at api/rpc/middleware.go:42 spawns a background goroutine during NewServer (via newHandlerStack at api/rpc/server.go:122). The only way to stop this goroutine is by canceling the context, which happens in Stop() at api/rpc/server.go:214. However, Stop() guards cancelFunc() behind s.started.CompareAndSwap(true, false) — if Start() was never called or failed (e.g., port already in use at api/rpc/server.go:183-186), the started flag remains false, the CompareAndSwap fails, and the method returns early at line 212 without ever calling cancelFunc(). This leaks the background goroutine (and its ticker). In the fx lifecycle (nodebuilder/rpc/module.go:21-26), if Start fails, fx does not call Stop for that component, so the goroutine is never cleaned up.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed

- Set WithMaxRequestSize(5 MiB) on go-jsonrpc server (down from 100 MiB default)
- Add http.Server timeouts: ReadTimeout, WriteTimeout, IdleTimeout, MaxHeaderBytes
- Add per-IP rate limiting middleware (100 req/s sustained, 200 burst)
- Add concurrent connection limiting middleware (500 max)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Wondertan Wondertan force-pushed the feat/rpc-server-hardening branch from e3f7e12 to 2d78b9b Compare April 6, 2026 15:08
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.

1 participant