mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-31 00:16:47 +01:00
feat: make all internal rate limits configurable (#5095)
### What This PR makes the rate limit for user creation and simple login (our password based login) configurable in the same way you can do metricsRateLimiting. ### Worth noting In addition this PR adds a `rate_limit{endpoint, method}` prometheus gauge, which gets the data from the UnleashConfig.
This commit is contained in:
parent
6fe4740e67
commit
1bba76413f
@ -145,6 +145,10 @@ exports[`should create default config 1`] = `
|
|||||||
"preRouterHook": undefined,
|
"preRouterHook": undefined,
|
||||||
"prometheusApi": undefined,
|
"prometheusApi": undefined,
|
||||||
"publicFolder": undefined,
|
"publicFolder": undefined,
|
||||||
|
"rateLimiting": {
|
||||||
|
"createUserMaxPerMinute": 20,
|
||||||
|
"simpleLoginMaxPerMinute": 10,
|
||||||
|
},
|
||||||
"secureHeaders": false,
|
"secureHeaders": false,
|
||||||
"segmentValuesLimit": 1000,
|
"segmentValuesLimit": 1000,
|
||||||
"server": {
|
"server": {
|
||||||
|
@ -19,6 +19,7 @@ import {
|
|||||||
ICspDomainOptions,
|
ICspDomainOptions,
|
||||||
IClientCachingOption,
|
IClientCachingOption,
|
||||||
IMetricsRateLimiting,
|
IMetricsRateLimiting,
|
||||||
|
IRateLimiting,
|
||||||
} from './types/option';
|
} from './types/option';
|
||||||
import { getDefaultLogProvider, LogLevel, validateLogProvider } from './logger';
|
import { getDefaultLogProvider, LogLevel, validateLogProvider } from './logger';
|
||||||
import { defaultCustomAuthDenyAll } from './default-custom-auth-deny-all';
|
import { defaultCustomAuthDenyAll } from './default-custom-auth-deny-all';
|
||||||
@ -132,6 +133,23 @@ function loadMetricsRateLimitingConfig(
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadRateLimitingConfig(options: IUnleashOptions): IRateLimiting {
|
||||||
|
const createUserMaxPerMinute = parseEnvVarNumber(
|
||||||
|
process.env.CREATE_USER_RATE_LIMIT_PER_MINUTE,
|
||||||
|
20,
|
||||||
|
);
|
||||||
|
const simpleLoginMaxPerMinute = parseEnvVarNumber(
|
||||||
|
process.env.SIMPLE_LOGIN_LIMIT_PER_MINUTE,
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
|
||||||
|
const defaultRateLimitOptions: IRateLimiting = {
|
||||||
|
createUserMaxPerMinute,
|
||||||
|
simpleLoginMaxPerMinute,
|
||||||
|
};
|
||||||
|
return mergeAll([defaultRateLimitOptions, options.rateLimiting || {}]);
|
||||||
|
}
|
||||||
|
|
||||||
function loadUI(options: IUnleashOptions): IUIConfig {
|
function loadUI(options: IUnleashOptions): IUIConfig {
|
||||||
const uiO = options.ui || {};
|
const uiO = options.ui || {};
|
||||||
const ui: IUIConfig = {
|
const ui: IUIConfig = {
|
||||||
@ -525,6 +543,8 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
|||||||
|
|
||||||
const metricsRateLimiting = loadMetricsRateLimitingConfig(options);
|
const metricsRateLimiting = loadMetricsRateLimitingConfig(options);
|
||||||
|
|
||||||
|
const rateLimiting = loadRateLimitingConfig(options);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
db,
|
db,
|
||||||
session,
|
session,
|
||||||
@ -559,6 +579,7 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
|||||||
disableScheduler: options.disableScheduler,
|
disableScheduler: options.disableScheduler,
|
||||||
isEnterprise: isEnterprise,
|
isEnterprise: isEnterprise,
|
||||||
metricsRateLimiting,
|
metricsRateLimiting,
|
||||||
|
rateLimiting,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +190,12 @@ export default class MetricsMonitor {
|
|||||||
labelNames: ['environment'],
|
labelNames: ['environment'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const rateLimits = new client.Gauge({
|
||||||
|
name: 'rate_limits',
|
||||||
|
help: 'Rate limits (per minute) for METHOD/ENDPOINT pairs',
|
||||||
|
labelNames: ['endpoint', 'method'],
|
||||||
|
});
|
||||||
|
|
||||||
async function collectStaticCounters() {
|
async function collectStaticCounters() {
|
||||||
try {
|
try {
|
||||||
const stats = await instanceStatsService.getStats();
|
const stats = await instanceStatsService.getStats();
|
||||||
@ -259,6 +265,42 @@ export default class MetricsMonitor {
|
|||||||
.labels({ range: clientStat.range })
|
.labels({ range: clientStat.range })
|
||||||
.set(clientStat.count),
|
.set(clientStat.count),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
rateLimits.reset();
|
||||||
|
rateLimits
|
||||||
|
.labels({ endpoint: '/api/client/metrics', method: 'POST' })
|
||||||
|
.set(config.metricsRateLimiting.clientMetricsMaxPerMinute);
|
||||||
|
rateLimits
|
||||||
|
.labels({
|
||||||
|
endpoint: '/api/client/register',
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
.set(config.metricsRateLimiting.clientRegisterMaxPerMinute);
|
||||||
|
rateLimits
|
||||||
|
.labels({
|
||||||
|
endpoint: '/api/frontend/metrics',
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
.set(
|
||||||
|
config.metricsRateLimiting.frontendMetricsMaxPerMinute,
|
||||||
|
);
|
||||||
|
rateLimits
|
||||||
|
.labels({
|
||||||
|
endpoint: '/api/frontend/register',
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
.set(
|
||||||
|
config.metricsRateLimiting.frontendRegisterMaxPerMinute,
|
||||||
|
);
|
||||||
|
rateLimits
|
||||||
|
.labels({
|
||||||
|
endpoint: '/api/admin/user-admin',
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
.set(config.rateLimiting.createUserMaxPerMinute);
|
||||||
|
rateLimits
|
||||||
|
.labels({ endpoint: '/auth/simple', method: 'POST' })
|
||||||
|
.set(config.rateLimiting.simpleLoginMaxPerMinute);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +288,7 @@ export default class UserAdminController extends Controller {
|
|||||||
}),
|
}),
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: minutesToMilliseconds(1),
|
windowMs: minutesToMilliseconds(1),
|
||||||
max: 20,
|
max: config.rateLimiting.createUserMaxPerMinute,
|
||||||
validate: false,
|
validate: false,
|
||||||
standardHeaders: true,
|
standardHeaders: true,
|
||||||
legacyHeaders: false,
|
legacyHeaders: false,
|
||||||
|
@ -31,7 +31,7 @@ class IndexRouter extends Controller {
|
|||||||
new SimplePasswordProvider(config, services).router,
|
new SimplePasswordProvider(config, services).router,
|
||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: minutesToMilliseconds(1),
|
windowMs: minutesToMilliseconds(1),
|
||||||
max: 10,
|
max: config.rateLimiting.simpleLoginMaxPerMinute,
|
||||||
validate: false,
|
validate: false,
|
||||||
standardHeaders: true,
|
standardHeaders: true,
|
||||||
legacyHeaders: false,
|
legacyHeaders: false,
|
||||||
|
@ -124,6 +124,7 @@ export interface IUnleashOptions {
|
|||||||
publicFolder?: string;
|
publicFolder?: string;
|
||||||
disableScheduler?: boolean;
|
disableScheduler?: boolean;
|
||||||
metricsRateLimiting?: Partial<IMetricsRateLimiting>;
|
metricsRateLimiting?: Partial<IMetricsRateLimiting>;
|
||||||
|
rateLimiting?: Partial<IRateLimiting>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEmailOption {
|
export interface IEmailOption {
|
||||||
@ -193,6 +194,11 @@ export interface IMetricsRateLimiting {
|
|||||||
frontendRegisterMaxPerMinute: number;
|
frontendRegisterMaxPerMinute: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IRateLimiting {
|
||||||
|
createUserMaxPerMinute: number;
|
||||||
|
simpleLoginMaxPerMinute: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IUnleashConfig {
|
export interface IUnleashConfig {
|
||||||
db: IDBOption;
|
db: IDBOption;
|
||||||
session: ISessionOption;
|
session: ISessionOption;
|
||||||
@ -227,4 +233,5 @@ export interface IUnleashConfig {
|
|||||||
publicFolder?: string;
|
publicFolder?: string;
|
||||||
disableScheduler?: boolean;
|
disableScheduler?: boolean;
|
||||||
isEnterprise: boolean;
|
isEnterprise: boolean;
|
||||||
|
rateLimiting: IRateLimiting;
|
||||||
}
|
}
|
||||||
|
@ -140,10 +140,13 @@ unleash.start(unleashOptions);
|
|||||||
- **keepAliveTimeout** - Use this to tweak connection keepalive timeout in seconds. Useful for hosted situations where you need to make sure your connections are closed before terminating the instance. Defaults to `15`. Overridable with the `SERVER_KEEPALIVE_TIMEOUT` environment variable.
|
- **keepAliveTimeout** - Use this to tweak connection keepalive timeout in seconds. Useful for hosted situations where you need to make sure your connections are closed before terminating the instance. Defaults to `15`. Overridable with the `SERVER_KEEPALIVE_TIMEOUT` environment variable.
|
||||||
You can also set the environment variable `ENABLED_ENVIRONMENTS` to a comma delimited string of environment names to override environments.
|
You can also set the environment variable `ENABLED_ENVIRONMENTS` to a comma delimited string of environment names to override environments.
|
||||||
- **metricsRateLimiting** - Use the following to tweak the rate limits for `/api/client/register`, `/api/client/metrics`, `/api/frontend/register` and `/api/frontend/metrics` POST endpoints
|
- **metricsRateLimiting** - Use the following to tweak the rate limits for `/api/client/register`, `/api/client/metrics`, `/api/frontend/register` and `/api/frontend/metrics` POST endpoints
|
||||||
- `clientMetricsMaxPerMinute` - How many requests per minute is allowed against POST `/api/client/metrics` before returning 429. Set to 6000 by default (100rps) - Overridable with `REGISTER_CLIENT_RATE_LIMIT_PER_MINUTE` environment variable
|
- `clientMetricsMaxPerMinute` - How many requests per minute per IP is allowed against POST `/api/client/metrics` before returning 429. Set to 6000 by default (100rps) - Overridable with `REGISTER_CLIENT_RATE_LIMIT_PER_MINUTE` environment variable
|
||||||
- `clientRegisterMaxPerMinute` - How many requests per minute is allowed against POST `/api/client/register` before returning 429. Set to 6000 by default (100rps) - Overridable with `CLIENT_METRICS_RATE_LIMIT_PER_MINUTE` environment variable
|
- `clientRegisterMaxPerMinute` - How many requests per minute per IP is allowed against POST `/api/client/register` before returning 429. Set to 6000 by default (100rps) - Overridable with `CLIENT_METRICS_RATE_LIMIT_PER_MINUTE` environment variable
|
||||||
- `frontendMetricsMaxPerMinute` - How many requests per minute is allowed against POST `/api/frontend/metrics` before returning 429. Set to 6000 by default (100rps) - Overridable with `FRONTEND_METRICS_RATE_LIMIT_PER_MINUTE` environment variable
|
- `frontendMetricsMaxPerMinute` - How many requests per minute per IP is allowed against POST `/api/frontend/metrics` before returning 429. Set to 6000 by default (100rps) - Overridable with `FRONTEND_METRICS_RATE_LIMIT_PER_MINUTE` environment variable
|
||||||
- `frontendRegisterMaxPerMinute` - How many requests per minute is allowed against POST `/api/frontend/register` before returning 429. Set to 6000 by default (100rps) - Overridable with `REGISTER_FRONTEND_RATE_LIMIT_PER_MINUTE` environment variable
|
- `frontendRegisterMaxPerMinute` - How many requests per minute per IP is allowed against POST `/api/frontend/register` before returning 429. Set to 6000 by default (100rps) - Overridable with `REGISTER_FRONTEND_RATE_LIMIT_PER_MINUTE` environment variable
|
||||||
|
- **rateLimiting** - Use the following to tweak the rate limits for `POST /auth/simple` (Password-based login) and `POST /api/admin/user-admin` (Creating users)
|
||||||
|
- `simpleLoginMaxPerMinute` - How many requests per minute per IP is allowed against POST `/auth/simple` before returning 429. Set to 10 by default - Overridable with `SIMPLE_LOGIN_LIMIT_PER_MINUTE` environment variable
|
||||||
|
- `createUserMaxPerMinute` - How many requests per minute per IP is allowed against POST `/api/admin/user-admin` before returning 429. Set to 20 by default - Overridable with `CREATE_USER_RATE_LIMIT_PER_MINUTE` environment variable
|
||||||
### Disabling Auto-Start {#disabling-auto-start}
|
### Disabling Auto-Start {#disabling-auto-start}
|
||||||
|
|
||||||
If you're using Unleash as part of a larger express app, you can disable the automatic server start by calling `server.create`. It takes the same options as `server.start`, but will not begin listening for connections.
|
If you're using Unleash as part of a larger express app, you can disable the automatic server start by calling `server.create`. It takes the same options as `server.start`, but will not begin listening for connections.
|
||||||
|
Loading…
Reference in New Issue
Block a user