diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts index 3282d946b8..076977b7c4 100644 --- a/apps/web/middleware.ts +++ b/apps/web/middleware.ts @@ -2,6 +2,7 @@ import { collectEvents } from "next-collect/server"; import { NextMiddleware, NextResponse, userAgent } from "next/server"; import { CONSOLE_URL, WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants"; +import { isIpInBanlist } from "@calcom/lib/getIP"; import { extendEventData, nextCollectBasicSettings } from "@calcom/lib/telemetry"; const V2_WHITELIST = ["/settings/admin"]; @@ -9,13 +10,14 @@ const V2_WHITELIST = ["/settings/admin"]; const middleware: NextMiddleware = async (req) => { const url = req.nextUrl; - if (url.pathname.startsWith("/api/auth")) { + if (["/api/collect-events", "/api/auth"].some((p) => url.pathname.startsWith(p))) { const callbackUrl = url.searchParams.get("callbackUrl"); const { isBot } = userAgent(req); if ( isBot || - (callbackUrl && ![CONSOLE_URL, WEBAPP_URL, WEBSITE_URL].some((u) => callbackUrl.startsWith(u))) + (callbackUrl && ![CONSOLE_URL, WEBAPP_URL, WEBSITE_URL].some((u) => callbackUrl.startsWith(u))) || + isIpInBanlist(req) ) { // DDOS Prevention: Immediately end request with no response - Avoids a redirect as well initiated by NextAuth on invalid callback req.nextUrl.pathname = "/api/nope"; diff --git a/packages/lib/getIP.ts b/packages/lib/getIP.ts new file mode 100644 index 0000000000..342670d5ab --- /dev/null +++ b/packages/lib/getIP.ts @@ -0,0 +1,30 @@ +import type { NextApiRequest } from "next"; +import z from "zod"; + +export function parseIpFromHeaders(value: string | string[]) { + return Array.isArray(value) ? value[0] : value.split(",")[0]; +} + +/** + * Tries to extract IP address from a request + * @see https://github.com/vercel/examples/blob/main/edge-functions/ip-blocking/lib/get-ip.ts + **/ +export default function getIP(request: Request | NextApiRequest) { + const xff = + request instanceof Request ? request.headers.get("x-forwarded-for") : request.headers["x-forwarded-for"]; + + return xff ? parseIpFromHeaders(xff) : "127.0.0.1"; +} + +const banlistSchema = z.array(z.string()); + +export function isIpInBanlist(request: Request | NextApiRequest) { + const IP = getIP(request); + const rawBanListJson = process.env.IP_BANLIST || "[]"; + const banList = banlistSchema.parse(JSON.parse(rawBanListJson)); + if (banList.includes(IP)) { + console.log(`Found banned IP: ${IP} in IP_BANLIST`); + return true; + } + return false; +} diff --git a/turbo.json b/turbo.json index 2df8fc5ad5..4d6dbd1358 100644 --- a/turbo.json +++ b/turbo.json @@ -219,6 +219,7 @@ "$HUBSPOT_CLIENT_ID", "$HUBSPOT_CLIENT_SECRET", "$INTEGRATION_TEST_MODE", + "$IP_BANLIST", "$NEXT_PUBLIC_CONSOLE_URL", "$NEXT_PUBLIC_DEBUG", "$NEXT_PUBLIC_EMBED_LIB_URL",