fosrl.pangolin/server/routers/olm/handleOlmRegisterMessage.ts

231 lines
6.9 KiB
TypeScript
Raw Normal View History

2025-07-24 14:48:24 -07:00
import { db, ExitNode } from "@server/db";
2025-02-21 10:13:41 -05:00
import { MessageHandler } from "../ws";
2025-08-04 20:22:13 -07:00
import { clients, clientSites, exitNodes, Olm, olms, sites } from "@server/db";
2025-08-04 20:17:35 -07:00
import { and, eq, inArray } from "drizzle-orm";
2025-02-21 10:55:38 -05:00
import { addPeer, deletePeer } from "../newt/peers";
2025-02-21 10:13:41 -05:00
import logger from "@server/logger";
2025-08-15 15:59:38 -07:00
import { listExitNodes } from "@server/lib/exitNodes";
2025-02-21 10:13:41 -05:00
export const handleOlmRegisterMessage: MessageHandler = async (context) => {
2025-04-01 22:49:08 -04:00
logger.info("Handling register olm message!");
2025-02-21 12:17:56 -05:00
const { message, client: c, sendToClient } = context;
const olm = c as Olm;
2025-04-01 22:49:08 -04:00
const now = new Date().getTime() / 1000;
2025-02-21 10:13:41 -05:00
if (!olm) {
logger.warn("Olm not found");
return;
}
2025-02-21 12:17:56 -05:00
if (!olm.clientId) {
logger.warn("Olm has no client ID!");
2025-02-21 10:13:41 -05:00
return;
}
2025-02-21 12:17:56 -05:00
const clientId = olm.clientId;
2025-07-24 14:48:24 -07:00
const { publicKey, relay } = message.data;
2025-08-04 20:22:13 -07:00
logger.debug(
`Olm client ID: ${clientId}, Public Key: ${publicKey}, Relay: ${relay}`
);
2025-07-24 14:48:24 -07:00
2025-02-21 10:13:41 -05:00
if (!publicKey) {
logger.warn("Public key not provided");
return;
}
// Get the client
2025-02-21 12:17:56 -05:00
const [client] = await db
.select()
.from(clients)
.where(eq(clients.clientId, clientId))
.limit(1);
if (!client) {
logger.warn("Client not found");
2025-02-21 12:17:56 -05:00
return;
}
if (client.exitNodeId) {
2025-08-04 20:22:13 -07:00
// TODO: FOR NOW WE ARE JUST HOLEPUNCHING ALL EXIT NODES BUT IN THE FUTURE WE SHOULD HANDLE THIS BETTER
2025-04-01 22:49:08 -04:00
2025-08-04 20:22:13 -07:00
// Get the exit node
2025-08-15 15:59:38 -07:00
const allExitNodes = await listExitNodes(client.orgId, true); // FILTER THE ONLINE ONES
2025-08-04 20:22:13 -07:00
const exitNodesHpData = allExitNodes.map((exitNode: ExitNode) => {
return {
2025-08-04 20:34:27 -07:00
publicKey: exitNode.publicKey,
2025-08-04 20:22:13 -07:00
endpoint: exitNode.endpoint
};
});
// Send holepunch message
await sendToClient(olm.olmId, {
2025-08-04 20:34:27 -07:00
type: "olm/wg/holepunch/all",
data: {
2025-08-04 20:34:27 -07:00
exitNodes: exitNodesHpData
}
});
// THIS IS FOR BACKWARDS COMPATIBILITY
await sendToClient(olm.olmId, {
type: "olm/wg/holepunch/all",
data: {
serverPubKey: allExitNodes[0].publicKey,
endpoint: allExitNodes[0].endpoint
}
});
2025-02-21 10:13:41 -05:00
}
if (now - (client.lastHolePunch || 0) > 6) {
2025-04-01 22:49:08 -04:00
logger.warn("Client last hole punch is too old, skipping all sites");
return;
2025-04-01 22:49:08 -04:00
}
2025-04-13 21:28:11 -04:00
if (client.pubKey !== publicKey) {
logger.info(
"Public key mismatch. Updating public key and clearing session info..."
);
// Update the client's public key
await db
.update(clients)
.set({
pubKey: publicKey
})
.where(eq(clients.clientId, olm.clientId));
// set isRelay to false for all of the client's sites to reset the connection metadata
await db
.update(clientSites)
.set({
2025-07-24 14:48:24 -07:00
isRelayed: relay == true
2025-04-13 21:28:11 -04:00
})
.where(eq(clientSites.clientId, olm.clientId));
}
// Get all sites data
const sitesData = await db
.select()
.from(sites)
.innerJoin(clientSites, eq(sites.siteId, clientSites.siteId))
.where(eq(clientSites.clientId, client.clientId));
// Prepare an array to store site configurations
2025-07-24 14:48:24 -07:00
let siteConfigurations = [];
2025-08-04 20:22:13 -07:00
logger.debug(
`Found ${sitesData.length} sites for client ${client.clientId}`
);
2025-07-28 22:40:27 -07:00
if (sitesData.length === 0) {
sendToClient(olm.olmId, {
type: "olm/register/no-sites",
data: {}
});
}
// Process each site
for (const { sites: site } of sitesData) {
if (!site.exitNodeId) {
logger.warn(
`Site ${site.siteId} does not have exit node, skipping`
);
continue;
}
// Validate endpoint and hole punch status
if (!site.endpoint) {
logger.warn(`Site ${site.siteId} has no endpoint, skipping`);
continue;
}
2025-07-27 10:21:27 -07:00
// if (site.lastHolePunch && now - site.lastHolePunch > 6 && relay) {
// logger.warn(
// `Site ${site.siteId} last hole punch is too old, skipping`
// );
// continue;
// }
// If public key changed, delete old peer from this site
2025-04-04 10:59:22 -04:00
if (client.pubKey && client.pubKey != publicKey) {
logger.info(
`Public key mismatch. Deleting old peer from site ${site.siteId}...`
);
await deletePeer(site.siteId, client.pubKey!);
}
if (!site.subnet) {
logger.warn(`Site ${site.siteId} has no subnet, skipping`);
continue;
}
2025-08-04 20:17:35 -07:00
const [clientSite] = await db
.select()
.from(clientSites)
2025-08-04 20:22:13 -07:00
.where(
and(
eq(clientSites.clientId, client.clientId),
eq(clientSites.siteId, site.siteId)
)
)
2025-08-04 20:17:35 -07:00
.limit(1);
// Add the peer to the exit node for this site
2025-08-04 20:17:35 -07:00
if (clientSite.endpoint) {
logger.info(
2025-08-04 20:17:35 -07:00
`Adding peer ${publicKey} to site ${site.siteId} with endpoint ${clientSite.endpoint}`
);
await addPeer(site.siteId, {
publicKey: publicKey,
2025-08-04 20:22:13 -07:00
allowedIps: [`${client.subnet.split("/")[0]}/32`], // we want to only allow from that client
2025-08-04 20:17:35 -07:00
endpoint: relay ? "" : clientSite.endpoint
});
} else {
logger.warn(
`Client ${client.clientId} has no endpoint, skipping peer addition`
2025-04-01 22:49:08 -04:00
);
}
2025-07-24 14:48:24 -07:00
let endpoint = site.endpoint;
if (relay) {
const [exitNode] = await db
.select()
.from(exitNodes)
.where(eq(exitNodes.exitNodeId, site.exitNodeId))
.limit(1);
if (!exitNode) {
logger.warn(`Exit node not found for site ${site.siteId}`);
continue;
}
endpoint = `${exitNode.endpoint}:21820`;
}
// Add site configuration to the array
siteConfigurations.push({
siteId: site.siteId,
2025-07-24 14:48:24 -07:00
endpoint: endpoint,
publicKey: site.publicKey,
2025-04-11 20:52:45 -04:00
serverIP: site.address,
2025-07-27 10:21:27 -07:00
serverPort: site.listenPort,
remoteSubnets: site.remoteSubnets
});
2025-02-21 10:13:41 -05:00
}
2025-08-04 20:22:13 -07:00
// REMOVED THIS SO IT CREATES THE INTERFACE AND JUST WAITS FOR THE SITES
2025-07-28 22:40:27 -07:00
// if (siteConfigurations.length === 0) {
// logger.warn("No valid site configurations found");
// return;
// }
// Return connect message with all site configurations
2025-02-21 10:13:41 -05:00
return {
message: {
type: "olm/wg/connect",
data: {
sites: siteConfigurations,
tunnelIP: client.subnet
2025-02-21 10:13:41 -05:00
}
},
broadcast: false,
excludeSender: false
2025-02-21 10:13:41 -05:00
};
};