From a824d72a1a544ebcec655501309fcefc259307bb Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 18 Jun 2024 12:09:29 -0500 Subject: [PATCH 1/5] Add IP rate limiter --- deno.json | 1 + src/app.ts | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/deno.json b/deno.json index 7e9bf6c4..2ab24847 100644 --- a/deno.json +++ b/deno.json @@ -44,6 +44,7 @@ "entities": "npm:entities@^4.5.0", "fast-stable-stringify": "npm:fast-stable-stringify@^1.0.0", "formdata-helper": "npm:formdata-helper@^0.3.0", + "hono-rate-limiter": "npm:hono-rate-limiter@^0.3.0", "iso-639-1": "npm:iso-639-1@2.1.15", "isomorphic-dompurify": "npm:isomorphic-dompurify@^2.11.0", "kysely": "npm:kysely@^0.27.3", diff --git a/src/app.ts b/src/app.ts index 912001ac..7d77c6b3 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,13 +1,15 @@ import { Context, Env as HonoEnv, Handler, Hono, Input as HonoInput, MiddlewareHandler } from '@hono/hono'; import { cors } from '@hono/hono/cors'; -import { serveStatic } from '@hono/hono/deno'; +import { getConnInfo, serveStatic } from '@hono/hono/deno'; import { logger } from '@hono/hono/logger'; import { NostrEvent, NostrSigner, NStore, NUploader } from '@nostrify/nostrify'; import Debug from '@soapbox/stickynotes/debug'; +import { rateLimiter } from 'hono-rate-limiter'; import { Conf } from '@/config.ts'; import { cron } from '@/cron.ts'; import { startFirehose } from '@/firehose.ts'; +import { Time } from '@/utils/time.ts'; import { accountController, @@ -145,6 +147,16 @@ if (Conf.cronEnabled) { cron(); } +// @ts-ignore Mismatched Hono versions. +const limiter: MiddlewareHandler = rateLimiter({ + limit: 300, + windowMs: Time.minutes(5), + // @ts-ignore Mismatched Hono versions. + keyGenerator: (c) => getConnInfo(c).remote.address!, +}); + +app.use('*', limiter); + app.use('/api/*', logger(debug)); app.use('/.well-known/*', logger(debug)); app.use('/users/*', logger(debug)); From 03fc6e777cba9b5b18e814741cc6e1332e942b58 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 18 Jun 2024 13:38:17 -0500 Subject: [PATCH 2/5] nginx: add more proxy headers --- installation/ditto.conf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/installation/ditto.conf b/installation/ditto.conf index 773573de..d74a8865 100644 --- a/installation/ditto.conf +++ b/installation/ditto.conf @@ -24,8 +24,10 @@ server { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; - proxy_set_header Host $http_host; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; root /opt/ditto/public; From 72f5391f97ab3f46d7e71ba03da28c3b171b3396 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 18 Jun 2024 13:38:56 -0500 Subject: [PATCH 3/5] Update deno.lock --- deno.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/deno.lock b/deno.lock index 67e4b7e7..b3a27ff0 100644 --- a/deno.lock +++ b/deno.lock @@ -1395,6 +1395,7 @@ "npm:entities@^4.5.0", "npm:fast-stable-stringify@^1.0.0", "npm:formdata-helper@^0.3.0", + "npm:hono-rate-limiter@^0.3.0", "npm:iso-639-1@2.1.15", "npm:isomorphic-dompurify@^2.11.0", "npm:kysely@^0.27.3", From 330b38ff6891d03059a178ad4ebb720fdb67c807 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 18 Jun 2024 14:28:13 -0500 Subject: [PATCH 4/5] Move rateLimiterMiddleware to a separate file --- src/app.ts | 18 +++++------------- src/middleware/rateLimitMiddleware.ts | 12 ++++++++++++ 2 files changed, 17 insertions(+), 13 deletions(-) create mode 100644 src/middleware/rateLimitMiddleware.ts diff --git a/src/app.ts b/src/app.ts index 7d77c6b3..0c21ecf0 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,10 +1,9 @@ import { Context, Env as HonoEnv, Handler, Hono, Input as HonoInput, MiddlewareHandler } from '@hono/hono'; import { cors } from '@hono/hono/cors'; -import { getConnInfo, serveStatic } from '@hono/hono/deno'; +import { serveStatic } from '@hono/hono/deno'; import { logger } from '@hono/hono/logger'; import { NostrEvent, NostrSigner, NStore, NUploader } from '@nostrify/nostrify'; import Debug from '@soapbox/stickynotes/debug'; -import { rateLimiter } from 'hono-rate-limiter'; import { Conf } from '@/config.ts'; import { cron } from '@/cron.ts'; @@ -16,6 +15,7 @@ import { accountLookupController, accountSearchController, accountStatusesController, + blockController, createAccountController, familiarFollowersController, favouritesController, @@ -24,6 +24,7 @@ import { followingController, muteController, relationshipsController, + unblockController, unfollowController, unmuteController, updateCredentialsController, @@ -112,11 +113,10 @@ import { nodeInfoController, nodeInfoSchemaController } from '@/controllers/well import { nostrController } from '@/controllers/well-known/nostr.ts'; import { auth98Middleware, requireProof, requireRole } from '@/middleware/auth98Middleware.ts'; import { cspMiddleware } from '@/middleware/cspMiddleware.ts'; +import { rateLimitMiddleware } from '@/middleware/rateLimitMiddleware.ts'; import { requireSigner } from '@/middleware/requireSigner.ts'; import { signerMiddleware } from '@/middleware/signerMiddleware.ts'; import { storeMiddleware } from '@/middleware/storeMiddleware.ts'; -import { blockController } from '@/controllers/api/accounts.ts'; -import { unblockController } from '@/controllers/api/accounts.ts'; import { uploaderMiddleware } from '@/middleware/uploaderMiddleware.ts'; interface AppEnv extends HonoEnv { @@ -147,15 +147,7 @@ if (Conf.cronEnabled) { cron(); } -// @ts-ignore Mismatched Hono versions. -const limiter: MiddlewareHandler = rateLimiter({ - limit: 300, - windowMs: Time.minutes(5), - // @ts-ignore Mismatched Hono versions. - keyGenerator: (c) => getConnInfo(c).remote.address!, -}); - -app.use('*', limiter); +app.use('*', rateLimitMiddleware(300, Time.minutes(5))); app.use('/api/*', logger(debug)); app.use('/.well-known/*', logger(debug)); diff --git a/src/middleware/rateLimitMiddleware.ts b/src/middleware/rateLimitMiddleware.ts new file mode 100644 index 00000000..9aeef34b --- /dev/null +++ b/src/middleware/rateLimitMiddleware.ts @@ -0,0 +1,12 @@ +import { MiddlewareHandler } from '@hono/hono'; +import { rateLimiter } from 'hono-rate-limiter'; + +/** Rate limit middleware for Hono. */ +export function rateLimitMiddleware(limit: number, windowMs: number): MiddlewareHandler { + return rateLimiter({ + limit, + windowMs, + skip: (c) => !c.req.header('x-real-ip'), + keyGenerator: (c) => c.req.header('x-real-ip')!, + }); +} From 0d3c6192459d17354f7b45b33c0c4f21ced5c8d0 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 18 Jun 2024 15:24:44 -0500 Subject: [PATCH 5/5] rateLimitMiddleware: improve tsdoc comment --- src/middleware/rateLimitMiddleware.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/middleware/rateLimitMiddleware.ts b/src/middleware/rateLimitMiddleware.ts index 9aeef34b..689f7cee 100644 --- a/src/middleware/rateLimitMiddleware.ts +++ b/src/middleware/rateLimitMiddleware.ts @@ -1,8 +1,11 @@ import { MiddlewareHandler } from '@hono/hono'; import { rateLimiter } from 'hono-rate-limiter'; -/** Rate limit middleware for Hono. */ +/** + * Rate limit middleware for Hono, based on [`hono-rate-limiter`](https://github.com/rhinobase/hono-rate-limiter). + */ export function rateLimitMiddleware(limit: number, windowMs: number): MiddlewareHandler { + // @ts-ignore Mismatched hono versions. return rateLimiter({ limit, windowMs,