Prevent overlapping resources with org subnets

This commit is contained in:
Owen
2025-12-18 17:08:38 -05:00
parent 3e01bfef7d
commit 6e7ba1dc52
2 changed files with 72 additions and 5 deletions

View File

@@ -2,6 +2,7 @@ import {
clientSiteResources, clientSiteResources,
db, db,
newts, newts,
orgs,
roles, roles,
roleSiteResources, roleSiteResources,
SiteResource, SiteResource,
@@ -10,7 +11,7 @@ import {
userSiteResources userSiteResources
} from "@server/db"; } from "@server/db";
import { getUniqueSiteResourceName } from "@server/db/names"; import { getUniqueSiteResourceName } from "@server/db/names";
import { getNextAvailableAliasAddress, portRangeStringSchema } from "@server/lib/ip"; import { getNextAvailableAliasAddress, isIpInCidr, portRangeStringSchema } from "@server/lib/ip";
import { rebuildClientAssociationsFromSiteResource } from "@server/lib/rebuildClientAssociations"; import { rebuildClientAssociationsFromSiteResource } from "@server/lib/rebuildClientAssociations";
import response from "@server/lib/response"; import response from "@server/lib/response";
import logger from "@server/logger"; import logger from "@server/logger";
@@ -84,8 +85,7 @@ const createSiteResourceSchema = z
if (data.mode === "cidr") { if (data.mode === "cidr") {
// Check if it's a valid CIDR (v4 or v6) // Check if it's a valid CIDR (v4 or v6)
const isValidCIDR = z const isValidCIDR = z
// .union([z.cidrv4(), z.cidrv6()]) .union([z.cidrv4(), z.cidrv6()])
.union([z.cidrv4()]) // for now lets just do ipv4 until we verify ipv6 works everywhere
.safeParse(data.destination).success; .safeParse(data.destination).success;
return isValidCIDR; return isValidCIDR;
} }
@@ -175,6 +175,39 @@ export async function createSiteResource(
return next(createHttpError(HttpCode.NOT_FOUND, "Site not found")); return next(createHttpError(HttpCode.NOT_FOUND, "Site not found"));
} }
const [org] = await db
.select()
.from(orgs)
.where(eq(orgs.orgId, orgId))
.limit(1);
if (!org) {
return next(createHttpError(HttpCode.NOT_FOUND, "Organization not found"));
}
if (!org.subnet || !org.utilitySubnet) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
`Organization with ID ${orgId} has no subnet or utilitySubnet defined defined`
)
);
}
// Only check if destination is an IP address
const isIp = z.union([z.ipv4(), z.ipv6()]).safeParse(destination).success;
if (
isIp &&
(isIpInCidr(destination, org.subnet) || isIpInCidr(destination, org.utilitySubnet))
) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"IP can not be in the CIDR range of the organization's subnet or utility subnet"
)
);
}
// // check if resource with same protocol and proxy port already exists (only for port mode) // // check if resource with same protocol and proxy port already exists (only for port mode)
// if (mode === "port" && protocol && proxyPort) { // if (mode === "port" && protocol && proxyPort) {
// const [existingResource] = await db // const [existingResource] = await db

View File

@@ -5,6 +5,7 @@ import {
clientSiteResourcesAssociationsCache, clientSiteResourcesAssociationsCache,
db, db,
newts, newts,
orgs,
roles, roles,
roleSiteResources, roleSiteResources,
sites, sites,
@@ -24,6 +25,7 @@ import {
generateAliasConfig, generateAliasConfig,
generateRemoteSubnets, generateRemoteSubnets,
generateSubnetProxyTargets, generateSubnetProxyTargets,
isIpInCidr,
portRangeStringSchema portRangeStringSchema
} from "@server/lib/ip"; } from "@server/lib/ip";
import { import {
@@ -96,8 +98,7 @@ const updateSiteResourceSchema = z
if (data.mode === "cidr" && data.destination) { if (data.mode === "cidr" && data.destination) {
// Check if it's a valid CIDR (v4 or v6) // Check if it's a valid CIDR (v4 or v6)
const isValidCIDR = z const isValidCIDR = z
// .union([z.cidrv4(), z.cidrv6()]) .union([z.cidrv4(), z.cidrv6()])
.union([z.cidrv4()]) // for now lets just do ipv4 until we verify ipv6 works everywhere
.safeParse(data.destination).success; .safeParse(data.destination).success;
return isValidCIDR; return isValidCIDR;
} }
@@ -196,6 +197,39 @@ export async function updateSiteResource(
); );
} }
const [org] = await db
.select()
.from(orgs)
.where(eq(orgs.orgId, existingSiteResource.orgId))
.limit(1);
if (!org) {
return next(createHttpError(HttpCode.NOT_FOUND, "Organization not found"));
}
if (!org.subnet || !org.utilitySubnet) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
`Organization with ID ${existingSiteResource.orgId} has no subnet or utilitySubnet defined defined`
)
);
}
// Only check if destination is an IP address
const isIp = z.union([z.ipv4(), z.ipv6()]).safeParse(destination).success;
if (
isIp &&
(isIpInCidr(destination!, org.subnet) || isIpInCidr(destination!, org.utilitySubnet))
) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"IP can not be in the CIDR range of the organization's subnet or utility subnet"
)
);
}
let existingSite = site; let existingSite = site;
let siteChanged = false; let siteChanged = false;
if (existingSiteResource.siteId !== siteId) { if (existingSiteResource.siteId !== siteId) {