From e73383cc79a05922fe313b4c72971b620e4cd090 Mon Sep 17 00:00:00 2001 From: Owen Date: Fri, 15 Aug 2025 16:53:30 -0700 Subject: [PATCH] Add auth to gerbil calls --- server/lib/exitNodes/exitNodes.ts | 7 ++++ server/routers/gerbil/receiveBandwidth.ts | 44 +++++++++++++++++++---- server/routers/gerbil/updateHolePunch.ts | 23 +++++++++++- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/server/lib/exitNodes/exitNodes.ts b/server/lib/exitNodes/exitNodes.ts index f49b9cdb..f607371d 100644 --- a/server/lib/exitNodes/exitNodes.ts +++ b/server/lib/exitNodes/exitNodes.ts @@ -53,3 +53,10 @@ export function selectBestExitNode( return pingResults[0]; } + +export async function checkExitNodeOrg( + exitNodeId: number, + orgId: string +) { + return false; +} \ No newline at end of file diff --git a/server/routers/gerbil/receiveBandwidth.ts b/server/routers/gerbil/receiveBandwidth.ts index 350228ec..fb7723ee 100644 --- a/server/routers/gerbil/receiveBandwidth.ts +++ b/server/routers/gerbil/receiveBandwidth.ts @@ -6,6 +6,7 @@ import logger from "@server/logger"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; +import { checkExitNodeOrg } from "@server/lib/exitNodes"; // Track sites that are already offline to avoid unnecessary queries const offlineSites = new Set(); @@ -48,7 +49,10 @@ export const receiveBandwidth = async ( } }; -export async function updateSiteBandwidth(bandwidthData: PeerBandwidth[]) { +export async function updateSiteBandwidth( + bandwidthData: PeerBandwidth[], + exitNodeId?: number +) { const currentTime = new Date(); const oneMinuteAgo = new Date(currentTime.getTime() - 60000); // 1 minute ago @@ -69,7 +73,7 @@ export async function updateSiteBandwidth(bandwidthData: PeerBandwidth[]) { // Update all active sites with bandwidth data and get the site data in one operation const updatedSites = []; for (const peer of activePeers) { - const updatedSite = await trx + const [updatedSite] = await trx .update(sites) .set({ megabytesOut: sql`${sites.megabytesOut} + ${peer.bytesIn}`, @@ -85,8 +89,19 @@ export async function updateSiteBandwidth(bandwidthData: PeerBandwidth[]) { lastBandwidthUpdate: sites.lastBandwidthUpdate }); - if (updatedSite.length > 0) { - updatedSites.push({ ...updatedSite[0], peer }); + if (exitNodeId) { + if (await checkExitNodeOrg(exitNodeId, updatedSite.orgId)) { + // not allowed + logger.warn( + `Exit node ${exitNodeId} is not allowed for org ${updatedSite.orgId}` + ); + // THIS SHOULD TRIGGER THE TRANSACTION TO FAIL? + throw new Error("Exit node not allowed"); + } + } + + if (updatedSite) { + updatedSites.push({ ...updatedSite, peer }); } } @@ -138,12 +153,29 @@ export async function updateSiteBandwidth(bandwidthData: PeerBandwidth[]) { // Always update lastBandwidthUpdate to show this instance is receiving reports // Only update online status if it changed if (site.online !== newOnlineStatus) { - await trx + const [updatedSite] = await trx .update(sites) .set({ online: newOnlineStatus }) - .where(eq(sites.siteId, site.siteId)); + .where(eq(sites.siteId, site.siteId)) + .returning(); + + if (exitNodeId) { + if ( + await checkExitNodeOrg( + exitNodeId, + updatedSite.orgId + ) + ) { + // not allowed + logger.warn( + `Exit node ${exitNodeId} is not allowed for org ${updatedSite.orgId}` + ); + // THIS SHOULD TRIGGER THE TRANSACTION TO FAIL? + throw new Error("Exit node not allowed"); + } + } // If site went offline, add it to our tracking set if (!newOnlineStatus && site.pubKey) { diff --git a/server/routers/gerbil/updateHolePunch.ts b/server/routers/gerbil/updateHolePunch.ts index 0eaa447e..9e2ec8b8 100644 --- a/server/routers/gerbil/updateHolePunch.ts +++ b/server/routers/gerbil/updateHolePunch.ts @@ -19,6 +19,7 @@ import { fromError } from "zod-validation-error"; import { validateNewtSessionToken } from "@server/auth/sessions/newt"; import { validateOlmSessionToken } from "@server/auth/sessions/olm"; import axios from "axios"; +import { checkExitNodeOrg } from "@server/lib/exitNodes"; // Define Zod schema for request validation const updateHolePunchSchema = z.object({ @@ -157,7 +158,13 @@ export async function updateAndGenerateEndpointDestinations( .where(eq(clients.clientId, olm.clientId)) .returning(); - + if (await checkExitNodeOrg(exitNode.exitNodeId, client.orgId)) { + // not allowed + logger.warn( + `Exit node ${exitNode.exitNodeId} is not allowed for org ${client.orgId}` + ); + throw new Error("Exit node not allowed"); + } // Get sites that are on this specific exit node and connected to this client const sitesOnExitNode = await db @@ -240,6 +247,20 @@ export async function updateAndGenerateEndpointDestinations( throw new Error("Newt not found"); } + const [site] = await db + .select() + .from(sites) + .where(eq(sites.siteId, newt.siteId)) + .limit(1); + + if (await checkExitNodeOrg(exitNode.exitNodeId, site.orgId)) { + // not allowed + logger.warn( + `Exit node ${exitNode.exitNodeId} is not allowed for org ${site.orgId}` + ); + throw new Error("Exit node not allowed"); + } + currentSiteId = newt.siteId; // Update the current site with the new endpoint