mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-03 08:26:00 +00:00
misc: migrated to structured singleton pattern
This commit is contained in:
@@ -73,8 +73,8 @@ export const main = async ({ db, smtp, logger, queue, keyStore }: TMain) => {
|
|||||||
if (appCfg.isProductionMode) {
|
if (appCfg.isProductionMode) {
|
||||||
const rateLimitDAL = rateLimitDALFactory(db);
|
const rateLimitDAL = rateLimitDALFactory(db);
|
||||||
const rateLimitService = rateLimitServiceFactory({ rateLimitDAL });
|
const rateLimitService = rateLimitServiceFactory({ rateLimitDAL });
|
||||||
const rateLimits = await rateLimitService.getRateLimits();
|
await rateLimitService.syncRateLimitConfiguration();
|
||||||
await server.register<FastifyRateLimitOptions>(ratelimiter, globalRateLimiterCfg(rateLimits));
|
await server.register<FastifyRateLimitOptions>(ratelimiter, globalRateLimiterCfg());
|
||||||
}
|
}
|
||||||
|
|
||||||
await server.register(helmet, { contentSecurityPolicy: false });
|
await server.register(helmet, { contentSecurityPolicy: false });
|
||||||
|
|||||||
@@ -1,31 +1,20 @@
|
|||||||
import type { RateLimitOptions, RateLimitPluginOptions } from "@fastify/rate-limit";
|
import type { RateLimitOptions, RateLimitPluginOptions } from "@fastify/rate-limit";
|
||||||
import { Redis } from "ioredis";
|
import { Redis } from "ioredis";
|
||||||
|
|
||||||
import { TRateLimit } from "@app/db/schemas";
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { getRateLimiterConfig } from "@app/services/rate-limit/rate-limit-service";
|
||||||
export const rateLimitMaxConfiguration = {
|
|
||||||
readLimit: 60,
|
|
||||||
publicEndpointLimit: 30,
|
|
||||||
writeLimit: 200,
|
|
||||||
secretsLimit: 60,
|
|
||||||
authRateLimit: 60,
|
|
||||||
inviteUserRateLimit: 30,
|
|
||||||
mfaRateLimit: 20,
|
|
||||||
creationLimit: 30
|
|
||||||
};
|
|
||||||
|
|
||||||
// GET endpoints
|
// GET endpoints
|
||||||
export const readLimit: RateLimitOptions = {
|
export const readLimit: RateLimitOptions = {
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => rateLimitMaxConfiguration.readLimit,
|
max: () => getRateLimiterConfig().readLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
// POST, PATCH, PUT, DELETE endpoints
|
// POST, PATCH, PUT, DELETE endpoints
|
||||||
export const writeLimit: RateLimitOptions = {
|
export const writeLimit: RateLimitOptions = {
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => rateLimitMaxConfiguration.writeLimit, // (too low, FA having issues so increasing it - maidul)
|
max: () => getRateLimiterConfig().writeLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,25 +22,25 @@ export const writeLimit: RateLimitOptions = {
|
|||||||
export const secretsLimit: RateLimitOptions = {
|
export const secretsLimit: RateLimitOptions = {
|
||||||
// secrets, folders, secret imports
|
// secrets, folders, secret imports
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => rateLimitMaxConfiguration.secretsLimit,
|
max: () => getRateLimiterConfig().secretsLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
export const authRateLimit: RateLimitOptions = {
|
export const authRateLimit: RateLimitOptions = {
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => rateLimitMaxConfiguration.authRateLimit,
|
max: () => getRateLimiterConfig().authRateLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
export const inviteUserRateLimit: RateLimitOptions = {
|
export const inviteUserRateLimit: RateLimitOptions = {
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => rateLimitMaxConfiguration.inviteUserRateLimit,
|
max: () => getRateLimiterConfig().inviteUserRateLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mfaRateLimit: RateLimitOptions = {
|
export const mfaRateLimit: RateLimitOptions = {
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => rateLimitMaxConfiguration.mfaRateLimit,
|
max: () => getRateLimiterConfig().mfaRateLimit,
|
||||||
keyGenerator: (req) => {
|
keyGenerator: (req) => {
|
||||||
return req.headers.authorization?.split(" ")[1] || req.realIp;
|
return req.headers.authorization?.split(" ")[1] || req.realIp;
|
||||||
}
|
}
|
||||||
@@ -60,7 +49,7 @@ export const mfaRateLimit: RateLimitOptions = {
|
|||||||
export const creationLimit: RateLimitOptions = {
|
export const creationLimit: RateLimitOptions = {
|
||||||
// identity, project, org
|
// identity, project, org
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => rateLimitMaxConfiguration.creationLimit,
|
max: () => getRateLimiterConfig().creationLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,27 +57,16 @@ export const creationLimit: RateLimitOptions = {
|
|||||||
export const publicEndpointLimit: RateLimitOptions = {
|
export const publicEndpointLimit: RateLimitOptions = {
|
||||||
// Shared Secrets
|
// Shared Secrets
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => rateLimitMaxConfiguration.publicEndpointLimit,
|
max: () => getRateLimiterConfig().publicEndpointLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
export const globalRateLimiterCfg = async (customRateLimits?: TRateLimit): Promise<RateLimitPluginOptions> => {
|
export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const redis = appCfg.isRedisConfigured
|
const redis = appCfg.isRedisConfigured
|
||||||
? new Redis(appCfg.REDIS_URL, { connectTimeout: 500, maxRetriesPerRequest: 1 })
|
? new Redis(appCfg.REDIS_URL, { connectTimeout: 500, maxRetriesPerRequest: 1 })
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (customRateLimits) {
|
|
||||||
rateLimitMaxConfiguration.readLimit = customRateLimits.readRateLimit;
|
|
||||||
rateLimitMaxConfiguration.publicEndpointLimit = customRateLimits.publicEndpointLimit;
|
|
||||||
rateLimitMaxConfiguration.writeLimit = customRateLimits.writeRateLimit;
|
|
||||||
rateLimitMaxConfiguration.secretsLimit = customRateLimits.secretsRateLimit;
|
|
||||||
rateLimitMaxConfiguration.authRateLimit = customRateLimits.authRateLimit;
|
|
||||||
rateLimitMaxConfiguration.inviteUserRateLimit = customRateLimits.inviteUserRateLimit;
|
|
||||||
rateLimitMaxConfiguration.mfaRateLimit = customRateLimits.mfaRateLimit;
|
|
||||||
rateLimitMaxConfiguration.creationLimit = customRateLimits.creationLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: 600,
|
max: 600,
|
||||||
|
|||||||
@@ -1,11 +1,27 @@
|
|||||||
import { CronJob } from "cron";
|
import { CronJob } from "cron";
|
||||||
|
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { rateLimitMaxConfiguration } from "@app/server/config/rateLimiter";
|
|
||||||
|
|
||||||
import { TRateLimitDALFactory } from "./rate-limit-dal";
|
import { TRateLimitDALFactory } from "./rate-limit-dal";
|
||||||
import { TRateLimit, TRateLimitUpdateDTO } from "./rate-limit-types";
|
import { TRateLimit, TRateLimitUpdateDTO } from "./rate-limit-types";
|
||||||
|
|
||||||
|
let rateLimitMaxConfiguration = {
|
||||||
|
readLimit: 60,
|
||||||
|
publicEndpointLimit: 30,
|
||||||
|
writeLimit: 200,
|
||||||
|
secretsLimit: 60,
|
||||||
|
authRateLimit: 60,
|
||||||
|
inviteUserRateLimit: 30,
|
||||||
|
mfaRateLimit: 20,
|
||||||
|
creationLimit: 30
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.freeze(rateLimitMaxConfiguration);
|
||||||
|
|
||||||
|
export const getRateLimiterConfig = () => {
|
||||||
|
return rateLimitMaxConfiguration;
|
||||||
|
};
|
||||||
|
|
||||||
type TRateLimitServiceFactoryDep = {
|
type TRateLimitServiceFactoryDep = {
|
||||||
rateLimitDAL: TRateLimitDALFactory;
|
rateLimitDAL: TRateLimitDALFactory;
|
||||||
};
|
};
|
||||||
@@ -37,27 +53,33 @@ export const rateLimitServiceFactory = ({ rateLimitDAL }: TRateLimitServiceFacto
|
|||||||
return rateLimitDAL.updateById(DEFAULT_RATE_LIMIT_CONFIG_ID, updates);
|
return rateLimitDAL.updateById(DEFAULT_RATE_LIMIT_CONFIG_ID, updates);
|
||||||
};
|
};
|
||||||
|
|
||||||
const initializeBackgroundSync = () => {
|
const syncRateLimitConfiguration = async () => {
|
||||||
const rateLimitSync = async () => {
|
try {
|
||||||
try {
|
const rateLimit = await getRateLimits();
|
||||||
const rateLimit = await getRateLimits();
|
if (rateLimit) {
|
||||||
if (rateLimit) {
|
const newRateLimitMaxConfiguration: typeof rateLimitMaxConfiguration = {
|
||||||
rateLimitMaxConfiguration.readLimit = rateLimit.readRateLimit;
|
readLimit: rateLimit.readRateLimit,
|
||||||
rateLimitMaxConfiguration.publicEndpointLimit = rateLimit.publicEndpointLimit;
|
publicEndpointLimit: rateLimit.publicEndpointLimit,
|
||||||
rateLimitMaxConfiguration.writeLimit = rateLimit.writeRateLimit;
|
writeLimit: rateLimit.writeRateLimit,
|
||||||
rateLimitMaxConfiguration.secretsLimit = rateLimit.secretsRateLimit;
|
secretsLimit: rateLimit.secretsRateLimit,
|
||||||
rateLimitMaxConfiguration.authRateLimit = rateLimit.authRateLimit;
|
authRateLimit: rateLimit.authRateLimit,
|
||||||
rateLimitMaxConfiguration.inviteUserRateLimit = rateLimit.inviteUserRateLimit;
|
inviteUserRateLimit: rateLimit.inviteUserRateLimit,
|
||||||
rateLimitMaxConfiguration.mfaRateLimit = rateLimit.mfaRateLimit;
|
mfaRateLimit: rateLimit.mfaRateLimit,
|
||||||
rateLimitMaxConfiguration.creationLimit = rateLimit.creationLimit;
|
creationLimit: rateLimit.creationLimit
|
||||||
}
|
};
|
||||||
} catch (error) {
|
|
||||||
logger.error(`Error syncing rate limit configurations: %o`, error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
logger.info(`Rate limit configuration: %o`, newRateLimitMaxConfiguration);
|
||||||
|
Object.freeze(newRateLimitMaxConfiguration);
|
||||||
|
rateLimitMaxConfiguration = newRateLimitMaxConfiguration;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Error syncing rate limit configurations: %o`, error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const initializeBackgroundSync = () => {
|
||||||
// sync rate limits configuration every 10 minutes
|
// sync rate limits configuration every 10 minutes
|
||||||
const job = new CronJob("*/10 * * * *", rateLimitSync);
|
const job = new CronJob("*/10 * * * *", syncRateLimitConfiguration);
|
||||||
job.start();
|
job.start();
|
||||||
|
|
||||||
return job;
|
return job;
|
||||||
@@ -66,6 +88,7 @@ export const rateLimitServiceFactory = ({ rateLimitDAL }: TRateLimitServiceFacto
|
|||||||
return {
|
return {
|
||||||
getRateLimits,
|
getRateLimits,
|
||||||
updateRateLimit,
|
updateRateLimit,
|
||||||
initializeBackgroundSync
|
initializeBackgroundSync,
|
||||||
|
syncRateLimitConfiguration
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user