From 32ca0fba5108d0dc2c7415f36e55f031d5a0562e Mon Sep 17 00:00:00 2001 From: bndw Date: Sat, 14 Feb 2026 21:53:14 -0800 Subject: feat: add rate limiting to WebSocket connections WebSocket clients were completely unprotected from abuse. Add RateLimiter interface to WebSocket handler and enforce limits on EVENT and REQ messages. - Add RateLimiter interface with Allow(identifier, method) method - Track client IP in connState (proxy-aware via X-Forwarded-For) - Check rate limits in handleEvent and handleReq - Use authenticated pubkey as identifier, fallback to IP - Share same rate limiter instance with gRPC - Add getClientIP() helper that checks proxy headers first Critical security fix for production deployment. Without this, any client could spam unlimited events/subscriptions via WebSocket. --- cmd/relay/main.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'cmd') diff --git a/cmd/relay/main.go b/cmd/relay/main.go index e4afec8..86a29cb 100644 --- a/cmd/relay/main.go +++ b/cmd/relay/main.go @@ -79,8 +79,9 @@ func main() { streamInterceptors = append(streamInterceptors, auth.NostrStreamInterceptor(authOpts)) } + var limiter *ratelimit.Limiter if cfg.RateLimit.Enabled { - limiter := ratelimit.New(cfg.RateLimit.ToRateLimiter()) + limiter = ratelimit.New(cfg.RateLimit.ToRateLimiter()) unaryInterceptors = append(unaryInterceptors, ratelimit.UnaryInterceptor(limiter)) streamInterceptors = append(streamInterceptors, ratelimit.StreamInterceptor(limiter)) } @@ -125,6 +126,10 @@ func main() { }) } + if limiter != nil { + wsHandler.SetRateLimiter(limiter) + } + var grpcDisplay, httpDisplay, wsDisplay string if cfg.Server.PublicURL != "" { grpcDisplay = cfg.Server.PublicURL + ":443" -- cgit v1.2.3