mirror of
https://github.com/fosrl/pangolin.git
synced 2025-07-15 08:25:01 +02:00
add traefik settings to config and use fullDomain
This commit is contained in:
parent
1c4608fbf4
commit
6d9731f071
5 changed files with 45 additions and 52 deletions
|
@ -10,6 +10,11 @@ server:
|
||||||
internal_hostname: localhost
|
internal_hostname: localhost
|
||||||
secure_cookies: "false"
|
secure_cookies: "false"
|
||||||
|
|
||||||
|
traefik:
|
||||||
|
cert_resolver: "letsencrypt"
|
||||||
|
http_entrypoint: "http"
|
||||||
|
https_entrypoint: "https"
|
||||||
|
|
||||||
rate_limit:
|
rate_limit:
|
||||||
window_minutes: "1"
|
window_minutes: "1"
|
||||||
max_requests: "100"
|
max_requests: "100"
|
||||||
|
|
|
@ -10,7 +10,6 @@ const environmentSchema = z.object({
|
||||||
app: z.object({
|
app: z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
base_url: z.string().url(),
|
base_url: z.string().url(),
|
||||||
base_domain: z.string(),
|
|
||||||
log_level: z.enum(["debug", "info", "warn", "error"]),
|
log_level: z.enum(["debug", "info", "warn", "error"]),
|
||||||
save_logs: z.string().transform((val) => val === "true"),
|
save_logs: z.string().transform((val) => val === "true"),
|
||||||
}),
|
}),
|
||||||
|
@ -26,6 +25,11 @@ const environmentSchema = z.object({
|
||||||
internal_hostname: z.string(),
|
internal_hostname: z.string(),
|
||||||
secure_cookies: z.string().transform((val) => val === "true"),
|
secure_cookies: z.string().transform((val) => val === "true"),
|
||||||
}),
|
}),
|
||||||
|
traefik: z.object({
|
||||||
|
http_entrypoint: z.string(),
|
||||||
|
https_entrypoint: z.string().optional(),
|
||||||
|
cert_resolver: z.string().optional(),
|
||||||
|
}),
|
||||||
rate_limit: z.object({
|
rate_limit: z.object({
|
||||||
window_minutes: z
|
window_minutes: z
|
||||||
.string()
|
.string()
|
||||||
|
|
|
@ -24,7 +24,8 @@ export const sites = sqliteTable("sites", {
|
||||||
});
|
});
|
||||||
|
|
||||||
export const resources = sqliteTable("resources", {
|
export const resources = sqliteTable("resources", {
|
||||||
resourceId: text("resourceId", { length: 2048 }).primaryKey(),
|
resourceId: integer("resourceId").primaryKey({ autoIncrement: true }),
|
||||||
|
fullDomain: text("fullDomain", { length: 2048 }),
|
||||||
siteId: integer("siteId").references(() => sites.siteId, {
|
siteId: integer("siteId").references(() => sites.siteId, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
}),
|
}),
|
||||||
|
@ -45,6 +46,7 @@ export const targets = sqliteTable("targets", {
|
||||||
port: integer("port").notNull(),
|
port: integer("port").notNull(),
|
||||||
protocol: text("protocol"),
|
protocol: text("protocol"),
|
||||||
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
||||||
|
ssl: integer("ssl", { mode: "boolean" }).notNull().default(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const exitNodes = sqliteTable("exitNodes", {
|
export const exitNodes = sqliteTable("exitNodes", {
|
||||||
|
|
|
@ -39,8 +39,8 @@ app.prepare().then(() => {
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
externalServer.use(
|
externalServer.use(
|
||||||
rateLimitMiddleware({
|
rateLimitMiddleware({
|
||||||
windowMin: 1,
|
windowMin: config.rate_limit.window_minutes,
|
||||||
max: 100,
|
max: config.rate_limit.max_requests,
|
||||||
type: "IP_ONLY",
|
type: "IP_ONLY",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -88,7 +88,7 @@ app.prepare().then(() => {
|
||||||
internalServer.use(errorHandlerMiddleware);
|
internalServer.use(errorHandlerMiddleware);
|
||||||
});
|
});
|
||||||
|
|
||||||
declare global {
|
declare global { // TODO: eventually make seperate types that extend express.Request
|
||||||
namespace Express {
|
namespace Express {
|
||||||
interface Request {
|
interface Request {
|
||||||
user?: User;
|
user?: User;
|
||||||
|
@ -97,4 +97,4 @@ declare global {
|
||||||
userOrgIds?: string[];
|
userOrgIds?: string[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,10 +2,9 @@ import { Request, Response } from "express";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import * as schema from "@server/db/schema";
|
import * as schema from "@server/db/schema";
|
||||||
import { DynamicTraefikConfig } from "./configSchema";
|
import { DynamicTraefikConfig } from "./configSchema";
|
||||||
import { and, like, eq } from "drizzle-orm";
|
import { and, like, eq, isNotNull } from "drizzle-orm";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import env from "@server/config";
|
|
||||||
import config from "@server/config";
|
import config from "@server/config";
|
||||||
|
|
||||||
export async function traefikConfigProvider(_: Request, res: Response) {
|
export async function traefikConfigProvider(_: Request, res: Response) {
|
||||||
|
@ -22,46 +21,36 @@ export async function traefikConfigProvider(_: Request, res: Response) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildTraefikConfig(
|
export function buildTraefikConfig(
|
||||||
targets: schema.Target[],
|
targets: schema.Target[]
|
||||||
): DynamicTraefikConfig {
|
): DynamicTraefikConfig {
|
||||||
|
if (!targets.length) {
|
||||||
|
return { http: {} } as DynamicTraefikConfig;
|
||||||
|
}
|
||||||
|
|
||||||
const middlewareName = "badger";
|
const middlewareName = "badger";
|
||||||
|
|
||||||
|
const baseDomain = new URL(config.app.base_url).hostname;
|
||||||
|
|
||||||
|
const tls = {
|
||||||
|
certResolver: config.traefik.cert_resolver,
|
||||||
|
// domains: [ // TODO: figure out if this is neccessary
|
||||||
|
// {
|
||||||
|
// main: baseDomain,
|
||||||
|
// sans: ["*." + baseDomain],
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
};
|
||||||
|
|
||||||
const http: any = {
|
const http: any = {
|
||||||
routers: {
|
routers: {},
|
||||||
main: {
|
services: {},
|
||||||
entryPoints: ["https"],
|
|
||||||
middlewares: [],
|
|
||||||
service: "service-main",
|
|
||||||
rule: "Host(`fossorial.io`)",
|
|
||||||
tls: {
|
|
||||||
certResolver: "letsencrypt",
|
|
||||||
domains: [
|
|
||||||
{
|
|
||||||
main: "fossorial.io",
|
|
||||||
sans: ["*.fossorial.io"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
services: {
|
|
||||||
"service-main": {
|
|
||||||
loadBalancer: {
|
|
||||||
servers: [
|
|
||||||
{
|
|
||||||
url: `http://${config.server.internal_hostname}:${config.server.external_port}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
middlewares: {
|
middlewares: {
|
||||||
[middlewareName]: {
|
[middlewareName]: {
|
||||||
plugin: {
|
plugin: {
|
||||||
[middlewareName]: {
|
[middlewareName]: {
|
||||||
apiBaseUrl: new URL(
|
apiBaseUrl: new URL(
|
||||||
"/api/v1",
|
"/api/v1",
|
||||||
`http://${config.server.internal_hostname}:${config.server.internal_port}`,
|
`http://${config.server.internal_hostname}:${config.server.internal_port}`
|
||||||
).href,
|
).href,
|
||||||
appBaseUrl: config.app.base_url,
|
appBaseUrl: config.app.base_url,
|
||||||
},
|
},
|
||||||
|
@ -74,19 +63,11 @@ export function buildTraefikConfig(
|
||||||
const serviceName = `service-${target.targetId}`;
|
const serviceName = `service-${target.targetId}`;
|
||||||
|
|
||||||
http.routers![routerName] = {
|
http.routers![routerName] = {
|
||||||
entryPoints: ["https"],
|
entryPoints: [target.ssl ? config.traefik.https_entrypoint : config.traefik.https_entrypoint],
|
||||||
middlewares: [middlewareName],
|
middlewares: [middlewareName],
|
||||||
service: serviceName,
|
service: serviceName,
|
||||||
rule: `Host(\`${target.resourceId}\`)`, // assuming resourceId is a valid full hostname
|
rule: `Host(\`${target.resourceId}\`)`, // assuming resourceId is a valid full hostname
|
||||||
tls: {
|
...(target.ssl ? { tls } : {}),
|
||||||
certResolver: "letsencrypt",
|
|
||||||
domains: [
|
|
||||||
{
|
|
||||||
main: "fossorial.io",
|
|
||||||
sans: ["*.fossorial.io"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
http.services![serviceName] = {
|
http.services![serviceName] = {
|
||||||
|
@ -105,11 +86,12 @@ export async function getAllTargets(): Promise<schema.Target[]> {
|
||||||
const all = await db
|
const all = await db
|
||||||
.select()
|
.select()
|
||||||
.from(schema.targets)
|
.from(schema.targets)
|
||||||
|
.innerJoin(schema.resources, eq(schema.targets.resourceId, schema.resources.resourceId))
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(schema.targets.enabled, true),
|
eq(schema.targets.enabled, true),
|
||||||
like(schema.targets.resourceId, "%.%"),
|
isNotNull(schema.resources.fullDomain)
|
||||||
),
|
)
|
||||||
); // any resourceId with a dot is a valid hostname; otherwise it's a UUID placeholder
|
);
|
||||||
return all;
|
return all.map((row) => row.targets);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue