mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
improvement: add more aggresive rate limiting on smtp endpoints
This commit is contained in:
@@ -11,7 +11,7 @@ export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
|
|||||||
return {
|
return {
|
||||||
errorResponseBuilder: (_, context) => {
|
errorResponseBuilder: (_, context) => {
|
||||||
throw new RateLimitError({
|
throw new RateLimitError({
|
||||||
message: `Rate limit exceeded. Please try again in ${context.after}`
|
message: `Rate limit exceeded. Please try again in ${Math.ceil(context.ttl / 1000)} seconds`
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
timeWindow: 60 * 1000,
|
timeWindow: 60 * 1000,
|
||||||
@@ -113,3 +113,12 @@ export const requestAccessLimit: RateLimitOptions = {
|
|||||||
max: 10,
|
max: 10,
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const smtpRateLimit = ({
|
||||||
|
keyGenerator = (req) => req.realIp
|
||||||
|
}: Pick<RateLimitOptions, "keyGenerator"> = {}): RateLimitOptions => ({
|
||||||
|
timeWindow: 20 * 1000,
|
||||||
|
hook: "preValidation",
|
||||||
|
max: 1,
|
||||||
|
keyGenerator
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { OrgMembershipRole, ProjectMembershipRole, UsersSchema } from "@app/db/schemas";
|
import { OrgMembershipRole, ProjectMembershipRole, UsersSchema } from "@app/db/schemas";
|
||||||
import { inviteUserRateLimit } from "@app/server/config/rateLimiter";
|
import { inviteUserRateLimit, smtpRateLimit } from "@app/server/config/rateLimiter";
|
||||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||||
@@ -11,7 +11,7 @@ export const registerInviteOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/signup",
|
url: "/signup",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: inviteUserRateLimit
|
rateLimit: smtpRateLimit()
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
schema: {
|
schema: {
|
||||||
@@ -81,7 +81,9 @@ export const registerInviteOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/signup-resend",
|
url: "/signup-resend",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: inviteUserRateLimit
|
rateLimit: smtpRateLimit({
|
||||||
|
keyGenerator: (req) => (req.body as { membershipId: string }).membershipId
|
||||||
|
})
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
schema: {
|
schema: {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { ProjectMembershipsSchema } from "@app/db/schemas";
|
import { ProjectMembershipsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, smtpRateLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
import { SanitizedProjectSchema } from "../sanitizedSchemas";
|
import { SanitizedProjectSchema } from "../sanitizedSchemas";
|
||||||
|
|
||||||
@@ -47,7 +47,9 @@ export const registerOrgAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/projects/:projectId/grant-admin-access",
|
url: "/projects/:projectId/grant-admin-access",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: writeLimit
|
rateLimit: smtpRateLimit({
|
||||||
|
keyGenerator: (req) => (req.auth.actor === ActorType.USER ? req.auth.userId : req.realIp)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { BackupPrivateKeySchema, UsersSchema } from "@app/db/schemas";
|
import { BackupPrivateKeySchema, UsersSchema } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { authRateLimit } from "@app/server/config/rateLimiter";
|
import { authRateLimit, smtpRateLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { validateSignUpAuthorization } from "@app/services/auth/auth-fns";
|
import { validateSignUpAuthorization } from "@app/services/auth/auth-fns";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { UserEncryption } from "@app/services/user/user-types";
|
import { UserEncryption } from "@app/services/user/user-types";
|
||||||
|
|
||||||
export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
||||||
@@ -80,7 +80,9 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/email/password-reset",
|
url: "/email/password-reset",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: authRateLimit
|
rateLimit: smtpRateLimit({
|
||||||
|
keyGenerator: (req) => (req.body as { email: string }).email
|
||||||
|
})
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
@@ -224,7 +226,9 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/email/password-setup",
|
url: "/email/password-setup",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: authRateLimit
|
rateLimit: smtpRateLimit({
|
||||||
|
keyGenerator: (req) => (req.auth.actor === ActorType.USER ? req.auth.userId : req.realIp)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
response: {
|
response: {
|
||||||
@@ -233,6 +237,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
await server.services.password.sendPasswordSetupEmail(req.permission);
|
await server.services.password.sendPasswordSetupEmail(req.permission);
|
||||||
|
|
||||||
@@ -267,6 +272,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
await server.services.password.setupPassword(req.body, req.permission);
|
await server.services.password.setupPassword(req.body, req.permission);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { AuthTokenSessionsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
import { AuthTokenSessionsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
||||||
import { ApiKeysSchema } from "@app/db/schemas/api-keys";
|
import { ApiKeysSchema } from "@app/db/schemas/api-keys";
|
||||||
import { authRateLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { authRateLimit, readLimit, smtpRateLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMethod, AuthMode, MfaMethod } from "@app/services/auth/auth-type";
|
import { AuthMethod, AuthMode, MfaMethod } from "@app/services/auth/auth-type";
|
||||||
import { sanitizedOrganizationSchema } from "@app/services/org/org-schema";
|
import { sanitizedOrganizationSchema } from "@app/services/org/org-schema";
|
||||||
@@ -12,7 +12,9 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/me/emails/code",
|
url: "/me/emails/code",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: authRateLimit
|
rateLimit: smtpRateLimit({
|
||||||
|
keyGenerator: (req) => (req.body as { username: string }).username
|
||||||
|
})
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { z } from "zod";
|
|||||||
import { UsersSchema } from "@app/db/schemas";
|
import { UsersSchema } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { ForbiddenRequestError } from "@app/lib/errors";
|
import { ForbiddenRequestError } from "@app/lib/errors";
|
||||||
import { authRateLimit } from "@app/server/config/rateLimiter";
|
import { authRateLimit, smtpRateLimit } from "@app/server/config/rateLimiter";
|
||||||
import { GenericResourceNameSchema } from "@app/server/lib/schemas";
|
import { GenericResourceNameSchema } from "@app/server/lib/schemas";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||||
@@ -13,7 +13,9 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
|
|||||||
url: "/email/signup",
|
url: "/email/signup",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: authRateLimit
|
rateLimit: smtpRateLimit({
|
||||||
|
keyGenerator: (req) => (req.body as { email: string }).email
|
||||||
|
})
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
|
|||||||
@@ -78,11 +78,14 @@ export default function CodeInputStep({
|
|||||||
const resendVerificationEmail = async () => {
|
const resendVerificationEmail = async () => {
|
||||||
setIsResendingVerificationEmail(true);
|
setIsResendingVerificationEmail(true);
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
await mutateAsync({ email });
|
try {
|
||||||
setTimeout(() => {
|
await mutateAsync({ email });
|
||||||
setIsLoading(false);
|
} finally {
|
||||||
setIsResendingVerificationEmail(false);
|
setTimeout(() => {
|
||||||
}, 2000);
|
setIsLoading(false);
|
||||||
|
setIsResendingVerificationEmail(false);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -22,8 +22,12 @@ export const VerifyEmailPage = () => {
|
|||||||
*/
|
*/
|
||||||
const sendVerificationEmail = async () => {
|
const sendVerificationEmail = async () => {
|
||||||
if (email) {
|
if (email) {
|
||||||
await mutateAsync({ email });
|
try {
|
||||||
setStep(2);
|
await mutateAsync({ email });
|
||||||
|
setStep(2);
|
||||||
|
} catch {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ export const AddOrgMemberModal = ({
|
|||||||
text: "Failed to invite user to org",
|
text: "Failed to invite user to org",
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverDetails?.emailConfigured) {
|
if (serverDetails?.emailConfigured) {
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ export const AddMemberModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
text: "Failed to add user to project",
|
text: "Failed to add user to project",
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
handlePopUpToggle("addMember", false);
|
handlePopUpToggle("addMember", false);
|
||||||
reset();
|
reset();
|
||||||
|
|||||||
Reference in New Issue
Block a user