summaryrefslogtreecommitdiffstats
path: root/cmd/relay/main.go
diff options
context:
space:
mode:
authorbndw <ben@bdw.to>2026-02-14 21:53:14 -0800
committerbndw <ben@bdw.to>2026-02-14 21:53:14 -0800
commit32ca0fba5108d0dc2c7415f36e55f031d5a0562e (patch)
treeff91309ef9af7d0ec8c9b5bd5b6f39f073d4be55 /cmd/relay/main.go
parente647880669b79cd968231cf85dc037a18e8bfd9c (diff)
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.
Diffstat (limited to 'cmd/relay/main.go')
-rw-r--r--cmd/relay/main.go7
1 files changed, 6 insertions, 1 deletions
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() {
79 streamInterceptors = append(streamInterceptors, auth.NostrStreamInterceptor(authOpts)) 79 streamInterceptors = append(streamInterceptors, auth.NostrStreamInterceptor(authOpts))
80 } 80 }
81 81
82 var limiter *ratelimit.Limiter
82 if cfg.RateLimit.Enabled { 83 if cfg.RateLimit.Enabled {
83 limiter := ratelimit.New(cfg.RateLimit.ToRateLimiter()) 84 limiter = ratelimit.New(cfg.RateLimit.ToRateLimiter())
84 unaryInterceptors = append(unaryInterceptors, ratelimit.UnaryInterceptor(limiter)) 85 unaryInterceptors = append(unaryInterceptors, ratelimit.UnaryInterceptor(limiter))
85 streamInterceptors = append(streamInterceptors, ratelimit.StreamInterceptor(limiter)) 86 streamInterceptors = append(streamInterceptors, ratelimit.StreamInterceptor(limiter))
86 } 87 }
@@ -125,6 +126,10 @@ func main() {
125 }) 126 })
126 } 127 }
127 128
129 if limiter != nil {
130 wsHandler.SetRateLimiter(limiter)
131 }
132
128 var grpcDisplay, httpDisplay, wsDisplay string 133 var grpcDisplay, httpDisplay, wsDisplay string
129 if cfg.Server.PublicURL != "" { 134 if cfg.Server.PublicURL != "" {
130 grpcDisplay = cfg.Server.PublicURL + ":443" 135 grpcDisplay = cfg.Server.PublicURL + ":443"