From 6dad11c7946ba14a3fcc9cdf80f58c9f4b9bd4fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Fri, 19 May 2023 18:45:42 +0200 Subject: [PATCH 1/5] fix: udpate docs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e411ad2..2088e9c 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ vim .env # edit your config + `HOST`: (default: "localhost:3000") Host the app should listen on + `PORT`: (default: 3000) Port the app should listen on + `DEFAULT_RATE_LIMIT`: (default: 10) Requests per second rate limit -+ `STRICT_RATE_LIMIT`: (default: 10) Requests per burst rate limit (e.g. 1 request each 10 seconds) -+ `BURST_RATE_LIMIT`: (default: 1) Rate limit burst ++ `STRICT_RATE_LIMIT`: (default: 10) Requests per second rate limit for resource-intensive APIs (e.g. sending a payment) ++ `BURST_RATE_LIMIT`: (default: 1) Specifies the maximum number of requests that can pass at the same moment + `ENABLE_PROMETHEUS`: (default: false) Enable Prometheus metrics to be exposed + `PROMETHEUS_PORT`: (default: 9092) Prometheus port (path: `/metrics`) + `WEBHOOK_URL`: Optional. Callback URL for incoming and outgoing payment events, see below. From 797fbf438fca3b9d4e729740a1e242029a36f31e Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Fri, 19 May 2023 18:47:44 +0200 Subject: [PATCH 2/5] Unify rate limit settings We always treat the rate limit settings as requests per second and not every second. The default rate limit is set globally additionally we set a strict rate limit for sending payments --- main.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index 9a91f0e..0b26d7b 100644 --- a/main.go +++ b/main.go @@ -125,7 +125,8 @@ func main() { } e.Use(middleware.Recover()) e.Use(middleware.BodyLimit("250K")) - e.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(20))) + // set the default rate limit defining the overal max requests/second + e.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(rate.Limit(c.DefaultRateLimit)))) e.Logger = logger e.Use(middleware.RequestID()) @@ -184,8 +185,9 @@ func main() { InvoicePubSub: service.NewPubsub(), } + // strict rate limit for requests for sending payments strictRateLimitMiddleware := createRateLimitMiddleware(c.StrictRateLimit, c.BurstRateLimit) - secured := e.Group("", tokens.Middleware(c.JWTSecret), middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(rate.Limit(c.DefaultRateLimit)))) + secured := e.Group("", tokens.Middleware(c.JWTSecret)) securedWithStrictRateLimit := e.Group("", tokens.Middleware(c.JWTSecret), strictRateLimitMiddleware) RegisterLegacyEndpoints(svc, e, secured, securedWithStrictRateLimit, strictRateLimitMiddleware, tokens.AdminTokenMiddleware(c.AdminToken)) @@ -304,9 +306,9 @@ func main() { svc.Logger.Info("LNDhub exiting gracefully. Goodbye.") } -func createRateLimitMiddleware(seconds int, burst int) echo.MiddlewareFunc { +func createRateLimitMiddleware(requestsPerSecond int, burst int) echo.MiddlewareFunc { config := middleware.RateLimiterMemoryStoreConfig{ - Rate: rate.Every(time.Duration(seconds) * time.Second), + Rate: rate.Limit(requestsPerSecond), Burst: burst, } return middleware.RateLimiter(middleware.NewRateLimiterMemoryStoreWithConfig(config)) From 13a6c82f5b4963f367951dcec1ae87788a4efde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Fri, 19 May 2023 19:04:12 +0200 Subject: [PATCH 3/5] fix: identify users by id (if available) --- main.go | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index 0b26d7b..1952bd4 100644 --- a/main.go +++ b/main.go @@ -307,11 +307,26 @@ func main() { } func createRateLimitMiddleware(requestsPerSecond int, burst int) echo.MiddlewareFunc { - config := middleware.RateLimiterMemoryStoreConfig{ - Rate: rate.Limit(requestsPerSecond), - Burst: burst, + config := middleware.RateLimiterConfig{ + Store: middleware.NewRateLimiterMemoryStoreWithConfig( + middleware.RateLimiterMemoryStoreConfig{Rate: rate.Limit(requestsPerSecond), Burst: burst}, + ), + IdentifierExtractor: func(ctx echo.Context) (string, error) { + userId := ctx.Get("UserID").(string) + var id string + if userId != "" { + id = userId + } else { + id = ctx.RealIP() + } + + fmt.Printf("Current ID for rate limiting %v\n", id) + + return id, nil + }, } - return middleware.RateLimiter(middleware.NewRateLimiterMemoryStoreWithConfig(config)) + + return middleware.RateLimiterWithConfig(config) } func createCacheClient() *cache.Client { From 330165d8af810a736e27f3786ecc0aa350e4295f Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Fri, 19 May 2023 19:21:37 +0200 Subject: [PATCH 4/5] Set rate limit per user and fallback to IP if no user is available --- main.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 1952bd4..57bf68d 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "os/signal" + "strconv" "sync" "time" @@ -312,12 +313,11 @@ func createRateLimitMiddleware(requestsPerSecond int, burst int) echo.Middleware middleware.RateLimiterMemoryStoreConfig{Rate: rate.Limit(requestsPerSecond), Burst: burst}, ), IdentifierExtractor: func(ctx echo.Context) (string, error) { - userId := ctx.Get("UserID").(string) - var id string - if userId != "" { - id = userId - } else { - id = ctx.RealIP() + userId := ctx.Get("UserID") + id := ctx.RealIP() + if userId != nil { + userIdAsInt64 := ctx.Get("UserID").(int64) + id = strconv.FormatInt(userIdAsInt64, 10) } fmt.Printf("Current ID for rate limiting %v\n", id) From ce6746999a5bdccbfb3a2665004d7b711b76f668 Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Tue, 23 May 2023 09:30:13 +0200 Subject: [PATCH 5/5] Remove debug log --- main.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/main.go b/main.go index 57bf68d..8efa78a 100644 --- a/main.go +++ b/main.go @@ -320,8 +320,6 @@ func createRateLimitMiddleware(requestsPerSecond int, burst int) echo.Middleware id = strconv.FormatInt(userIdAsInt64, 10) } - fmt.Printf("Current ID for rate limiting %v\n", id) - return id, nil }, }