mirror of
https://github.com/fosrl/pangolin.git
synced 2026-01-28 22:00:51 +00:00
Standardize remote subnets build
This commit is contained in:
@@ -1454,9 +1454,7 @@
|
||||
"sitesFetchError": "An error occurred while fetching sites.",
|
||||
"olmErrorFetchReleases": "An error occurred while fetching Olm releases.",
|
||||
"olmErrorFetchLatest": "An error occurred while fetching the latest Olm release.",
|
||||
"remoteSubnets": "Remote Subnets",
|
||||
"enterCidrRange": "Enter CIDR range",
|
||||
"remoteSubnetsDescription": "Add CIDR ranges that can be accessed from this site remotely using clients. Use format like 10.0.0.0/24. This ONLY applies to VPN client connectivity.",
|
||||
"resourceEnableProxy": "Enable Public Proxy",
|
||||
"resourceEnableProxyDescription": "Enable public proxying to this resource. This allows access to the resource from outside the network through the cloud on an open port. Requires Traefik config.",
|
||||
"externalProxyEnabled": "External Proxy Enabled",
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { db } from "@server/db";
|
||||
import { db, SiteResource } from "@server/db";
|
||||
import { clients, orgs, sites } from "@server/db";
|
||||
import { and, eq, isNotNull } from "drizzle-orm";
|
||||
import config from "@server/lib/config";
|
||||
import z from "zod";
|
||||
|
||||
interface IPRange {
|
||||
start: bigint;
|
||||
@@ -300,3 +301,28 @@ export async function getNextAvailableOrgSubnet(): Promise<string> {
|
||||
|
||||
return subnet;
|
||||
}
|
||||
|
||||
export function generateRemoteSubnetsStr(allSiteResources: SiteResource[]) {
|
||||
let remoteSubnets = allSiteResources
|
||||
.filter((sr) => {
|
||||
if (sr.mode === "cidr") return true;
|
||||
if (sr.mode === "host") {
|
||||
// check if its a valid IP using zod
|
||||
const ipSchema = z.string().ip();
|
||||
const parseResult = ipSchema.safeParse(sr.destination);
|
||||
return parseResult.success;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.map((sr) => {
|
||||
if (sr.mode === "cidr") return sr.destination;
|
||||
if (sr.mode === "host") {
|
||||
return `${sr.destination}/32`;
|
||||
}
|
||||
});
|
||||
// remove duplicates
|
||||
remoteSubnets = Array.from(new Set(remoteSubnets));
|
||||
const remoteSubnetsStr =
|
||||
remoteSubnets.length > 0 ? remoteSubnets.join(",") : null;
|
||||
return remoteSubnetsStr;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import {
|
||||
} from "@server/routers/olm/peers";
|
||||
import { sendToExitNode } from "#dynamic/lib/exitNodes";
|
||||
import logger from "@server/logger";
|
||||
import z from "zod";
|
||||
import { generateRemoteSubnetsStr } from "@server/lib/ip";
|
||||
|
||||
export async function rebuildSiteClientAssociations(
|
||||
siteResource: SiteResource,
|
||||
@@ -331,14 +333,6 @@ async function handleMessagesForSiteClients(
|
||||
.from(siteResources)
|
||||
.where(eq(siteResources.siteId, site.siteId));
|
||||
|
||||
let remoteSubnets = allSiteResources
|
||||
.filter((sr) => sr.mode == "cidr")
|
||||
.map((sr) => sr.destination);
|
||||
// remove duplicates
|
||||
remoteSubnets = Array.from(new Set(remoteSubnets));
|
||||
const remoteSubnetsStr =
|
||||
remoteSubnets.length > 0 ? remoteSubnets.join(",") : null;
|
||||
|
||||
olmJobs.push(
|
||||
olmAddPeer(
|
||||
client.clientId,
|
||||
@@ -351,7 +345,7 @@ async function handleMessagesForSiteClients(
|
||||
publicKey: site.publicKey,
|
||||
serverIP: site.address,
|
||||
serverPort: site.listenPort,
|
||||
remoteSubnets: remoteSubnetsStr
|
||||
remoteSubnets: generateRemoteSubnetsStr(allSiteResources)
|
||||
},
|
||||
olm.olmId
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@ import { clients, clientSites, Newt, sites } from "@server/db";
|
||||
import { eq, and, inArray } from "drizzle-orm";
|
||||
import { updatePeer } from "../olm/peers";
|
||||
import { sendToExitNode } from "#dynamic/lib/exitNodes";
|
||||
import { generateRemoteSubnetsStr } from "@server/lib/ip";
|
||||
|
||||
const inputSchema = z.object({
|
||||
publicKey: z.string(),
|
||||
@@ -188,23 +189,13 @@ export const handleGetConfigMessage: MessageHandler = async (context) => {
|
||||
.from(siteResources)
|
||||
.where(eq(siteResources.siteId, site.siteId));
|
||||
|
||||
let remoteSubnets = allSiteResources
|
||||
.filter((sr) => sr.mode == "cidr")
|
||||
.map((sr) => sr.destination);
|
||||
// remove duplicates
|
||||
remoteSubnets = Array.from(new Set(remoteSubnets));
|
||||
const remoteSubnetsStr =
|
||||
remoteSubnets.length > 0
|
||||
? remoteSubnets.join(",")
|
||||
: null;
|
||||
|
||||
await updatePeer(client.clients.clientId, {
|
||||
siteId: site.siteId,
|
||||
endpoint: endpoint,
|
||||
publicKey: site.publicKey,
|
||||
serverIP: site.address,
|
||||
serverPort: site.listenPort,
|
||||
remoteSubnets: remoteSubnetsStr
|
||||
remoteSubnets: generateRemoteSubnetsStr(allSiteResources)
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
@@ -231,46 +222,35 @@ export const handleGetConfigMessage: MessageHandler = async (context) => {
|
||||
.from(siteResources)
|
||||
.where(eq(siteResources.siteId, siteId));
|
||||
|
||||
const { tcpTargets, udpTargets } = allSiteResources.reduce(
|
||||
(acc, resource) => {
|
||||
// Only process port mode resources
|
||||
if (resource.mode !== "port") {
|
||||
return acc;
|
||||
let targets: {
|
||||
cidr: string;
|
||||
portRange?: {
|
||||
min: number;
|
||||
max: number;
|
||||
}[];
|
||||
}[] = [];
|
||||
|
||||
for (const siteResource of allSiteResources) {
|
||||
if (siteResource.mode == "host") {
|
||||
// check if this is a valid ip
|
||||
const ipSchema = z.string().ip();
|
||||
if (ipSchema.safeParse(siteResource.destination).success) {
|
||||
targets.push({
|
||||
cidr: `${siteResource.destination}/32`
|
||||
});
|
||||
}
|
||||
|
||||
// Filter out invalid targets
|
||||
if (
|
||||
!resource.proxyPort ||
|
||||
!resource.destination ||
|
||||
!resource.destinationPort ||
|
||||
!resource.protocol
|
||||
) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
// Format target into string
|
||||
const formattedTarget = `${resource.proxyPort}:${resource.destination}:${resource.destinationPort}`;
|
||||
|
||||
// Add to the appropriate protocol array
|
||||
if (resource.protocol === "tcp") {
|
||||
acc.tcpTargets.push(formattedTarget);
|
||||
} else {
|
||||
acc.udpTargets.push(formattedTarget);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ tcpTargets: [] as string[], udpTargets: [] as string[] }
|
||||
);
|
||||
} else if (siteResource.mode == "cidr") {
|
||||
targets.push({
|
||||
cidr: siteResource.destination
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Build the configuration response
|
||||
const configResponse = {
|
||||
ipAddress: site.address,
|
||||
peers: validPeers,
|
||||
targets: {
|
||||
udp: udpTargets,
|
||||
tcp: tcpTargets
|
||||
}
|
||||
targets: targets
|
||||
};
|
||||
|
||||
logger.debug("Sending config: ", configResponse);
|
||||
|
||||
@@ -18,6 +18,7 @@ import { addPeer, deletePeer } from "../newt/peers";
|
||||
import logger from "@server/logger";
|
||||
import { listExitNodes } from "#dynamic/lib/exitNodes";
|
||||
import { getNextAvailableClientSubnet } from "@server/lib/ip";
|
||||
import { generateRemoteSubnetsStr } from "@server/lib/ip";
|
||||
|
||||
export const handleOlmRegisterMessage: MessageHandler = async (context) => {
|
||||
logger.info("Handling register olm message!");
|
||||
@@ -238,11 +239,6 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
|
||||
.from(siteResources)
|
||||
.where(eq(siteResources.siteId, site.siteId));
|
||||
|
||||
let remoteSubnets = allSiteResources.filter((sr => sr.mode == "cidr")).map(sr => sr.destination);
|
||||
// remove duplicates
|
||||
remoteSubnets = Array.from(new Set(remoteSubnets));
|
||||
const remoteSubnetsStr = remoteSubnets.length > 0 ? remoteSubnets.join(",") : null;
|
||||
|
||||
// Add the peer to the exit node for this site
|
||||
if (clientSite.endpoint) {
|
||||
logger.info(
|
||||
@@ -280,7 +276,7 @@ export const handleOlmRegisterMessage: MessageHandler = async (context) => {
|
||||
publicKey: site.publicKey,
|
||||
serverIP: site.address,
|
||||
serverPort: site.listenPort,
|
||||
remoteSubnets: remoteSubnetsStr
|
||||
remoteSubnets: generateRemoteSubnetsStr(allSiteResources)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,39 @@ const createSiteResourceSchema = z
|
||||
message:
|
||||
"Protocol, proxy port, and destination port are required for port mode"
|
||||
}
|
||||
);
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.mode === "host") {
|
||||
// Check if it's a valid IP address using zod
|
||||
const isValidIP = z.string().ip().safeParse(data.destination).success;
|
||||
|
||||
// Check if it's a valid domain (hostname pattern, TLD not required)
|
||||
const domainRegex = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
|
||||
const isValidDomain = domainRegex.test(data.destination);
|
||||
|
||||
return isValidIP || isValidDomain;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message:
|
||||
"Destination must be a valid IP address or domain name for host mode"
|
||||
}
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.mode === "cidr") {
|
||||
// Check if it's a valid CIDR
|
||||
const isValidCIDR = z.string().cidr().safeParse(data.destination).success;
|
||||
return isValidCIDR;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "Destination must be a valid CIDR notation for cidr mode"
|
||||
}
|
||||
);
|
||||
|
||||
export type CreateSiteResourceBody = z.infer<typeof createSiteResourceSchema>;
|
||||
export type CreateSiteResourceResponse = SiteResource;
|
||||
|
||||
Reference in New Issue
Block a user