mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
misc: moved to plan-based rate limit
This commit is contained in:
2
backend/src/@types/fastify.d.ts
vendored
2
backend/src/@types/fastify.d.ts
vendored
@@ -18,6 +18,7 @@ import { TOidcConfigServiceFactory } from "@app/ee/services/oidc/oidc-config-ser
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { TProjectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service";
|
||||
import { TRateLimitServiceFactory } from "@app/ee/services/rate-limit/rate-limit-service";
|
||||
import { RateLimitConfiguration } from "@app/ee/services/rate-limit/rate-limit-types";
|
||||
import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
|
||||
import { TScimServiceFactory } from "@app/ee/services/scim/scim-service";
|
||||
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
|
||||
@@ -88,6 +89,7 @@ declare module "fastify" {
|
||||
id: string;
|
||||
orgId: string;
|
||||
};
|
||||
rateLimits: RateLimitConfiguration;
|
||||
// passport data
|
||||
passportUser: {
|
||||
isUserCompleted: string;
|
||||
|
||||
@@ -40,7 +40,17 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
||||
secretRotation: true,
|
||||
caCrl: false,
|
||||
instanceUserManagement: false,
|
||||
externalKms: false
|
||||
externalKms: false,
|
||||
rateLimits: {
|
||||
readLimit: 60,
|
||||
publicEndpointLimit: 30,
|
||||
writeLimit: 200,
|
||||
secretsLimit: 60,
|
||||
authRateLimit: 60,
|
||||
inviteUserRateLimit: 30,
|
||||
mfaRateLimit: 20,
|
||||
creationLimit: 30
|
||||
}
|
||||
});
|
||||
|
||||
export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { TOrgPermission } from "@app/lib/types";
|
||||
|
||||
import { RateLimitConfiguration } from "../rate-limit/rate-limit-types";
|
||||
|
||||
export enum InstanceType {
|
||||
OnPrem = "self-hosted",
|
||||
EnterpriseOnPrem = "enterprise-self-hosted",
|
||||
@@ -58,6 +60,7 @@ export type TFeatureSet = {
|
||||
caCrl: false;
|
||||
instanceUserManagement: false;
|
||||
externalKms: false;
|
||||
rateLimits: RateLimitConfiguration;
|
||||
};
|
||||
|
||||
export type TOrgPlansTableDTO = {
|
||||
|
||||
@@ -4,9 +4,9 @@ import { logger } from "@app/lib/logger";
|
||||
|
||||
import { TLicenseServiceFactory } from "../license/license-service";
|
||||
import { TRateLimitDALFactory } from "./rate-limit-dal";
|
||||
import { TRateLimit, TRateLimitUpdateDTO } from "./rate-limit-types";
|
||||
import { RateLimitConfiguration, TRateLimit, TRateLimitUpdateDTO } from "./rate-limit-types";
|
||||
|
||||
let rateLimitMaxConfiguration = {
|
||||
let rateLimitMaxConfiguration: RateLimitConfiguration = {
|
||||
readLimit: 60,
|
||||
publicEndpointLimit: 30,
|
||||
writeLimit: 200,
|
||||
|
||||
@@ -14,3 +14,14 @@ export type TRateLimit = {
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
} & TRateLimitUpdateDTO;
|
||||
|
||||
export type RateLimitConfiguration = {
|
||||
readLimit: number;
|
||||
publicEndpointLimit: number;
|
||||
writeLimit: number;
|
||||
secretsLimit: number;
|
||||
authRateLimit: number;
|
||||
inviteUserRateLimit: number;
|
||||
mfaRateLimit: number;
|
||||
creationLimit: number;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { RateLimitOptions, RateLimitPluginOptions } from "@fastify/rate-limit";
|
||||
import { Redis } from "ioredis";
|
||||
|
||||
import { getRateLimiterConfig } from "@app/ee/services/rate-limit/rate-limit-service";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
|
||||
export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
|
||||
@@ -22,14 +21,16 @@ export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
|
||||
// GET endpoints
|
||||
export const readLimit: RateLimitOptions = {
|
||||
timeWindow: 60 * 1000,
|
||||
max: () => getRateLimiterConfig().readLimit,
|
||||
hook: "preValidation",
|
||||
max: (req) => req.rateLimits.readLimit,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
// POST, PATCH, PUT, DELETE endpoints
|
||||
export const writeLimit: RateLimitOptions = {
|
||||
timeWindow: 60 * 1000,
|
||||
max: () => getRateLimiterConfig().writeLimit,
|
||||
hook: "preValidation",
|
||||
max: (req) => req.rateLimits.writeLimit,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
@@ -37,25 +38,29 @@ export const writeLimit: RateLimitOptions = {
|
||||
export const secretsLimit: RateLimitOptions = {
|
||||
// secrets, folders, secret imports
|
||||
timeWindow: 60 * 1000,
|
||||
max: () => getRateLimiterConfig().secretsLimit,
|
||||
hook: "preValidation",
|
||||
max: (req) => req.rateLimits.secretsLimit,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
export const authRateLimit: RateLimitOptions = {
|
||||
timeWindow: 60 * 1000,
|
||||
max: () => getRateLimiterConfig().authRateLimit,
|
||||
hook: "preValidation",
|
||||
max: (req) => req.rateLimits.authRateLimit,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
export const inviteUserRateLimit: RateLimitOptions = {
|
||||
timeWindow: 60 * 1000,
|
||||
max: () => getRateLimiterConfig().inviteUserRateLimit,
|
||||
hook: "preValidation",
|
||||
max: (req) => req.rateLimits.inviteUserRateLimit,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
export const mfaRateLimit: RateLimitOptions = {
|
||||
timeWindow: 60 * 1000,
|
||||
max: () => getRateLimiterConfig().mfaRateLimit,
|
||||
hook: "preValidation",
|
||||
max: (req) => req.rateLimits.mfaRateLimit,
|
||||
keyGenerator: (req) => {
|
||||
return req.headers.authorization?.split(" ")[1] || req.realIp;
|
||||
}
|
||||
@@ -64,7 +69,8 @@ export const mfaRateLimit: RateLimitOptions = {
|
||||
export const creationLimit: RateLimitOptions = {
|
||||
// identity, project, org
|
||||
timeWindow: 60 * 1000,
|
||||
max: () => getRateLimiterConfig().creationLimit,
|
||||
hook: "preValidation",
|
||||
max: (req) => req.rateLimits.creationLimit,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
@@ -72,7 +78,8 @@ export const creationLimit: RateLimitOptions = {
|
||||
export const publicEndpointLimit: RateLimitOptions = {
|
||||
// Read Shared Secrets
|
||||
timeWindow: 60 * 1000,
|
||||
max: () => getRateLimiterConfig().publicEndpointLimit,
|
||||
hook: "preValidation",
|
||||
max: (req) => req.rateLimits.publicEndpointLimit,
|
||||
keyGenerator: (req) => req.realIp
|
||||
};
|
||||
|
||||
|
||||
35
backend/src/server/plugins/inject-rate-limits.ts
Normal file
35
backend/src/server/plugins/inject-rate-limits.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import fp from "fastify-plugin";
|
||||
|
||||
import { getRateLimiterConfig } from "@app/ee/services/rate-limit/rate-limit-service";
|
||||
|
||||
export const injectRateLimits = fp(async (server) => {
|
||||
server.decorateRequest("rateLimits", null);
|
||||
server.addHook("onRequest", async (req) => {
|
||||
const defaultRateLimiterConfig = getRateLimiterConfig();
|
||||
if (!req.auth) {
|
||||
// for public endpoints
|
||||
req.rateLimits = defaultRateLimiterConfig;
|
||||
return;
|
||||
}
|
||||
|
||||
const plan = await server.services.license.getPlan(req.auth.orgId);
|
||||
const { rateLimits } = plan;
|
||||
|
||||
if (plan.customRateLimits) {
|
||||
req.rateLimits = defaultRateLimiterConfig;
|
||||
return;
|
||||
}
|
||||
|
||||
// we're using the null coalescing operator in order to handle outdated licenses
|
||||
req.rateLimits = {
|
||||
readLimit: rateLimits?.readLimit ?? defaultRateLimiterConfig.readLimit,
|
||||
publicEndpointLimit: rateLimits?.publicEndpointLimit ?? defaultRateLimiterConfig.publicEndpointLimit,
|
||||
writeLimit: rateLimits?.writeLimit ?? defaultRateLimiterConfig.writeLimit,
|
||||
secretsLimit: rateLimits?.secretsLimit ?? defaultRateLimiterConfig.secretsLimit,
|
||||
authRateLimit: rateLimits?.authRateLimit ?? defaultRateLimiterConfig.authRateLimit,
|
||||
inviteUserRateLimit: rateLimits?.inviteUserRateLimit ?? defaultRateLimiterConfig.inviteUserRateLimit,
|
||||
mfaRateLimit: rateLimits?.mfaRateLimit ?? defaultRateLimiterConfig.mfaRateLimit,
|
||||
creationLimit: rateLimits?.creationLimit ?? defaultRateLimiterConfig.creationLimit
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -183,6 +183,7 @@ import { webhookServiceFactory } from "@app/services/webhook/webhook-service";
|
||||
import { injectAuditLogInfo } from "../plugins/audit-log";
|
||||
import { injectIdentity } from "../plugins/auth/inject-identity";
|
||||
import { injectPermission } from "../plugins/auth/inject-permission";
|
||||
import { injectRateLimits } from "../plugins/inject-rate-limits";
|
||||
import { registerSecretScannerGhApp } from "../plugins/secret-scanner";
|
||||
import { registerV1Routes } from "./v1";
|
||||
import { registerV2Routes } from "./v2";
|
||||
@@ -1130,6 +1131,7 @@ export const registerRoutes = async (
|
||||
|
||||
await server.register(injectIdentity, { userDAL, serviceTokenDAL });
|
||||
await server.register(injectPermission);
|
||||
await server.register(injectRateLimits);
|
||||
await server.register(injectAuditLogInfo);
|
||||
|
||||
server.route({
|
||||
|
||||
Reference in New Issue
Block a user