mirror of
https://github.com/fosrl/pangolin.git
synced 2026-06-18 21:31:56 +00:00
Complete removal of http and protocol from public
This commit is contained in:
@@ -106,7 +106,7 @@ export async function applyBlueprint({
|
||||
site.newt.newtId,
|
||||
[target],
|
||||
matchingHealthcheck ? [matchingHealthcheck] : [],
|
||||
result.proxyResource.protocol,
|
||||
result.proxyResource.mode === "udp" ? "udp" : "tcp",
|
||||
site.newt.version
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import { isValidRegionId } from "@server/db/regions";
|
||||
import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed";
|
||||
import { fireHealthCheckUnknownAlert } from "@server/lib/alerts";
|
||||
import { tierMatrix } from "../billing/tierMatrix";
|
||||
import { http } from "winston";
|
||||
|
||||
export type ProxyResourcesResults = {
|
||||
proxyResource: Resource;
|
||||
@@ -198,9 +199,6 @@ export async function updateProxyResources(
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
const http = resourceData.protocol == "http";
|
||||
const protocol =
|
||||
resourceData.protocol == "http" ? "tcp" : resourceData.protocol;
|
||||
const resourceEnabled =
|
||||
resourceData.enabled == undefined || resourceData.enabled == null
|
||||
? true
|
||||
@@ -216,7 +214,9 @@ export async function updateProxyResources(
|
||||
|
||||
if (existingResource) {
|
||||
let domain;
|
||||
if (http) {
|
||||
if (
|
||||
["http", "ssh", "rdp", "vnc"].includes(resourceData.mode || "")
|
||||
) {
|
||||
domain = await getDomain(
|
||||
existingResource.resourceId,
|
||||
resourceData["full-domain"]!,
|
||||
@@ -246,10 +246,17 @@ export async function updateProxyResources(
|
||||
.update(resources)
|
||||
.set({
|
||||
name: resourceData.name || "Unnamed Resource",
|
||||
protocol: protocol || "tcp",
|
||||
http: http,
|
||||
proxyPort: http ? null : resourceData["proxy-port"],
|
||||
fullDomain: http ? resourceData["full-domain"] : null,
|
||||
mode: resourceData.mode,
|
||||
proxyPort: ["http", "ssh", "rdp", "vnc"].includes(
|
||||
resourceData.mode || ""
|
||||
)
|
||||
? null
|
||||
: resourceData["proxy-port"],
|
||||
fullDomain: ["http", "ssh", "rdp", "vnc"].includes(
|
||||
resourceData.mode || ""
|
||||
)
|
||||
? resourceData["full-domain"]
|
||||
: null,
|
||||
subdomain: domain ? domain.subdomain : null,
|
||||
domainId: domain ? domain.domainId : null,
|
||||
wildcard: domain ? domain.wildcard : false,
|
||||
@@ -466,7 +473,10 @@ export async function updateProxyResources(
|
||||
.set({
|
||||
siteId: site.siteId,
|
||||
ip: targetData.hostname,
|
||||
method: http ? targetData.method : null,
|
||||
method:
|
||||
resourceData.mode == "http" // the other types of ssh, rdp, and vnc use the browser gateway targets and not this one so this is okay
|
||||
? targetData.method
|
||||
: null,
|
||||
port: targetData.port,
|
||||
enabled: targetData.enabled,
|
||||
path: targetData.path,
|
||||
@@ -687,7 +697,9 @@ export async function updateProxyResources(
|
||||
} else {
|
||||
// create a brand new resource
|
||||
let domain;
|
||||
if (http) {
|
||||
if (
|
||||
["http", "ssh", "rdp", "vnc"].includes(resourceData.mode || "")
|
||||
) {
|
||||
domain = await getDomain(
|
||||
undefined,
|
||||
resourceData["full-domain"]!,
|
||||
@@ -711,10 +723,17 @@ export async function updateProxyResources(
|
||||
orgId,
|
||||
niceId: resourceNiceId,
|
||||
name: resourceData.name || "Unnamed Resource",
|
||||
protocol: protocol || "tcp",
|
||||
http: http,
|
||||
proxyPort: http ? null : resourceData["proxy-port"],
|
||||
fullDomain: http ? resourceData["full-domain"] : null,
|
||||
mode: resourceData.mode,
|
||||
proxyPort: ["http", "ssh", "rdp", "vnc"].includes(
|
||||
resourceData.mode || ""
|
||||
)
|
||||
? null
|
||||
: resourceData["proxy-port"],
|
||||
fullDomain: ["http", "ssh", "rdp", "vnc"].includes(
|
||||
resourceData.mode || ""
|
||||
)
|
||||
? resourceData["full-domain"]
|
||||
: null,
|
||||
subdomain: domain ? domain.subdomain : null,
|
||||
domainId: domain ? domain.domainId : null,
|
||||
wildcard: domain ? domain.wildcard : false,
|
||||
|
||||
@@ -162,10 +162,13 @@ export const HeaderSchema = z.object({
|
||||
});
|
||||
|
||||
// Schema for individual resource
|
||||
export const ResourceSchema = z
|
||||
export const PublicResourceSchema = z
|
||||
.object({
|
||||
name: z.string().optional(),
|
||||
protocol: z.enum(["http", "tcp", "udp"]).optional(),
|
||||
protocol: z
|
||||
.enum(["http", "tcp", "udp", "ssh", "rdp", "vnc"])
|
||||
.optional(), // this was the old one and is now DEPRECATED in favor of the mode
|
||||
mode: z.enum(["http", "tcp", "udp", "ssh", "rdp", "vnc"]).optional(),
|
||||
ssl: z.boolean().optional(),
|
||||
scheme: z.enum(["http", "https"]).optional(),
|
||||
"full-domain": z.string().optional(),
|
||||
@@ -185,9 +188,10 @@ export const ResourceSchema = z
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, require name and protocol for full resource definition
|
||||
// Otherwise, require name and protocol/mode for full resource definition
|
||||
return (
|
||||
resource.name !== undefined && resource.protocol !== undefined
|
||||
resource.name !== undefined &&
|
||||
(resource.mode !== undefined || resource.protocol !== undefined)
|
||||
);
|
||||
},
|
||||
{
|
||||
@@ -201,8 +205,8 @@ export const ResourceSchema = z
|
||||
return true;
|
||||
}
|
||||
|
||||
// If protocol is http, all targets must have method field
|
||||
if (resource.protocol === "http") {
|
||||
// If protocol/mode is http, all targets must have method field
|
||||
if ((resource.mode ?? resource.protocol) === "http") {
|
||||
return resource.targets.every(
|
||||
(target) => target == null || target.method !== undefined
|
||||
);
|
||||
@@ -220,8 +224,9 @@ export const ResourceSchema = z
|
||||
return true;
|
||||
}
|
||||
|
||||
// If protocol is tcp or udp, no target should have method field
|
||||
if (resource.protocol === "tcp" || resource.protocol === "udp") {
|
||||
// If protocol/mode is tcp or udp, no target should have method field
|
||||
const effectiveProtocol1 = resource.mode ?? resource.protocol;
|
||||
if (effectiveProtocol1 === "tcp" || effectiveProtocol1 === "udp") {
|
||||
return resource.targets.every(
|
||||
(target) => target == null || target.method === undefined
|
||||
);
|
||||
@@ -239,8 +244,8 @@ export const ResourceSchema = z
|
||||
return true;
|
||||
}
|
||||
|
||||
// If protocol is http, it must have a full-domain
|
||||
if (resource.protocol === "http") {
|
||||
// If protocol/mode is http, it must have a full-domain
|
||||
if ((resource.mode ?? resource.protocol) === "http") {
|
||||
return (
|
||||
resource["full-domain"] !== undefined &&
|
||||
resource["full-domain"].length > 0
|
||||
@@ -259,8 +264,9 @@ export const ResourceSchema = z
|
||||
return true;
|
||||
}
|
||||
|
||||
// If protocol is tcp or udp, it must have both proxy-port
|
||||
if (resource.protocol === "tcp" || resource.protocol === "udp") {
|
||||
// If protocol/mode is tcp or udp, it must have both proxy-port
|
||||
const effectiveProtocol2 = resource.mode ?? resource.protocol;
|
||||
if (effectiveProtocol2 === "tcp" || effectiveProtocol2 === "udp") {
|
||||
return resource["proxy-port"] !== undefined;
|
||||
}
|
||||
return true;
|
||||
@@ -277,8 +283,9 @@ export const ResourceSchema = z
|
||||
return true;
|
||||
}
|
||||
|
||||
// If protocol is tcp or udp, it must not have auth
|
||||
if (resource.protocol === "tcp" || resource.protocol === "udp") {
|
||||
// If protocol/mode is tcp or udp, it must not have auth
|
||||
const effectiveProtocol3 = resource.mode ?? resource.protocol;
|
||||
if (effectiveProtocol3 === "tcp" || effectiveProtocol3 === "udp") {
|
||||
return resource.auth === undefined;
|
||||
}
|
||||
return true;
|
||||
@@ -340,7 +347,8 @@ export const ResourceSchema = z
|
||||
if (parts.includes("*", 1)) return false; // no further wildcards
|
||||
if (parts.length < 3) return false; // need at least *.label.tld
|
||||
|
||||
const labelRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$|^[a-zA-Z0-9]$/;
|
||||
const labelRegex =
|
||||
/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$|^[a-zA-Z0-9]$/;
|
||||
return parts.slice(1).every((label) => labelRegex.test(label));
|
||||
},
|
||||
{
|
||||
@@ -348,16 +356,23 @@ export const ResourceSchema = z
|
||||
message:
|
||||
'Wildcard full-domain must have "*" as the leftmost label only, followed by at least two valid hostname labels (e.g. "*.example.com" or "*.level1.example.com"). Patterns like "*example.com" or "level2.*.example.com" are not supported.'
|
||||
}
|
||||
);
|
||||
)
|
||||
.transform((resource) => {
|
||||
// Normalize: prefer mode, fall back to protocol for backwards compatibility
|
||||
if (resource.mode === undefined && resource.protocol !== undefined) {
|
||||
resource.mode = resource.protocol;
|
||||
}
|
||||
return resource;
|
||||
});
|
||||
|
||||
export function isTargetsOnlyResource(resource: any): boolean {
|
||||
return Object.keys(resource).length === 1 && resource.targets;
|
||||
}
|
||||
|
||||
export const ClientResourceSchema = z
|
||||
export const PrivateResourceSchema = z
|
||||
.object({
|
||||
name: z.string().min(1).max(255),
|
||||
mode: z.enum(["host", "cidr", "http"]),
|
||||
mode: z.enum(["host", "cidr", "http", "ssh"]),
|
||||
site: z.string().optional(), // DEPRECATED IN FAVOR OF sites
|
||||
sites: z.array(z.string()).optional().default([]),
|
||||
// protocol: z.enum(["tcp", "udp"]).optional(),
|
||||
@@ -435,19 +450,19 @@ export const ClientResourceSchema = z
|
||||
export const ConfigSchema = z
|
||||
.object({
|
||||
"proxy-resources": z
|
||||
.record(z.string(), ResourceSchema)
|
||||
.record(z.string(), PublicResourceSchema)
|
||||
.optional()
|
||||
.prefault({}),
|
||||
"public-resources": z
|
||||
.record(z.string(), ResourceSchema)
|
||||
.record(z.string(), PublicResourceSchema)
|
||||
.optional()
|
||||
.prefault({}),
|
||||
"client-resources": z
|
||||
.record(z.string(), ClientResourceSchema)
|
||||
.record(z.string(), PrivateResourceSchema)
|
||||
.optional()
|
||||
.prefault({}),
|
||||
"private-resources": z
|
||||
.record(z.string(), ClientResourceSchema)
|
||||
.record(z.string(), PrivateResourceSchema)
|
||||
.optional()
|
||||
.prefault({}),
|
||||
sites: z.record(z.string(), SiteSchema).optional().prefault({})
|
||||
@@ -472,10 +487,13 @@ export const ConfigSchema = z
|
||||
}
|
||||
|
||||
return data as {
|
||||
"proxy-resources": Record<string, z.infer<typeof ResourceSchema>>;
|
||||
"proxy-resources": Record<
|
||||
string,
|
||||
z.infer<typeof PublicResourceSchema>
|
||||
>;
|
||||
"client-resources": Record<
|
||||
string,
|
||||
z.infer<typeof ClientResourceSchema>
|
||||
z.infer<typeof PrivateResourceSchema>
|
||||
>;
|
||||
sites: Record<string, z.infer<typeof SiteSchema>>;
|
||||
};
|
||||
@@ -614,5 +632,5 @@ export const ConfigSchema = z
|
||||
// Type inference from the schema
|
||||
export type Site = z.infer<typeof SiteSchema>;
|
||||
export type Target = z.infer<typeof TargetSchema>;
|
||||
export type Resource = z.infer<typeof ResourceSchema>;
|
||||
export type Resource = z.infer<typeof PublicResourceSchema>;
|
||||
export type Config = z.infer<typeof ConfigSchema>;
|
||||
|
||||
@@ -2,7 +2,14 @@ import { PostHog } from "posthog-node";
|
||||
import config from "./config";
|
||||
import { getHostMeta } from "./hostMeta";
|
||||
import logger from "@server/logger";
|
||||
import { alertRules, apiKeys, blueprints, db, roles, siteResources } from "@server/db";
|
||||
import {
|
||||
alertRules,
|
||||
apiKeys,
|
||||
blueprints,
|
||||
db,
|
||||
roles,
|
||||
siteResources
|
||||
} from "@server/db";
|
||||
import { sites, users, orgs, resources, clients, idp } from "@server/db";
|
||||
import { eq, count, notInArray, and, isNotNull, isNull } from "drizzle-orm";
|
||||
import { APP_VERSION } from "./consts";
|
||||
@@ -143,8 +150,7 @@ class TelemetryClient {
|
||||
.select({
|
||||
name: resources.name,
|
||||
sso: resources.sso,
|
||||
protocol: resources.protocol,
|
||||
http: resources.http
|
||||
mode: resources.mode
|
||||
})
|
||||
.from(resources);
|
||||
|
||||
@@ -311,7 +317,7 @@ class TelemetryClient {
|
||||
(r) => r.sso
|
||||
).length,
|
||||
num_resources_non_http: stats.resources.filter(
|
||||
(r) => !r.http
|
||||
(r) => r.mode !== "http"
|
||||
).length,
|
||||
num_newt_sites: stats.sites.filter((s) => s.type === "newt")
|
||||
.length,
|
||||
|
||||
@@ -55,9 +55,7 @@ export async function getTraefikConfig(
|
||||
resourceName: resources.name,
|
||||
fullDomain: resources.fullDomain,
|
||||
ssl: resources.ssl,
|
||||
http: resources.http,
|
||||
proxyPort: resources.proxyPort,
|
||||
protocol: resources.protocol,
|
||||
subdomain: resources.subdomain,
|
||||
domainId: resources.domainId,
|
||||
enabled: resources.enabled,
|
||||
@@ -68,6 +66,7 @@ export async function getTraefikConfig(
|
||||
headers: resources.headers,
|
||||
proxyProtocol: resources.proxyProtocol,
|
||||
proxyProtocolVersion: resources.proxyProtocolVersion,
|
||||
mode: resources.mode,
|
||||
|
||||
// Target fields
|
||||
targetId: targets.targetId,
|
||||
@@ -115,8 +114,8 @@ export async function getTraefikConfig(
|
||||
),
|
||||
inArray(sites.type, siteTypes),
|
||||
allowRawResources
|
||||
? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true
|
||||
: eq(resources.http, true)
|
||||
? inArray(resources.mode, ["http", "udp", "tcp"]) // allow all three
|
||||
: eq(resources.mode, "http")
|
||||
)
|
||||
)
|
||||
.orderBy(desc(targets.priority), targets.targetId); // stable ordering
|
||||
@@ -166,9 +165,8 @@ export async function getTraefikConfig(
|
||||
key: key,
|
||||
fullDomain: row.fullDomain,
|
||||
ssl: row.ssl,
|
||||
http: row.http,
|
||||
mode: row.mode,
|
||||
proxyPort: row.proxyPort,
|
||||
protocol: row.protocol,
|
||||
subdomain: row.subdomain,
|
||||
domainId: row.domainId,
|
||||
enabled: row.enabled,
|
||||
@@ -580,7 +578,7 @@ export async function getTraefikConfig(
|
||||
continue;
|
||||
}
|
||||
|
||||
const protocol = resource.protocol.toLowerCase();
|
||||
const protocol = resource.mode === "udp" ? "udp" : "tcp"; // all of the other ones are tcp
|
||||
const port = resource.proxyPort;
|
||||
|
||||
if (!port) {
|
||||
|
||||
Reference in New Issue
Block a user