Add api rate limiter

pull/2260/head
Jérémy Rialland 2022-08-29 11:58:53 +02:00 committed by Jérémy Rialland
parent 28ddd61d8d
commit 4de5060002
4 changed files with 120 additions and 3 deletions

View File

@ -33,12 +33,14 @@
"@publicodes/api": "^1.0.0-beta.46",
"@sentry/node": "^7.1.1",
"@sentry/tracing": "^7.1.1",
"ioredis": "^5.2.3",
"koa": "^2.13.4",
"koa-body": "^5.0.0",
"koa-static": "^5.0.0",
"modele-social": "workspace:^",
"nodemon": "^2.0.16",
"publicodes": "^1.0.0-beta.46",
"rate-limiter-flexible": "^2.3.8",
"swagger-ui-dist": "^4.11.1"
},
"devDependencies": {

View File

@ -6,6 +6,7 @@ import rules from 'modele-social'
import Engine from 'publicodes'
import { catchErrors } from './errors.js'
import openapi from './openapi.json' assert { type: 'json' }
import { rateLimiterMiddleware } from './rate-limiter.js'
import { docRoutes } from './route/doc.js'
import { openapiRoutes } from './route/openapi.js'
import Sentry, { requestHandler, tracingMiddleWare } from './sentry.js'
@ -17,6 +18,8 @@ const app = new Koa<State, Context>()
const router = new Router<State, Context>()
if (process.env.NODE_ENV === 'production') {
app.proxy = true // Trust X-Forwarded-For proxy header
app.use(requestHandler)
app.use(tracingMiddleWare)
@ -34,6 +37,8 @@ app.use(catchErrors())
app.use(cors())
app.use(rateLimiterMiddleware)
const apiRoutes = publicodesAPI(new Engine(rules))
router.use('/api/v1', apiRoutes, docRoutes(), openapiRoutes(openapi))

View File

@ -0,0 +1,33 @@
import { BaseContext, Next } from 'koa'
import { RateLimiterMemory, RateLimiterRedis } from 'rate-limiter-flexible'
import IORedis from 'ioredis'
const Redis = IORedis.default
const rateLimiter =
process.env.NODE_ENV === 'production' && process.env.SCALINGO_REDIS_URL
? new RateLimiterRedis({
storeClient: new Redis(process.env.SCALINGO_REDIS_URL, {
enableOfflineQueue: false,
}),
keyPrefix: 'middleware',
points: 5, // 5 requests for ctx.ip
duration: 1, // per 1 second
})
: new RateLimiterMemory({
points: 5, // 5 requests for ctx.ip
duration: 1, // per 1 seconds
})
export const rateLimiterMiddleware = async (ctx: BaseContext, next: Next) => {
try {
await rateLimiter.consume(ctx.ip)
} catch (rejRes) {
ctx.status = 429
ctx.body = 'Too Many Requests'
return
}
await next()
}

View File

@ -3498,6 +3498,13 @@ __metadata:
languageName: node
linkType: hard
"@ioredis/commands@npm:^1.1.1":
version: 1.2.0
resolution: "@ioredis/commands@npm:1.2.0"
checksum: 9b20225ba36ef3e5caf69b3c0720597c3016cc9b1e157f519ea388f621dd9037177f84cfe7e25c4c32dad7dd90c70ff9123cd411f747e053cf292193c9c461e2
languageName: node
linkType: hard
"@istanbuljs/load-nyc-config@npm:^1.0.0":
version: 1.1.0
resolution: "@istanbuljs/load-nyc-config@npm:1.1.0"
@ -8493,9 +8500,9 @@ __metadata:
linkType: hard
"@types/node@npm:^17.0.35":
version: 17.0.35
resolution: "@types/node@npm:17.0.35"
checksum: 7a24946ae7fd20267ed92466384f594e448bfb151081158d565cc635d406ecb29ea8fb85fcd2a1f71efccf26fb5bd3c6f509bde56077eb8b832b847a6664bc62
version: 17.0.45
resolution: "@types/node@npm:17.0.45"
checksum: aa04366b9103b7d6cfd6b2ef64182e0eaa7d4462c3f817618486ea0422984c51fc69fd0d436eae6c9e696ddfdbec9ccaa27a917f7c2e8c75c5d57827fe3d95e8
languageName: node
linkType: hard
@ -9971,12 +9978,14 @@ __metadata:
"@types/node": ^17.0.35
"@types/swagger-ui-dist": ^3.30.1
chai-http: ^4.3.0
ioredis: ^5.2.3
koa: ^2.13.4
koa-body: ^5.0.0
koa-static: ^5.0.0
modele-social: "workspace:^"
nodemon: ^2.0.16
publicodes: ^1.0.0-beta.46
rate-limiter-flexible: ^2.3.8
rimraf: ^3.0.2
swagger-ui-dist: ^4.11.1
ts-node: ^10.8.0
@ -12053,6 +12062,13 @@ __metadata:
languageName: node
linkType: hard
"cluster-key-slot@npm:^1.1.0":
version: 1.1.0
resolution: "cluster-key-slot@npm:1.1.0"
checksum: fc953c75209b1ef9088081bab4e40a0b2586491c974ab93460569c014515ca5a2e31c043f185285e177007162fc353d07836d98f570c171dbe055775430e495b
languageName: node
linkType: hard
"co-body@npm:^5.1.1":
version: 5.2.0
resolution: "co-body@npm:5.2.0"
@ -13430,6 +13446,13 @@ __metadata:
languageName: node
linkType: hard
"denque@npm:^2.0.1":
version: 2.1.0
resolution: "denque@npm:2.1.0"
checksum: 1d4ae1d05e59ac3a3481e7b478293f4b4c813819342273f3d5b826c7ffa9753c520919ba264f377e09108d24ec6cf0ec0ac729a5686cbb8f32d797126c5dae74
languageName: node
linkType: hard
"depd@npm:2.0.0, depd@npm:^2.0.0, depd@npm:~2.0.0":
version: 2.0.0
resolution: "depd@npm:2.0.0"
@ -17861,6 +17884,23 @@ __metadata:
languageName: node
linkType: hard
"ioredis@npm:^5.2.3":
version: 5.2.3
resolution: "ioredis@npm:5.2.3"
dependencies:
"@ioredis/commands": ^1.1.1
cluster-key-slot: ^1.1.0
debug: ^4.3.4
denque: ^2.0.1
lodash.defaults: ^4.2.0
lodash.isarguments: ^3.1.0
redis-errors: ^1.2.0
redis-parser: ^3.0.0
standard-as-callback: ^2.1.0
checksum: 2cb7f0f4217e6774accad3620af1b7114722721c1d1824be2c9f0c2a77ab9629f2e0848d18b1a7208bc37796ae1207cb3e0898fce61900cfe797da0382724ad1
languageName: node
linkType: hard
"ip-regex@npm:^2.0.0":
version: 2.1.0
resolution: "ip-regex@npm:2.1.0"
@ -19797,6 +19837,13 @@ __metadata:
languageName: node
linkType: hard
"lodash.isarguments@npm:^3.1.0":
version: 3.1.0
resolution: "lodash.isarguments@npm:3.1.0"
checksum: ae1526f3eb5c61c77944b101b1f655f846ecbedcb9e6b073526eba6890dc0f13f09f72e11ffbf6540b602caee319af9ac363d6cdd6be41f4ee453436f04f13b5
languageName: node
linkType: hard
"lodash.isboolean@npm:^3.0.3":
version: 3.0.3
resolution: "lodash.isboolean@npm:3.0.3"
@ -23395,6 +23442,13 @@ __metadata:
languageName: node
linkType: hard
"rate-limiter-flexible@npm:^2.3.8":
version: 2.3.8
resolution: "rate-limiter-flexible@npm:2.3.8"
checksum: e257fe661e488039aa29da11fbc0c76c813b1e8dcd92b53afb0ff8d2e555e47fe5a3208859e927620a799f8286cde695f26a40f8fec11387c2291f37a8e50d66
languageName: node
linkType: hard
"raw-body@npm:2.5.1, raw-body@npm:^2.2.0, raw-body@npm:^2.4.1":
version: 2.5.1
resolution: "raw-body@npm:2.5.1"
@ -23990,6 +24044,22 @@ __metadata:
languageName: node
linkType: hard
"redis-errors@npm:^1.0.0, redis-errors@npm:^1.2.0":
version: 1.2.0
resolution: "redis-errors@npm:1.2.0"
checksum: f28ac2692113f6f9c222670735aa58aeae413464fd58ccf3fce3f700cae7262606300840c802c64f2b53f19f65993da24dc918afc277e9e33ac1ff09edb394f4
languageName: node
linkType: hard
"redis-parser@npm:^3.0.0":
version: 3.0.0
resolution: "redis-parser@npm:3.0.0"
dependencies:
redis-errors: ^1.0.0
checksum: 89290ae530332f2ae37577647fa18208d10308a1a6ba750b9d9a093e7398f5e5253f19855b64c98757f7129cccce958e4af2573fdc33bad41405f87f1943459a
languageName: node
linkType: hard
"reduce-css-calc@npm:^2.1.8":
version: 2.1.8
resolution: "reduce-css-calc@npm:2.1.8"
@ -25642,6 +25712,13 @@ __metadata:
languageName: node
linkType: hard
"standard-as-callback@npm:^2.1.0":
version: 2.1.0
resolution: "standard-as-callback@npm:2.1.0"
checksum: 88bec83ee220687c72d94fd86a98d5272c91d37ec64b66d830dbc0d79b62bfa6e47f53b71646011835fc9ce7fae62739545d13124262b53be4fbb3e2ebad551c
languageName: node
linkType: hard
"state-toggle@npm:^1.0.0":
version: 1.0.3
resolution: "state-toggle@npm:1.0.3"