mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 18:27:36 +00:00
misc: moved to plan-based rate limit
This commit is contained in:
Vendored
+2
@@ -18,6 +18,7 @@ import { TOidcConfigServiceFactory } from "@app/ee/services/oidc/oidc-config-ser
|
|||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
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 { 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 { 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 { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
|
||||||
import { TScimServiceFactory } from "@app/ee/services/scim/scim-service";
|
import { TScimServiceFactory } from "@app/ee/services/scim/scim-service";
|
||||||
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
|
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
|
||||||
@@ -88,6 +89,7 @@ declare module "fastify" {
|
|||||||
id: string;
|
id: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
rateLimits: RateLimitConfiguration;
|
||||||
// passport data
|
// passport data
|
||||||
passportUser: {
|
passportUser: {
|
||||||
isUserCompleted: string;
|
isUserCompleted: string;
|
||||||
|
|||||||
@@ -40,7 +40,17 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
secretRotation: true,
|
secretRotation: true,
|
||||||
caCrl: false,
|
caCrl: false,
|
||||||
instanceUserManagement: 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) => {
|
export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { TOrgPermission } from "@app/lib/types";
|
import { TOrgPermission } from "@app/lib/types";
|
||||||
|
|
||||||
|
import { RateLimitConfiguration } from "../rate-limit/rate-limit-types";
|
||||||
|
|
||||||
export enum InstanceType {
|
export enum InstanceType {
|
||||||
OnPrem = "self-hosted",
|
OnPrem = "self-hosted",
|
||||||
EnterpriseOnPrem = "enterprise-self-hosted",
|
EnterpriseOnPrem = "enterprise-self-hosted",
|
||||||
@@ -58,6 +60,7 @@ export type TFeatureSet = {
|
|||||||
caCrl: false;
|
caCrl: false;
|
||||||
instanceUserManagement: false;
|
instanceUserManagement: false;
|
||||||
externalKms: false;
|
externalKms: false;
|
||||||
|
rateLimits: RateLimitConfiguration;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TOrgPlansTableDTO = {
|
export type TOrgPlansTableDTO = {
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { logger } from "@app/lib/logger";
|
|||||||
|
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { TRateLimitDALFactory } from "./rate-limit-dal";
|
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,
|
readLimit: 60,
|
||||||
publicEndpointLimit: 30,
|
publicEndpointLimit: 30,
|
||||||
writeLimit: 200,
|
writeLimit: 200,
|
||||||
|
|||||||
@@ -14,3 +14,14 @@ export type TRateLimit = {
|
|||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
} & TRateLimitUpdateDTO;
|
} & 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 type { RateLimitOptions, RateLimitPluginOptions } from "@fastify/rate-limit";
|
||||||
import { Redis } from "ioredis";
|
import { Redis } from "ioredis";
|
||||||
|
|
||||||
import { getRateLimiterConfig } from "@app/ee/services/rate-limit/rate-limit-service";
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
|
||||||
export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
|
export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
|
||||||
@@ -22,14 +21,16 @@ export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
|
|||||||
// GET endpoints
|
// GET endpoints
|
||||||
export const readLimit: RateLimitOptions = {
|
export const readLimit: RateLimitOptions = {
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => getRateLimiterConfig().readLimit,
|
hook: "preValidation",
|
||||||
|
max: (req) => req.rateLimits.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: () => getRateLimiterConfig().writeLimit,
|
hook: "preValidation",
|
||||||
|
max: (req) => req.rateLimits.writeLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,25 +38,29 @@ 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: () => getRateLimiterConfig().secretsLimit,
|
hook: "preValidation",
|
||||||
|
max: (req) => req.rateLimits.secretsLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
export const authRateLimit: RateLimitOptions = {
|
export const authRateLimit: RateLimitOptions = {
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => getRateLimiterConfig().authRateLimit,
|
hook: "preValidation",
|
||||||
|
max: (req) => req.rateLimits.authRateLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
export const inviteUserRateLimit: RateLimitOptions = {
|
export const inviteUserRateLimit: RateLimitOptions = {
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => getRateLimiterConfig().inviteUserRateLimit,
|
hook: "preValidation",
|
||||||
|
max: (req) => req.rateLimits.inviteUserRateLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mfaRateLimit: RateLimitOptions = {
|
export const mfaRateLimit: RateLimitOptions = {
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => getRateLimiterConfig().mfaRateLimit,
|
hook: "preValidation",
|
||||||
|
max: (req) => req.rateLimits.mfaRateLimit,
|
||||||
keyGenerator: (req) => {
|
keyGenerator: (req) => {
|
||||||
return req.headers.authorization?.split(" ")[1] || req.realIp;
|
return req.headers.authorization?.split(" ")[1] || req.realIp;
|
||||||
}
|
}
|
||||||
@@ -64,7 +69,8 @@ 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: () => getRateLimiterConfig().creationLimit,
|
hook: "preValidation",
|
||||||
|
max: (req) => req.rateLimits.creationLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,7 +78,8 @@ export const creationLimit: RateLimitOptions = {
|
|||||||
export const publicEndpointLimit: RateLimitOptions = {
|
export const publicEndpointLimit: RateLimitOptions = {
|
||||||
// Read Shared Secrets
|
// Read Shared Secrets
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
max: () => getRateLimiterConfig().publicEndpointLimit,
|
hook: "preValidation",
|
||||||
|
max: (req) => req.rateLimits.publicEndpointLimit,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 { injectAuditLogInfo } from "../plugins/audit-log";
|
||||||
import { injectIdentity } from "../plugins/auth/inject-identity";
|
import { injectIdentity } from "../plugins/auth/inject-identity";
|
||||||
import { injectPermission } from "../plugins/auth/inject-permission";
|
import { injectPermission } from "../plugins/auth/inject-permission";
|
||||||
|
import { injectRateLimits } from "../plugins/inject-rate-limits";
|
||||||
import { registerSecretScannerGhApp } from "../plugins/secret-scanner";
|
import { registerSecretScannerGhApp } from "../plugins/secret-scanner";
|
||||||
import { registerV1Routes } from "./v1";
|
import { registerV1Routes } from "./v1";
|
||||||
import { registerV2Routes } from "./v2";
|
import { registerV2Routes } from "./v2";
|
||||||
@@ -1130,6 +1131,7 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
await server.register(injectIdentity, { userDAL, serviceTokenDAL });
|
await server.register(injectIdentity, { userDAL, serviceTokenDAL });
|
||||||
await server.register(injectPermission);
|
await server.register(injectPermission);
|
||||||
|
await server.register(injectRateLimits);
|
||||||
await server.register(injectAuditLogInfo);
|
await server.register(injectAuditLogInfo);
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
|
|||||||
Reference in New Issue
Block a user