Merge remote-tracking branch 'origin' into cert-mgmt

This commit is contained in:
Tuan Dang
2024-05-29 16:15:09 -07:00
4 changed files with 26 additions and 34 deletions

View File

@@ -8,6 +8,8 @@ import cors from "@fastify/cors";
import fastifyEtag from "@fastify/etag";
import fastifyFormBody from "@fastify/formbody";
import helmet from "@fastify/helmet";
import type { FastifyRateLimitOptions } from "@fastify/rate-limit";
import ratelimiter from "@fastify/rate-limit";
import fasitfy from "fastify";
import { Knex } from "knex";
import { Logger } from "pino";
@@ -17,6 +19,7 @@ import { getConfig } from "@app/lib/config/env";
import { TQueueServiceFactory } from "@app/queue";
import { TSmtpService } from "@app/services/smtp/smtp-service";
import { globalRateLimiterCfg } from "./config/rateLimiter";
import { fastifyErrHandler } from "./plugins/error-handler";
import { registerExternalNextjs } from "./plugins/external-nextjs";
import { serializerCompiler, validatorCompiler, ZodTypeProvider } from "./plugins/fastify-zod";
@@ -64,6 +67,10 @@ export const main = async ({ db, smtp, logger, queue, keyStore }: TMain) => {
await server.register(fastifyFormBody);
await server.register(fastifyErrHandler);
// Rate limiters and security headers
if (appCfg.isProductionMode) {
await server.register<FastifyRateLimitOptions>(ratelimiter, globalRateLimiterCfg());
}
await server.register(helmet, { contentSecurityPolicy: false });
await server.register(maintenanceMode);

View File

@@ -1,34 +1,20 @@
import type { RateLimitOptions, RateLimitPluginOptions } from "@fastify/rate-limit";
import { FastifyRequest } from "fastify";
import { Redis } from "ioredis";
import { getConfig } from "@app/lib/config/env";
import { ActorType } from "@app/services/auth/auth-type";
const getDistinctRequestActorId = (req: FastifyRequest) => {
if (req?.auth?.actor === ActorType.USER) {
return req.auth.user.username;
}
if (req?.auth?.actor === ActorType.IDENTITY) {
return `${req.auth.identityId}-machine-identity-`;
}
if (req?.auth?.actor === ActorType.SERVICE) {
return `${req.auth.serviceToken.id}-service-token`; // when user gets removed from system
}
return req.realIp;
};
export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
const appCfg = getConfig();
const redis = appCfg.isRedisConfigured
? new Redis(appCfg.REDIS_URL, { connectTimeout: 500, maxRetriesPerRequest: 1 })
: null;
return {
timeWindow: 60 * 1000,
max: 600,
redis,
allowList: (req) => req.url === "/healthcheck" || req.url === "/api/status",
keyGenerator: (req) => getDistinctRequestActorId(req)
keyGenerator: (req) => req.realIp
};
};
@@ -36,39 +22,39 @@ export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
export const readLimit: RateLimitOptions = {
timeWindow: 60 * 1000,
max: 600,
keyGenerator: (req) => getDistinctRequestActorId(req)
keyGenerator: (req) => req.realIp
};
// POST, PATCH, PUT, DELETE endpoints
export const writeLimit: RateLimitOptions = {
timeWindow: 60 * 1000,
max: 50,
keyGenerator: (req) => getDistinctRequestActorId(req)
keyGenerator: (req) => req.realIp
};
// special endpoints
export const secretsLimit: RateLimitOptions = {
// secrets, folders, secret imports
timeWindow: 60 * 1000,
max: 1000,
keyGenerator: (req) => getDistinctRequestActorId(req)
max: 60,
keyGenerator: (req) => req.realIp
};
export const authRateLimit: RateLimitOptions = {
timeWindow: 60 * 1000,
max: 60,
keyGenerator: (req) => getDistinctRequestActorId(req)
keyGenerator: (req) => req.realIp
};
export const inviteUserRateLimit: RateLimitOptions = {
timeWindow: 60 * 1000,
max: 30,
keyGenerator: (req) => getDistinctRequestActorId(req)
keyGenerator: (req) => req.realIp
};
export const creationLimit: RateLimitOptions = {
// identity, project, org
timeWindow: 60 * 1000,
max: 30,
keyGenerator: (req) => getDistinctRequestActorId(req)
keyGenerator: (req) => req.realIp
};

View File

@@ -1,4 +1,3 @@
import ratelimiter, { FastifyRateLimitOptions } from "@fastify/rate-limit";
import { Knex } from "knex";
import { z } from "zod";
@@ -62,7 +61,7 @@ import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { TQueueServiceFactory } from "@app/queue";
import { globalRateLimiterCfg, readLimit } from "@app/server/config/rateLimiter";
import { readLimit } from "@app/server/config/rateLimiter";
import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal";
import { apiKeyServiceFactory } from "@app/services/api-key/api-key-service";
import { authDALFactory } from "@app/services/auth/auth-dal";
@@ -875,11 +874,6 @@ export const registerRoutes = async (
user: userDAL
});
// Rate limiters and security headers
if (appCfg.isProductionMode) {
await server.register<FastifyRateLimitOptions>(ratelimiter, globalRateLimiterCfg());
}
await server.register(injectIdentity, { userDAL, serviceTokenDAL });
await server.register(injectPermission);
await server.register(injectAuditLogInfo);

View File

@@ -7,6 +7,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { ActorType } from "../auth/auth-type";
import { TProjectDALFactory } from "../project/project-dal";
@@ -166,11 +167,15 @@ export const serviceTokenServiceFactory = ({
const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceToken.secretHash);
if (!isMatch) throw new UnauthorizedError();
const updatedToken = await serviceTokenDAL.updateById(serviceToken.id, {
lastUsed: new Date()
});
// const updatedToken = await serviceTokenDAL.updateById(serviceToken.id, {
// lastUsed: new Date()
// });
return { ...serviceToken, lastUsed: updatedToken.lastUsed, orgId: project.orgId };
logger.info(
`fnValidateServiceToken: [serviceToken=${serviceToken.id}] [serviceTokenProjectId=${serviceToken.projectId}]`
);
return { ...serviceToken, lastUsed: serviceToken.lastUsed, orgId: project.orgId };
};
return {