mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-05 20:21:19 +02:00
Rename subnet for clarity, pick subnet on client
This commit is contained in:
@@ -107,7 +107,7 @@ export const sites = sqliteTable("sites", {
|
|||||||
}),
|
}),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
pubKey: text("pubKey"),
|
pubKey: text("pubKey"),
|
||||||
subnet: text("subnet"),
|
exitNodeSubnet: text("exitNodeSubnet"),
|
||||||
megabytesIn: integer("bytesIn").default(0),
|
megabytesIn: integer("bytesIn").default(0),
|
||||||
megabytesOut: integer("bytesOut").default(0),
|
megabytesOut: integer("bytesOut").default(0),
|
||||||
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
||||||
@@ -599,6 +599,7 @@ export const clients = sqliteTable("clients", {
|
|||||||
pubKey: text("pubKey"),
|
pubKey: text("pubKey"),
|
||||||
olmId: text("olmId"), // to lock it to a specific olm optionally
|
olmId: text("olmId"), // to lock it to a specific olm optionally
|
||||||
subnet: text("subnet").notNull(),
|
subnet: text("subnet").notNull(),
|
||||||
|
exitNodeSubnet: text("exitNodeSubnet"), // this is the subnet when connecting to an exit node
|
||||||
megabytesIn: integer("bytesIn"),
|
megabytesIn: integer("bytesIn"),
|
||||||
megabytesOut: integer("bytesOut"),
|
megabytesOut: integer("bytesOut"),
|
||||||
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { db, ExitNode, Transaction, sites, clients } from "@server/db";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import config from "@server/lib/config";
|
||||||
|
import { findNextAvailableCidr } from "@server/lib/ip";
|
||||||
|
import { lockManager } from "#dynamic/lib/lock";
|
||||||
|
|
||||||
|
export async function getUniqueSubnetForExitNode(
|
||||||
|
exitNode: ExitNode,
|
||||||
|
trx: Transaction | typeof db = db
|
||||||
|
): Promise<string | null> {
|
||||||
|
const lockKey = `subnet-allocation:${exitNode.exitNodeId}`;
|
||||||
|
|
||||||
|
return await lockManager.withLock(
|
||||||
|
lockKey,
|
||||||
|
async () => {
|
||||||
|
const [sitesQuery, clientsQuery] = await Promise.all([
|
||||||
|
trx
|
||||||
|
.select({ subnet: sites.exitNodeSubnet })
|
||||||
|
.from(sites)
|
||||||
|
.where(eq(sites.exitNodeId, exitNode.exitNodeId)),
|
||||||
|
trx
|
||||||
|
.select({ subnet: clients.exitNodeSubnet })
|
||||||
|
.from(clients)
|
||||||
|
.where(eq(clients.exitNodeId, exitNode.exitNodeId))
|
||||||
|
]);
|
||||||
|
|
||||||
|
const blockSize = config.getRawConfig().gerbil.site_block_size;
|
||||||
|
const subnets = [...sitesQuery, ...clientsQuery]
|
||||||
|
.map((row) => row.subnet)
|
||||||
|
.filter(
|
||||||
|
(subnet): subnet is string =>
|
||||||
|
!!subnet &&
|
||||||
|
/^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet)
|
||||||
|
);
|
||||||
|
subnets.push(exitNode.address.replace(/\/\d+$/, `/${blockSize}`));
|
||||||
|
|
||||||
|
return findNextAvailableCidr(subnets, blockSize, exitNode.address);
|
||||||
|
},
|
||||||
|
5000 // 5 second lock TTL - subnet allocation should be quick
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,3 +3,4 @@ export * from "./exitNodeComms";
|
|||||||
export * from "./subnet";
|
export * from "./subnet";
|
||||||
export * from "./getCurrentExitNodeId";
|
export * from "./getCurrentExitNodeId";
|
||||||
export * from "./calculateExitNodeWeight";
|
export * from "./calculateExitNodeWeight";
|
||||||
|
export * from "./getUniqueSubnetForExitNode";
|
||||||
|
|||||||
@@ -966,7 +966,7 @@ export async function updateClientSiteDestinations(
|
|||||||
.where(eq(clientSitesAssociationsCache.clientId, client.clientId));
|
.where(eq(clientSitesAssociationsCache.clientId, client.clientId));
|
||||||
|
|
||||||
for (const site of sitesData) {
|
for (const site of sitesData) {
|
||||||
if (!site.sites.subnet) {
|
if (!site.sites.exitNodeSubnet) {
|
||||||
logger.debug(`Site ${site.sites.siteId} has no subnet, skipping`);
|
logger.debug(`Site ${site.sites.siteId} has no subnet, skipping`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1002,7 +1002,7 @@ export async function updateClientSiteDestinations(
|
|||||||
sourcePort: parsedEndpoint.port,
|
sourcePort: parsedEndpoint.port,
|
||||||
destinations: [
|
destinations: [
|
||||||
{
|
{
|
||||||
destinationIP: site.sites.subnet.split("/")[0],
|
destinationIP: site.sites.exitNodeSubnet.split("/")[0],
|
||||||
destinationPort: site.sites.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
destinationPort: site.sites.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1010,7 +1010,7 @@ export async function updateClientSiteDestinations(
|
|||||||
} else {
|
} else {
|
||||||
// add to the existing destinations
|
// add to the existing destinations
|
||||||
destinations.destinations.push({
|
destinations.destinations.push({
|
||||||
destinationIP: site.sites.subnet.split("/")[0],
|
destinationIP: site.sites.exitNodeSubnet.split("/")[0],
|
||||||
destinationPort: site.sites.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
destinationPort: site.sites.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export async function getTraefikConfig(
|
|||||||
siteId: sites.siteId,
|
siteId: sites.siteId,
|
||||||
siteType: sites.type,
|
siteType: sites.type,
|
||||||
siteOnline: sites.online,
|
siteOnline: sites.online,
|
||||||
subnet: sites.subnet,
|
subnet: sites.exitNodeSubnet,
|
||||||
exitNodeId: sites.exitNodeId,
|
exitNodeId: sites.exitNodeId,
|
||||||
// Domain cert resolver fields
|
// Domain cert resolver fields
|
||||||
domainCertResolver: domains.certResolver,
|
domainCertResolver: domains.certResolver,
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ export async function getTraefikConfig(
|
|||||||
siteId: sites.siteId,
|
siteId: sites.siteId,
|
||||||
siteType: sites.type,
|
siteType: sites.type,
|
||||||
siteOnline: sites.online,
|
siteOnline: sites.online,
|
||||||
subnet: sites.subnet,
|
subnet: sites.exitNodeSubnet,
|
||||||
exitNodeId: sites.exitNodeId,
|
exitNodeId: sites.exitNodeId,
|
||||||
// Namespace
|
// Namespace
|
||||||
domainNamespaceId: domainNamespaces.domainNamespaceId,
|
domainNamespaceId: domainNamespaces.domainNamespaceId,
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ export async function reGenerateSiteSecret(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (site.exitNodeId && site.subnet) {
|
if (site.exitNodeId && site.exitNodeSubnet) {
|
||||||
await deletePeer(site.exitNodeId, site.pubKey!); // the old pubkey
|
await deletePeer(site.exitNodeId, site.pubKey!); // the old pubkey
|
||||||
await addPeer(site.exitNodeId, {
|
await addPeer(site.exitNodeId, {
|
||||||
publicKey: pubKey,
|
publicKey: pubKey,
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export async function generateRelayMappings(exitNode: ExitNode) {
|
|||||||
// Filter to sites with the required fields up front so the rest of the
|
// Filter to sites with the required fields up front so the rest of the
|
||||||
// function can safely treat endpoint/subnet/listenPort as defined.
|
// function can safely treat endpoint/subnet/listenPort as defined.
|
||||||
const validSites = sitesRes.filter(
|
const validSites = sitesRes.filter(
|
||||||
(s) => s.endpoint && s.subnet && s.listenPort
|
(s) => s.endpoint && s.exitNodeSubnet && s.listenPort
|
||||||
);
|
);
|
||||||
|
|
||||||
if (validSites.length === 0) {
|
if (validSites.length === 0) {
|
||||||
@@ -136,7 +136,7 @@ export async function generateRelayMappings(exitNode: ExitNode) {
|
|||||||
if (
|
if (
|
||||||
peer.orgId == null ||
|
peer.orgId == null ||
|
||||||
!peer.endpoint ||
|
!peer.endpoint ||
|
||||||
!peer.subnet ||
|
!peer.exitNodeSubnet ||
|
||||||
!peer.listenPort
|
!peer.listenPort
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
@@ -183,7 +183,7 @@ export async function generateRelayMappings(exitNode: ExitNode) {
|
|||||||
// Process each site using the pre-fetched data.
|
// Process each site using the pre-fetched data.
|
||||||
for (const site of validSites) {
|
for (const site of validSites) {
|
||||||
const siteDestination: PeerDestination = {
|
const siteDestination: PeerDestination = {
|
||||||
destinationIP: site.subnet!.split("/")[0],
|
destinationIP: site.exitNodeSubnet!.split("/")[0],
|
||||||
destinationPort: site.listenPort! || 1 // this satisfies gerbil for now but should be reevaluated
|
destinationPort: site.listenPort! || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ export async function generateRelayMappings(exitNode: ExitNode) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
addDestination(site.endpoint!, {
|
addDestination(site.endpoint!, {
|
||||||
destinationIP: peer.subnet!.split("/")[0],
|
destinationIP: peer.exitNodeSubnet!.split("/")[0],
|
||||||
destinationPort: peer.listenPort! || 1 // this satisfies gerbil for now but should be reevaluated
|
destinationPort: peer.listenPort! || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export async function generateGerbilConfig(exitNode: ExitNode) {
|
|||||||
and(
|
and(
|
||||||
eq(sites.exitNodeId, exitNode.exitNodeId),
|
eq(sites.exitNodeId, exitNode.exitNodeId),
|
||||||
isNotNull(sites.pubKey),
|
isNotNull(sites.pubKey),
|
||||||
isNotNull(sites.subnet)
|
isNotNull(sites.exitNodeSubnet)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ export async function generateGerbilConfig(exitNode: ExitNode) {
|
|||||||
} else if (site.type === "newt") {
|
} else if (site.type === "newt") {
|
||||||
return {
|
return {
|
||||||
publicKey: site.pubKey,
|
publicKey: site.pubKey,
|
||||||
allowedIps: [site.subnet!]
|
allowedIps: [site.exitNodeSubnet!]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ export async function updateAndGenerateEndpointDestinations(
|
|||||||
.select({
|
.select({
|
||||||
siteId: sites.siteId,
|
siteId: sites.siteId,
|
||||||
newtId: newts.newtId,
|
newtId: newts.newtId,
|
||||||
subnet: sites.subnet,
|
subnet: sites.exitNodeSubnet,
|
||||||
listenPort: sites.listenPort,
|
listenPort: sites.listenPort,
|
||||||
publicKey: sites.publicKey,
|
publicKey: sites.publicKey,
|
||||||
endpoint: clientSitesAssociationsCache.endpoint,
|
endpoint: clientSitesAssociationsCache.endpoint,
|
||||||
|
|||||||
@@ -95,16 +95,16 @@ export const handleNewtGetConfigMessage: MessageHandler = async (context) => {
|
|||||||
.limit(1);
|
.limit(1);
|
||||||
if (
|
if (
|
||||||
exitNode.reachableAt &&
|
exitNode.reachableAt &&
|
||||||
existingSite.subnet &&
|
existingSite.exitNodeSubnet &&
|
||||||
existingSite.listenPort
|
existingSite.listenPort
|
||||||
) {
|
) {
|
||||||
const payload = {
|
const payload = {
|
||||||
oldDestination: {
|
oldDestination: {
|
||||||
destinationIP: existingSite.subnet?.split("/")[0],
|
destinationIP: existingSite.exitNodeSubnet?.split("/")[0],
|
||||||
destinationPort: existingSite.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
destinationPort: existingSite.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
},
|
},
|
||||||
newDestination: {
|
newDestination: {
|
||||||
destinationIP: site.subnet?.split("/")[0],
|
destinationIP: site.exitNodeSubnet?.split("/")[0],
|
||||||
destinationPort: site.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
destinationPort: site.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -132,7 +132,10 @@ export const handleNewtGetConfigMessage: MessageHandler = async (context) => {
|
|||||||
({ targets: dedupedTargets, certs } = dedupeCertsForTargets(targets));
|
({ targets: dedupedTargets, certs } = dedupeCertsForTargets(targets));
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetsToSend = await convertTargetsIfNecessary(newt.newtId, dedupedTargets); // for backward compatibility with old newt versions that don't support the new target format
|
const targetsToSend = await convertTargetsIfNecessary(
|
||||||
|
newt.newtId,
|
||||||
|
dedupedTargets
|
||||||
|
); // for backward compatibility with old newt versions that don't support the new target format
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: {
|
message: {
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
import { db, ExitNode, newts, remoteExitNodes, Transaction } from "@server/db";
|
import { db, newts, remoteExitNodes } from "@server/db";
|
||||||
import { MessageHandler } from "@server/routers/ws";
|
import { MessageHandler } from "@server/routers/ws";
|
||||||
import { exitNodes, Newt, sites } from "@server/db";
|
import { exitNodes, Newt, sites } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { addPeer, deletePeer } from "../gerbil/peers";
|
import { addPeer, deletePeer } from "../gerbil/peers";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import { findNextAvailableCidr } from "@server/lib/ip";
|
|
||||||
import {
|
import {
|
||||||
ExitNodePingResult,
|
ExitNodePingResult,
|
||||||
selectBestExitNode,
|
selectBestExitNode,
|
||||||
verifyExitNodeOrgAccess
|
verifyExitNodeOrgAccess
|
||||||
} from "#dynamic/lib/exitNodes";
|
} from "#dynamic/lib/exitNodes";
|
||||||
|
import { getUniqueSubnetForExitNode } from "@server/lib/exitNodes";
|
||||||
import { fetchContainers } from "./dockerSocket";
|
import { fetchContainers } from "./dockerSocket";
|
||||||
import { lockManager } from "#dynamic/lib/lock";
|
|
||||||
import { buildTargetConfigurationForNewtClient } from "./buildConfiguration";
|
import { buildTargetConfigurationForNewtClient } from "./buildConfiguration";
|
||||||
import { canCompress } from "@server/lib/clientVersionChecks";
|
import { canCompress } from "@server/lib/clientVersionChecks";
|
||||||
|
|
||||||
@@ -85,9 +84,12 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => {
|
|||||||
fetchContainers(newt.newtId);
|
fetchContainers(newt.newtId);
|
||||||
}
|
}
|
||||||
|
|
||||||
let siteSubnet = oldSite.subnet;
|
let siteSubnet = oldSite.exitNodeSubnet;
|
||||||
let exitNodeIdToQuery = oldSite.exitNodeId;
|
let exitNodeIdToQuery = oldSite.exitNodeId;
|
||||||
if (exitNodeId && (oldSite.exitNodeId !== exitNodeId || !oldSite.subnet)) {
|
if (
|
||||||
|
exitNodeId &&
|
||||||
|
(oldSite.exitNodeId !== exitNodeId || !oldSite.exitNodeSubnet)
|
||||||
|
) {
|
||||||
// This effectively moves the exit node to the new one
|
// This effectively moves the exit node to the new one
|
||||||
exitNodeIdToQuery = exitNodeId; // Use the provided exitNodeId if it differs from the site's exitNodeId
|
exitNodeIdToQuery = exitNodeId; // Use the provided exitNodeId if it differs from the site's exitNodeId
|
||||||
|
|
||||||
@@ -106,7 +108,7 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newSubnet = await getUniqueSubnetForSite(exitNode);
|
const newSubnet = await getUniqueSubnetForExitNode(exitNode);
|
||||||
|
|
||||||
if (!newSubnet) {
|
if (!newSubnet) {
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -122,7 +124,7 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => {
|
|||||||
.set({
|
.set({
|
||||||
pubKey: publicKey,
|
pubKey: publicKey,
|
||||||
exitNodeId: exitNodeId,
|
exitNodeId: exitNodeId,
|
||||||
subnet: newSubnet
|
exitNodeSubnet: newSubnet
|
||||||
})
|
})
|
||||||
.where(eq(sites.siteId, siteId))
|
.where(eq(sites.siteId, siteId))
|
||||||
.returning();
|
.returning();
|
||||||
@@ -241,40 +243,3 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => {
|
|||||||
excludeSender: false // Include sender in broadcast
|
excludeSender: false // Include sender in broadcast
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getUniqueSubnetForSite(
|
|
||||||
exitNode: ExitNode,
|
|
||||||
trx: Transaction | typeof db = db
|
|
||||||
): Promise<string | null> {
|
|
||||||
const lockKey = `subnet-allocation:${exitNode.exitNodeId}`;
|
|
||||||
|
|
||||||
return await lockManager.withLock(
|
|
||||||
lockKey,
|
|
||||||
async () => {
|
|
||||||
const sitesQuery = await trx
|
|
||||||
.select({
|
|
||||||
subnet: sites.subnet
|
|
||||||
})
|
|
||||||
.from(sites)
|
|
||||||
.where(eq(sites.exitNodeId, exitNode.exitNodeId));
|
|
||||||
|
|
||||||
const blockSize = config.getRawConfig().gerbil.site_block_size;
|
|
||||||
const subnets = sitesQuery
|
|
||||||
.map((site) => site.subnet)
|
|
||||||
.filter(
|
|
||||||
(subnet) =>
|
|
||||||
subnet &&
|
|
||||||
/^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet)
|
|
||||||
)
|
|
||||||
.filter((subnet) => subnet !== null);
|
|
||||||
subnets.push(exitNode.address.replace(/\/\d+$/, `/${blockSize}`));
|
|
||||||
const newSubnet = findNextAvailableCidr(
|
|
||||||
subnets,
|
|
||||||
blockSize,
|
|
||||||
exitNode.address
|
|
||||||
);
|
|
||||||
return newSubnet;
|
|
||||||
},
|
|
||||||
5000 // 5 second lock TTL - subnet allocation should be quick
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ export async function buildSiteConfigurationForOlmClient(
|
|||||||
peerOps.push(deletePeer(site.siteId, client.pubKey!));
|
peerOps.push(deletePeer(site.siteId, client.pubKey!));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!site.subnet) {
|
if (!site.exitNodeSubnet) {
|
||||||
logger.debug(`Site ${site.siteId} has no subnet, skipping`);
|
logger.debug(`Site ${site.siteId} has no subnet, skipping`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,12 @@ import { canCompress } from "@server/lib/clientVersionChecks";
|
|||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import cache from "#dynamic/lib/cache"; // not using regional here because we need this in the register message handler before we know where the client is
|
import cache from "#dynamic/lib/cache"; // not using regional here because we need this in the register message handler before we know where the client is
|
||||||
import { waitForClientRebuildIdle } from "@server/lib/rebuildClientAssociations";
|
import { waitForClientRebuildIdle } from "@server/lib/rebuildClientAssociations";
|
||||||
import { ExitNodePingResult, selectBestExitNode } from "#dynamic/lib/exitNodes";
|
import {
|
||||||
|
ExitNodePingResult,
|
||||||
|
selectBestExitNode,
|
||||||
|
verifyExitNodeOrgAccess
|
||||||
|
} from "#dynamic/lib/exitNodes";
|
||||||
|
import { getUniqueSubnetForExitNode } from "@server/lib/exitNodes";
|
||||||
|
|
||||||
const HOLEPUNCH_STALE_CHAIN_THRESHOLD = 18;
|
const HOLEPUNCH_STALE_CHAIN_THRESHOLD = 18;
|
||||||
const HOLEPUNCH_STALE_CHAIN_TTL_SECONDS = 1800;
|
const HOLEPUNCH_STALE_CHAIN_TTL_SECONDS = 1800;
|
||||||
@@ -297,10 +302,50 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
|
|||||||
exitNodeId = bestPingResult?.exitNodeId;
|
exitNodeId = bestPingResult?.exitNodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let clientSubnet = client.exitNodeSubnet;
|
||||||
|
if (
|
||||||
|
exitNodeId &&
|
||||||
|
(client.exitNodeId !== exitNodeId || !client.exitNodeSubnet)
|
||||||
|
) {
|
||||||
|
const { exitNode, hasAccess } = await verifyExitNodeOrgAccess(
|
||||||
|
exitNodeId,
|
||||||
|
client.orgId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!exitNode) {
|
||||||
|
logger.warn("[handleOlmRegisterMessage] Exit node not found", {
|
||||||
|
orgId: client.orgId,
|
||||||
|
clientId: client.clientId
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasAccess) {
|
||||||
|
logger.warn(
|
||||||
|
"[handleOlmRegisterMessage] Not authorized to use this exit node",
|
||||||
|
{ orgId: client.orgId, clientId: client.clientId }
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newSubnet = await getUniqueSubnetForExitNode(exitNode);
|
||||||
|
|
||||||
|
if (!newSubnet) {
|
||||||
|
logger.error(
|
||||||
|
`[handleOlmRegisterMessage] No available subnets found for exit node id ${exitNodeId} and client id ${client.clientId}`,
|
||||||
|
{ orgId: client.orgId, clientId: client.clientId }
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientSubnet = newSubnet;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
client.pubKey !== publicKey ||
|
client.pubKey !== publicKey ||
|
||||||
client.archived ||
|
client.archived ||
|
||||||
client.exitNodeId !== exitNodeId
|
client.exitNodeId !== exitNodeId ||
|
||||||
|
client.exitNodeSubnet !== clientSubnet
|
||||||
) {
|
) {
|
||||||
logger.info(
|
logger.info(
|
||||||
"[handleOlmRegisterMessage] Public key mismatch. Updating public key and clearing session info...",
|
"[handleOlmRegisterMessage] Public key mismatch. Updating public key and clearing session info...",
|
||||||
@@ -312,7 +357,8 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
|
|||||||
.set({
|
.set({
|
||||||
pubKey: publicKey,
|
pubKey: publicKey,
|
||||||
archived: false,
|
archived: false,
|
||||||
exitNodeId: exitNodeId // this can be undefined if no exit node was selected, which is fine just means we cant talk to the node or connect to it
|
exitNodeId: exitNodeId, // this can be undefined if no exit node was selected, which is fine just means we cant talk to the node or connect to it
|
||||||
|
exitNodeSubnet: clientSubnet
|
||||||
})
|
})
|
||||||
.where(eq(clients.clientId, client.clientId));
|
.where(eq(clients.clientId, client.clientId));
|
||||||
|
|
||||||
|
|||||||
@@ -311,13 +311,13 @@ export async function createSite(
|
|||||||
// lets also make sure there is no overlap with other sites on the exit node
|
// lets also make sure there is no overlap with other sites on the exit node
|
||||||
const sitesQuery = await db
|
const sitesQuery = await db
|
||||||
.select({
|
.select({
|
||||||
subnet: sites.subnet
|
subnet: sites.exitNodeSubnet
|
||||||
})
|
})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(sites.exitNodeId, exitNodeId),
|
eq(sites.exitNodeId, exitNodeId),
|
||||||
eq(sites.subnet, subnet)
|
eq(sites.exitNodeSubnet, subnet)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -427,7 +427,7 @@ export async function createSite(
|
|||||||
exitNodeId,
|
exitNodeId,
|
||||||
name,
|
name,
|
||||||
niceId: updatedNiceId!,
|
niceId: updatedNiceId!,
|
||||||
subnet,
|
exitNodeSubnet: subnet,
|
||||||
type,
|
type,
|
||||||
pubKey: pubKey || null,
|
pubKey: pubKey || null,
|
||||||
status: "approved"
|
status: "approved"
|
||||||
@@ -444,7 +444,7 @@ export async function createSite(
|
|||||||
type,
|
type,
|
||||||
dockerSocketEnabled: false,
|
dockerSocketEnabled: false,
|
||||||
online: true,
|
online: true,
|
||||||
subnet: "0.0.0.0/32",
|
exitNodeSubnet: "0.0.0.0/32",
|
||||||
status: "approved"
|
status: "approved"
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ function querySitesBase() {
|
|||||||
niceId: sites.niceId,
|
niceId: sites.niceId,
|
||||||
name: sites.name,
|
name: sites.name,
|
||||||
pubKey: sites.pubKey,
|
pubKey: sites.pubKey,
|
||||||
subnet: sites.subnet,
|
subnet: sites.exitNodeSubnet,
|
||||||
megabytesIn: sites.megabytesIn,
|
megabytesIn: sites.megabytesIn,
|
||||||
megabytesOut: sites.megabytesOut,
|
megabytesOut: sites.megabytesOut,
|
||||||
orgName: orgs.name,
|
orgName: orgs.name,
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ const PickSiteDefaultsResponseDataSchema = z.object({
|
|||||||
clientAddress: z.string().optional()
|
clientAddress: z.string().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "get",
|
method: "get",
|
||||||
path: "/org/{orgId}/pick-site-defaults",
|
path: "/org/{orgId}/pick-site-defaults",
|
||||||
@@ -60,7 +59,9 @@ registry.registerPath({
|
|||||||
description: "Successful response",
|
description: "Successful response",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: createApiResponseSchema(PickSiteDefaultsResponseDataSchema)
|
schema: createApiResponseSchema(
|
||||||
|
PickSiteDefaultsResponseDataSchema
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +109,7 @@ export async function pickSiteDefaults(
|
|||||||
// list all of the sites on that exit node
|
// list all of the sites on that exit node
|
||||||
const sitesQuery = await db
|
const sitesQuery = await db
|
||||||
.select({
|
.select({
|
||||||
subnet: sites.subnet
|
subnet: sites.exitNodeSubnet
|
||||||
})
|
})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(eq(sites.exitNodeId, randomExitNode.exitNodeId));
|
.where(eq(sites.exitNodeId, randomExitNode.exitNodeId));
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ export async function createTarget(
|
|||||||
// make sure the target is within the site subnet
|
// make sure the target is within the site subnet
|
||||||
if (
|
if (
|
||||||
site.type == "wireguard" &&
|
site.type == "wireguard" &&
|
||||||
!isIpInCidr(targetData.ip, site.subnet!)
|
!isIpInCidr(targetData.ip, site.exitNodeSubnet!)
|
||||||
) {
|
) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
|
|||||||
@@ -72,8 +72,7 @@ export default function CredentialsPage() {
|
|||||||
const { data: latestVersions } = useQuery(
|
const { data: latestVersions } = useQuery(
|
||||||
productUpdatesQueries.latestVersion(true)
|
productUpdatesQueries.latestVersion(true)
|
||||||
);
|
);
|
||||||
const newtVersion =
|
const newtVersion = latestVersions?.data?.newt?.latestVersion ?? "latest";
|
||||||
latestVersions?.data?.newt?.latestVersion ?? "latest";
|
|
||||||
|
|
||||||
// Fetch site defaults for wireguard sites to show in obfuscated config
|
// Fetch site defaults for wireguard sites to show in obfuscated config
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -354,7 +353,7 @@ export default function CredentialsPage() {
|
|||||||
text={generateObfuscatedWireGuardConfig(
|
text={generateObfuscatedWireGuardConfig(
|
||||||
{
|
{
|
||||||
subnet:
|
subnet:
|
||||||
site?.subnet ||
|
site?.exitNodeSubnet ||
|
||||||
siteDefaults?.subnet ||
|
siteDefaults?.subnet ||
|
||||||
null,
|
null,
|
||||||
address:
|
address:
|
||||||
|
|||||||
Reference in New Issue
Block a user