diff --git a/server/lib/blueprints/privateResources.ts b/server/lib/blueprints/privateResources.ts index 3e6a784e0..8d00701f1 100644 --- a/server/lib/blueprints/privateResources.ts +++ b/server/lib/blueprints/privateResources.ts @@ -415,7 +415,11 @@ export async function updatePrivateResources( } else { let aliasAddress: string | null = null; let releaseAliasLock: (() => Promise) | null = null; - if (resourceData.mode === "host" || resourceData.mode === "http") { + if ( + resourceData.mode === "host" || + resourceData.mode === "http" || + resourceData.mode === "ssh" + ) { const { value, release } = await getNextAvailableAliasAddress( orgId, trx diff --git a/server/lib/ip.ts b/server/lib/ip.ts index d7e85e7ac..6da8bf887 100644 --- a/server/lib/ip.ts +++ b/server/lib/ip.ts @@ -504,7 +504,7 @@ export function generateRemoteSubnets( const parseResult = cidrSchema.safeParse(sr.destination); return parseResult.success; } - if (sr.mode === "host") { + if (sr.mode === "host" || sr.mode === "ssh") { // check if its a valid IP using zod const ipSchema = z.union([z.ipv4(), z.ipv6()]); const parseResult = ipSchema.safeParse(sr.destination); @@ -514,7 +514,7 @@ export function generateRemoteSubnets( }) .map((sr) => { if (sr.mode === "cidr") return sr.destination; - if (sr.mode === "host") { + if (sr.mode === "host" || sr.mode === "ssh") { return `${sr.destination}/32`; } return ""; // This should never be reached due to filtering, but satisfies TypeScript @@ -531,7 +531,7 @@ export function generateAliasConfig(allSiteResources: SiteResource[]): Alias[] { .filter( (sr) => sr.aliasAddress && - ((sr.alias && sr.mode == "host") || + ((sr.alias && (sr.mode == "host" || sr.mode == "ssh")) || (sr.fullDomain && sr.mode == "http")) ) .map((sr) => ({ @@ -577,6 +577,10 @@ export function generateSubnetProxyTargets( continue; } + if (!siteResource.destination) { + continue; + } + const clientPrefix = `${clientSite.subnet.split("/")[0]}/32`; const portRange = [ ...parsePortRangeString(siteResource.tcpPortRangeString, "tcp"), @@ -584,7 +588,7 @@ export function generateSubnetProxyTargets( ]; const disableIcmp = siteResource.disableIcmp ?? false; - if (siteResource.mode == "host") { + if (siteResource.mode == "host" || siteResource.mode == "ssh") { let destination = siteResource.destination; // check if this is a valid ip const ipSchema = z.union([z.ipv4(), z.ipv6()]); @@ -665,6 +669,11 @@ export async function generateSubnetProxyTargetV2( return; } + if (!siteResource.destination) { + // ssh can have no destination + return; + } + const targets: SubnetProxyTargetV2[] = []; const portRange = [ @@ -673,7 +682,7 @@ export async function generateSubnetProxyTargetV2( ]; const disableIcmp = siteResource.disableIcmp ?? false; - if (siteResource.mode == "host") { + if (siteResource.mode == "host" || siteResource.mode == "ssh") { let destination = siteResource.destination; // check if this is a valid ip const ipSchema = z.union([z.ipv4(), z.ipv6()]); diff --git a/server/lib/telemetry.ts b/server/lib/telemetry.ts index 77f7740c5..4f1adbd53 100644 --- a/server/lib/telemetry.ts +++ b/server/lib/telemetry.ts @@ -181,6 +181,7 @@ class TelemetryClient { let numPrivResourceHosts = 0; let numPrivResourceCidr = 0; let numPrivResourceHttp = 0; + let numPrivResourceSsh = 0; for (const res of allPrivateResources) { if (res.mode === "host") { numPrivResourceHosts += 1; @@ -188,6 +189,8 @@ class TelemetryClient { numPrivResourceCidr += 1; } else if (res.mode === "http") { numPrivResourceHttp += 1; + } else if (res.mode === "ssh") { + numPrivResourceSsh += 1; } if (res.alias) { @@ -207,6 +210,7 @@ class TelemetryClient { numPrivateResourceHosts: numPrivResourceHosts, numPrivateResourceCidr: numPrivResourceCidr, numPrivateResourceHttp: numPrivResourceHttp, + numPrivateResourceSsh: numPrivResourceSsh, numAlertRules: numAlertRules.count, numUserDevices: userDevicesCount.count, numMachineClients: machineClients.count, diff --git a/server/routers/idp/validateOidcCallback.ts b/server/routers/idp/validateOidcCallback.ts index 8188f46da..6a82f2c12 100644 --- a/server/routers/idp/validateOidcCallback.ts +++ b/server/routers/idp/validateOidcCallback.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db, Org } from "@server/db"; +import { db, Org, primaryDb } from "@server/db"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; @@ -635,9 +635,7 @@ export async function validateOidcCallback( } }); - db.transaction(async (trx) => { - await calculateUserClientsForOrgs(userId!, trx); - }).catch((err) => { + calculateUserClientsForOrgs(userId!, primaryDb).catch((err) => { logger.error( "Error calculating user clients after syncing orgs and roles for OIDC user", { error: err } diff --git a/server/routers/resource/listUserResourceAliases.ts b/server/routers/resource/listUserResourceAliases.ts index 1199a6cf2..d6e02b522 100644 --- a/server/routers/resource/listUserResourceAliases.ts +++ b/server/routers/resource/listUserResourceAliases.ts @@ -7,7 +7,7 @@ import { userOrgRoles, userOrgs } from "@server/db"; -import { and, eq, inArray, asc, isNotNull, ne } from "drizzle-orm"; +import { and, eq, inArray, asc, isNotNull, ne, or } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; @@ -224,7 +224,7 @@ export async function listUserResourceAliases( const whereClause = and( eq(siteResources.orgId, orgId), eq(siteResources.enabled, true), - eq(siteResources.mode, "host"), + or(eq(siteResources.mode, "host"), eq(siteResources.mode, "ssh")), isNotNull(siteResources.alias), ne(siteResources.alias, ""), inArray(siteResources.siteResourceId, accessibleSiteResourceIds) diff --git a/server/routers/siteResource/createSiteResource.ts b/server/routers/siteResource/createSiteResource.ts index 0648c45cc..1eebbc01d 100644 --- a/server/routers/siteResource/createSiteResource.ts +++ b/server/routers/siteResource/createSiteResource.ts @@ -445,7 +445,7 @@ export async function createSiteResource( let aliasAddress: string | null = null; let releaseAliasLock: (() => Promise) | null = null; - if (mode === "host" || mode === "http") { + if (mode === "host" || mode === "http" || mode === "ssh") { const { value, release } = await getNextAvailableAliasAddress(orgId); aliasAddress = value;