feat(backend-app-api): start implementing in memory rate limit

Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
Camila Belo
2024-03-14 13:58:23 +01:00
parent 9717111e53
commit 502d1c61fd
4 changed files with 47 additions and 2 deletions
+12
View File
@@ -14,6 +14,8 @@
* limitations under the License.
*/
import { HumanDuration } from '@backstage/types';
export interface Config {
backend?: {
auth?: {
@@ -32,6 +34,16 @@ export interface Config {
* unless you configure credentials for service calls.
*/
dangerouslyDisableDefaultAuthPolicy?: boolean;
rateLimit?: {
/**
* Limit each IP to max requests per window
*/
max?: number;
/**
* The duration for which the rate limit is enforced
*/
window?: HumanDuration;
};
};
};
+1
View File
@@ -64,6 +64,7 @@
"cors": "^2.8.5",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
"express-rate-limit": "^7.2.0",
"fs-extra": "^11.2.0",
"helmet": "^6.0.0",
"jose": "^5.0.0",
@@ -19,8 +19,11 @@ import {
HttpRouterServiceAuthPolicy,
RootConfigService,
} from '@backstage/backend-plugin-api';
import { durationToMilliseconds } from '@backstage/types';
import { RequestHandler } from 'express';
import { pathToRegexp } from 'path-to-regexp';
import { rateLimit } from 'express-rate-limit';
import { readDurationFromConfig } from '@backstage/config';
export function createPathPolicyPredicate(policyPath: string) {
if (policyPath === '/' || policyPath === '*') {
@@ -59,13 +62,32 @@ export function createCredentialsBarrier(options: {
const unauthenticatedPredicates = new Array<(path: string) => boolean>();
const cookiePredicates = new Array<(path: string) => boolean>();
const middleware: RequestHandler = (req, _, next) => {
// Default rate limit is 100 requests per 15 minutes
const max = config?.has('backend.auth.rateLimit.max')
? config.getNumber('backend.auth.rateLimit.max')
: 100;
const duration = config?.has('backend.auth.rateLimit.window')
? readDurationFromConfig(config.getConfig('backend.auth.rateLimit.window'))
: undefined;
// Default rate limit window is 15 minutes
const windowMs = duration ? durationToMilliseconds(duration) : 15 * 60 * 1000;
const limiter = rateLimit({
windowMs,
max,
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
});
const middleware: RequestHandler = (req, res, next) => {
const allowsUnauthenticated = unauthenticatedPredicates.some(predicate =>
predicate(req.path),
);
if (allowsUnauthenticated) {
next();
limiter(req, res, next);
return;
}
+10
View File
@@ -3248,6 +3248,7 @@ __metadata:
cors: ^2.8.5
express: ^4.17.1
express-promise-router: ^4.1.0
express-rate-limit: ^7.2.0
fs-extra: ^11.2.0
helmet: ^6.0.0
http-errors: ^2.0.0
@@ -28044,6 +28045,15 @@ __metadata:
languageName: node
linkType: hard
"express-rate-limit@npm:^7.2.0":
version: 7.2.0
resolution: "express-rate-limit@npm:7.2.0"
peerDependencies:
express: 4 || 5 || ^5.0.0-beta.1
checksum: 6adc3e06d430e91cf8ef8da23107466ffd9193b5680b03bc12702894cab0adcbb980634602b839b3a1444620f2379707098af3a4dcc38c909e89214abaf6c1bf
languageName: node
linkType: hard
"express-session@npm:^1.17.1, express-session@npm:^1.17.3":
version: 1.18.0
resolution: "express-session@npm:1.18.0"