misc: moved to plan-based rate limit

This commit is contained in:
Sheen Capadngan
2024-08-02 21:37:48 +08:00
parent a0490d0fde
commit 55a6740714
8 changed files with 82 additions and 12 deletions
+2
View File
@@ -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;
+11 -1
View File
@@ -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;
};
+16 -9
View File
@@ -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
};
});
});
+2
View File
@@ -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({