From 035156bcc3c3912ac199584d72fe188f599aad09 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 4 Sep 2025 21:58:21 +0530 Subject: [PATCH] feat: primary forwarding mode completed --- backend/package-lock.json | 44 +++++++++++++++++-- backend/package.json | 1 + backend/src/@types/fastify.d.ts | 1 + backend/src/lib/config/env.ts | 2 + .../server/plugins/auth/inject-identity.ts | 7 ++- .../src/server/plugins/auth/verify-auth.ts | 4 ++ .../server/plugins/primary-forwarding-mode.ts | 14 ++++++ backend/src/server/routes/index.ts | 9 +++- 8 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 backend/src/server/plugins/primary-forwarding-mode.ts diff --git a/backend/package-lock.json b/backend/package-lock.json index f22fd0b7b..c6ac0b147 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -25,6 +25,7 @@ "@fastify/multipart": "8.3.1", "@fastify/passport": "^2.4.0", "@fastify/rate-limit": "^9.0.0", + "@fastify/reply-from": "^9.8.0", "@fastify/request-context": "^5.1.0", "@fastify/session": "^10.7.0", "@fastify/static": "^7.0.4", @@ -8044,6 +8045,42 @@ "toad-cache": "^3.3.0" } }, + "node_modules/@fastify/reply-from": { + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/@fastify/reply-from/-/reply-from-9.8.0.tgz", + "integrity": "sha512-bPNVaFhEeNI0Lyl6404YZaPFokudCplidE3QoOcr78yOy6H9sYw97p5KPYvY/NJNUHfFtvxOaSAHnK+YSiv/Mg==", + "license": "MIT", + "dependencies": { + "@fastify/error": "^3.0.0", + "end-of-stream": "^1.4.4", + "fast-content-type-parse": "^1.1.0", + "fast-querystring": "^1.0.0", + "fastify-plugin": "^4.0.0", + "toad-cache": "^3.7.0", + "undici": "^5.19.1" + } + }, + "node_modules/@fastify/reply-from/node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@fastify/reply-from/node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/@fastify/request-context": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@fastify/request-context/-/request-context-5.1.0.tgz", @@ -29330,9 +29367,10 @@ } }, "node_modules/toad-cache": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.3.0.tgz", - "integrity": "sha512-3oDzcogWGHZdkwrHyvJVpPjA7oNzY6ENOV3PsWJY9XYPZ6INo94Yd47s5may1U+nleBPwDhrRiTPMIvKaa3MQg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "license": "MIT", "engines": { "node": ">=12" } diff --git a/backend/package.json b/backend/package.json index aaef9f567..0c8464eaf 100644 --- a/backend/package.json +++ b/backend/package.json @@ -145,6 +145,7 @@ "@fastify/multipart": "8.3.1", "@fastify/passport": "^2.4.0", "@fastify/rate-limit": "^9.0.0", + "@fastify/reply-from": "^9.8.0", "@fastify/request-context": "^5.1.0", "@fastify/session": "^10.7.0", "@fastify/static": "^7.0.4", diff --git a/backend/src/@types/fastify.d.ts b/backend/src/@types/fastify.d.ts index 47973cc09..153700463 100644 --- a/backend/src/@types/fastify.d.ts +++ b/backend/src/@types/fastify.d.ts @@ -162,6 +162,7 @@ declare module "fastify" { }; // identity injection. depending on which kinda of token the information is filled in auth auth: TAuthMode; + isPrimaryForwardingMode: boolean; permission: { authMethod: ActorAuthMethod; type: ActorType; diff --git a/backend/src/lib/config/env.ts b/backend/src/lib/config/env.ts index 586a69655..7add25e70 100644 --- a/backend/src/lib/config/env.ts +++ b/backend/src/lib/config/env.ts @@ -218,6 +218,8 @@ const envSchema = z ), PARAMS_FOLDER_SECRET_DETECTION_ENTROPY: z.coerce.number().optional().default(3.7), + INFISICAL_PRIMARY_URL: zpStr(z.string().optional()), + // HSM HSM_LIB_PATH: zpStr(z.string().optional()), HSM_PIN: zpStr(z.string().optional()), diff --git a/backend/src/server/plugins/auth/inject-identity.ts b/backend/src/server/plugins/auth/inject-identity.ts index 97c62b545..28bfd8814 100644 --- a/backend/src/server/plugins/auth/inject-identity.ts +++ b/backend/src/server/plugins/auth/inject-identity.ts @@ -107,11 +107,16 @@ export const extractAuth = async (req: FastifyRequest, jwtSecret: string) => { }; // ! Important: You can only 100% count on the `req.permission.orgId` field being present when the auth method is Identity Access Token (Machine Identity). -export const injectIdentity = fp(async (server: FastifyZodProvider) => { +export const injectIdentity = fp(async (server: FastifyZodProvider, opt: { isPrimaryForwardingMode?: boolean }) => { server.decorateRequest("auth", null); + server.decorateRequest("isPrimaryForwardingMode", Boolean(opt.isPrimaryForwardingMode)); server.addHook("onRequest", async (req) => { const appCfg = getConfig(); + if (opt.isPrimaryForwardingMode && req.method !== "GET") { + return; + } + if (req.url.includes(".well-known/est") || req.url.includes("/api/v3/auth/")) { return; } diff --git a/backend/src/server/plugins/auth/verify-auth.ts b/backend/src/server/plugins/auth/verify-auth.ts index 44ea069dc..0358a8117 100644 --- a/backend/src/server/plugins/auth/verify-auth.ts +++ b/backend/src/server/plugins/auth/verify-auth.ts @@ -10,6 +10,10 @@ interface TAuthOptions { export const verifyAuth = (authStrategies: AuthMode[], options: TAuthOptions = { requireOrg: true }) => (req: T, _res: FastifyReply, done: HookHandlerDoneFunction) => { + if (req.isPrimaryForwardingMode && req.method !== "GET") { + return done(); + } + if (!Array.isArray(authStrategies)) throw new Error("Auth strategy must be array"); if (!req.auth) throw new UnauthorizedError({ message: "Token missing" }); diff --git a/backend/src/server/plugins/primary-forwarding-mode.ts b/backend/src/server/plugins/primary-forwarding-mode.ts new file mode 100644 index 000000000..c609bbf64 --- /dev/null +++ b/backend/src/server/plugins/primary-forwarding-mode.ts @@ -0,0 +1,14 @@ +import replyFrom from "@fastify/reply-from"; +import fp from "fastify-plugin"; + +export const primaryForwardingMode = fp(async (server, opt: { primaryUrl: string }) => { + await server.register(replyFrom, { + base: opt.primaryUrl + }); + + server.addHook("preValidation", async (request, reply) => { + if (request.url.startsWith("/api") && ["POST", "PUT", "DELETE", "PATCH"].includes(request.method)) { + return reply.from(request.url); + } + }); +}); diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 9af4f6ade..f932d34ca 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -312,6 +312,7 @@ import { injectAssumePrivilege } from "../plugins/auth/inject-assume-privilege"; import { injectIdentity } from "../plugins/auth/inject-identity"; import { injectPermission } from "../plugins/auth/inject-permission"; import { injectRateLimits } from "../plugins/inject-rate-limits"; +import { primaryForwardingMode } from "../plugins/primary-forwarding-mode"; import { registerV1Routes } from "./v1"; import { initializeOauthConfigSync } from "./v1/sso-router"; import { registerV2Routes } from "./v2"; @@ -2146,8 +2147,14 @@ export const registerRoutes = async ( user: userDAL, kmipClient: kmipClientDAL }); + const isPrimaryForwardingMode = Boolean(envConfig.INFISICAL_PRIMARY_URL); + if (isPrimaryForwardingMode) { + logger.info(`Infisical primary instance is configured: ${envConfig.INFISICAL_PRIMARY_URL}`); - await server.register(injectIdentity, { userDAL, serviceTokenDAL }); + await server.register(primaryForwardingMode, { primaryUrl: envConfig.INFISICAL_PRIMARY_URL as string }); + } + + await server.register(injectIdentity, { isPrimaryForwardingMode }); await server.register(injectAssumePrivilege); await server.register(injectPermission); await server.register(injectRateLimits);