From 04b85599a69c64a7ab815e693b06386ee5baf6fa Mon Sep 17 00:00:00 2001 From: tdurieux Date: Wed, 19 Oct 2022 11:04:52 +0200 Subject: [PATCH] feat: slow down request after 50 requests --- package-lock.json | 68 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ src/server.ts | 37 +++++++++++++++++--------- 3 files changed, 94 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index f9f4480..66e0f5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "express": "^4.18.1", "express-rate-limit": "^6.6.0", "express-session": "^1.17.3", + "express-slow-down": "^1.5.0", "got": "^11.8.5", "istextorbinary": "^6.0.0", "mime-types": "^2.1.35", @@ -44,6 +45,7 @@ "@types/express": "^4.17.13", "@types/express-rate-limit": "^6.0.0", "@types/express-session": "^1.17.5", + "@types/express-slow-down": "^1.3.2", "@types/got": "^9.6.12", "@types/mime-types": "^2.1.0", "@types/parse-github-url": "^1.0.0", @@ -875,6 +877,15 @@ "@types/express": "*" } }, + "node_modules/@types/express-slow-down": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/express-slow-down/-/express-slow-down-1.3.2.tgz", + "integrity": "sha512-Jw/orNMX+htFMYMugjhLotS5eRkdId4V5/89vqNXoUfxaHBU8VVIshItDh5YuWeyGeKuPli2fh94ORjYz/dkxA==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -1845,6 +1856,14 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -2160,6 +2179,17 @@ "node": ">=0.12" } }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -2516,6 +2546,14 @@ } ] }, + "node_modules/express-slow-down": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/express-slow-down/-/express-slow-down-1.5.0.tgz", + "integrity": "sha512-GCoa2a+mf7CE7C00TIoPMbcgQSXxEVolSAbP97uHyzVy87ssp0/IDtJ/GBxjv+gnfM2R1l2QcEfYixFJK75n7w==", + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -6048,6 +6086,15 @@ "@types/express": "*" } }, + "@types/express-slow-down": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/express-slow-down/-/express-slow-down-1.3.2.tgz", + "integrity": "sha512-Jw/orNMX+htFMYMugjhLotS5eRkdId4V5/89vqNXoUfxaHBU8VVIshItDh5YuWeyGeKuPli2fh94ORjYz/dkxA==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, "@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -6843,6 +6890,11 @@ "wrap-ansi": "^7.0.0" } }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" + }, "clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -7070,6 +7122,14 @@ "type-detect": "^4.0.0" } }, + "defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "requires": { + "clone": "^1.0.2" + } + }, "defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -7364,6 +7424,14 @@ } } }, + "express-slow-down": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/express-slow-down/-/express-slow-down-1.5.0.tgz", + "integrity": "sha512-GCoa2a+mf7CE7C00TIoPMbcgQSXxEVolSAbP97uHyzVy87ssp0/IDtJ/GBxjv+gnfM2R1l2QcEfYixFJK75n7w==", + "requires": { + "defaults": "^1.0.3" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", diff --git a/package.json b/package.json index c8c032a..32cae62 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "express": "^4.18.1", "express-rate-limit": "^6.6.0", "express-session": "^1.17.3", + "express-slow-down": "^1.5.0", "got": "^11.8.5", "istextorbinary": "^6.0.0", "mime-types": "^2.1.35", @@ -59,6 +60,7 @@ "@types/express": "^4.17.13", "@types/express-rate-limit": "^6.0.0", "@types/express-session": "^1.17.5", + "@types/express-slow-down": "^1.3.2", "@types/got": "^9.6.12", "@types/mime-types": "^2.1.0", "@types/parse-github-url": "^1.0.0", diff --git a/src/server.ts b/src/server.ts index 4ab3082..7321bba 100644 --- a/src/server.ts +++ b/src/server.ts @@ -2,6 +2,7 @@ import * as path from "path"; import * as ofs from "fs"; import { createClient } from "redis"; import rateLimit from "express-rate-limit"; +import * as slowDown from "express-slow-down"; import RedisStore from "rate-limit-redis"; import * as express from "express"; import * as compression from "compression"; @@ -37,7 +38,7 @@ export default async function start() { app.use(express.json()); app.use(compression()); - app.set("trust proxy", config.TRUST_PROXY); + app.set("trust proxy", true); app.set("etag", "strong"); app.get('/ip', (request, response) => response.send(request.ip)) @@ -67,29 +68,36 @@ export default async function start() { max: config.RATE_LIMIT, // limit each IP standardHeaders: true, legacyHeaders: false, - // delayMs: 0, // disable delaying - full speed until the max limit is reached + }); + const speedLimiter = slowDown({ + windowMs: 15 * 60 * 1000, // 15 minutes + delayAfter: 50, + delayMs: 5000, + headers: true, }); - app.use("/github", rate, connection.router); + app.use("/github", rate, speedLimiter, connection.router); // api routes - app.use("/api/admin", rate, router.admin); - app.use("/api/options", rate, router.option); - app.use("/api/conferences", rate, router.conference); - app.use("/api/user", rate, router.user); - app.use("/api/repo", rate, router.repositoryPublic); - app.use("/api/repo", rate, router.file); - app.use("/api/repo", rate, router.repositoryPrivate); - app.use("/w/", rate, router.webview); + const apiRouter = express.Router() + app.use("/api", rate, speedLimiter, apiRouter); - app.get("/api/message", async (_, res) => { + apiRouter.use("/admin", router.admin); + apiRouter.use("/options", router.option); + apiRouter.use("/conferences", router.conference); + apiRouter.use("/user", router.user); + apiRouter.use("/repo", router.repositoryPublic); + apiRouter.use("/repo", router.file); + apiRouter.use("/repo", router.repositoryPrivate); + + apiRouter.get("/message", async (_, res) => { if (ofs.existsSync("./message.txt")) { return res.sendFile(path.resolve(__dirname, "..", "message.txt")); } res.sendStatus(404); }); - app.get("/api/stat", async (_, res) => { + apiRouter.get("/stat", async (_, res) => { const nbRepositories = await AnonymizedRepositoryModel.estimatedDocumentCount(); @@ -97,6 +105,9 @@ export default async function start() { res.json({ nbRepositories, nbUsers }); }); + // web view + app.use("/w/", rate, speedLimiter, router.webview); + app .get("/", indexResponse) .get("/404", indexResponse)