Get the headers into the traefik config

This commit is contained in:
Owen
2025-09-11 12:20:50 -07:00
parent 612446c3c9
commit 1eacb8ff36
7 changed files with 84 additions and 46 deletions

View File

@@ -31,6 +31,8 @@ def convert_and_send(file_path, url, headers):
# convert the parsed YAML to a JSON string # convert the parsed YAML to a JSON string
json_payload = json.dumps(parsed_yaml) json_payload = json.dumps(parsed_yaml)
print("Converted JSON payload:")
print(json_payload)
# Encode the JSON string to Base64 # Encode the JSON string to Base64
encoded_json = base64.b64encode(json_payload.encode('utf-8')).decode('utf-8') encoded_json = base64.b64encode(json_payload.encode('utf-8')).decode('utf-8')

View File

@@ -5,16 +5,19 @@ resources:
full-domain: level1.test3.example.com full-domain: level1.test3.example.com
host-header: example.com host-header: example.com
tls-server-name: example.com tls-server-name: example.com
auth: # auth:
pincode: 123456 # pincode: 123456
password: sadfasdfadsf # password: sadfasdfadsf
sso-enabled: true # sso-enabled: true
sso-roles: # sso-roles:
- Member # - Member
sso-users: # sso-users:
- owen@fossorial.io # - owen@fossorial.io
whitelist-users: # whitelist-users:
- owen@fossorial.io # - owen@fossorial.io
headers:
- X-Example-Header: example-value
- X-Another-Header: another-value
targets: targets:
- site: lively-yosemite-toad - site: lively-yosemite-toad
hostname: localhost hostname: localhost
@@ -24,15 +27,11 @@ resources:
hostname: localhost hostname: localhost
method: http method: http
port: 8001 port: 8001
- site: glossy-plains-viscacha-rat
hostname: localhost
method: http
port: 8001
resource-nice-id2: resource-nice-id2:
name: this is other resource name: this is other resource
protocol: tcp protocol: tcp
proxy-port: 3000 proxy-port: 3000
targets: targets:
- site: glossy-plains-viscacha-rat - site: lively-yosemite-toad
hostname: localhost hostname: localhost
port: 3000 port: 3000

View File

@@ -96,6 +96,7 @@ export const resources = pgTable("resources", {
skipToIdpId: integer("skipToIdpId").references(() => idp.idpId, { skipToIdpId: integer("skipToIdpId").references(() => idp.idpId, {
onDelete: "cascade" onDelete: "cascade"
}), }),
headers: text("headers"), // comma-separated list of headers to add to the request
}); });
export const targets = pgTable("targets", { export const targets = pgTable("targets", {

View File

@@ -107,7 +107,8 @@ export const resources = sqliteTable("resources", {
enableProxy: integer("enableProxy", { mode: "boolean" }).default(true), enableProxy: integer("enableProxy", { mode: "boolean" }).default(true),
skipToIdpId: integer("skipToIdpId").references(() => idp.idpId, { skipToIdpId: integer("skipToIdpId").references(() => idp.idpId, {
onDelete: "cascade" onDelete: "cascade"
}) }),
headers: text("headers"), // comma-separated list of headers to add to the request
}); });
export const targets = sqliteTable("targets", { export const targets = sqliteTable("targets", {

View File

@@ -120,6 +120,17 @@ export async function updateResources(
const http = resourceData.protocol == "http"; const http = resourceData.protocol == "http";
const protocol = const protocol =
resourceData.protocol == "http" ? "tcp" : resourceData.protocol; resourceData.protocol == "http" ? "tcp" : resourceData.protocol;
const resourceEnabled = resourceData.enabled == undefined || resourceData.enabled == null ? true : resourceData.enabled;
let headers = "";
for (const headerObj of resourceData.headers || []) {
for (const [key, value] of Object.entries(headerObj)) {
headers += `${key}: ${value},`;
}
}
// if there are headers, remove the trailing comma
if (headers.endsWith(",")) {
headers = headers.slice(0, -1);
}
if (existingResource) { if (existingResource) {
let domain; let domain;
@@ -152,7 +163,7 @@ export async function updateResources(
fullDomain: http ? resourceData["full-domain"] : null, fullDomain: http ? resourceData["full-domain"] : null,
subdomain: domain ? domain.subdomain : null, subdomain: domain ? domain.subdomain : null,
domainId: domain ? domain.domainId : null, domainId: domain ? domain.domainId : null,
enabled: resourceData.enabled ? true : false, enabled: resourceEnabled,
sso: resourceData.auth?.["sso-enabled"] || false, sso: resourceData.auth?.["sso-enabled"] || false,
ssl: resourceData.ssl ? true : false, ssl: resourceData.ssl ? true : false,
setHostHeader: resourceData["host-header"] || null, setHostHeader: resourceData["host-header"] || null,
@@ -161,7 +172,8 @@ export async function updateResources(
"whitelist-users" "whitelist-users"
] ]
? resourceData.auth["whitelist-users"].length > 0 ? resourceData.auth["whitelist-users"].length > 0
: false : false,
headers: headers || null,
}) })
.where( .where(
eq(resources.resourceId, existingResource.resourceId) eq(resources.resourceId, existingResource.resourceId)
@@ -379,11 +391,12 @@ export async function updateResources(
fullDomain: http ? resourceData["full-domain"] : null, fullDomain: http ? resourceData["full-domain"] : null,
subdomain: domain ? domain.subdomain : null, subdomain: domain ? domain.subdomain : null,
domainId: domain ? domain.domainId : null, domainId: domain ? domain.domainId : null,
enabled: resourceData.enabled ? true : false, enabled: resourceEnabled,
sso: resourceData.auth?.["sso-enabled"] || false, sso: resourceData.auth?.["sso-enabled"] || false,
setHostHeader: resourceData["host-header"] || null, setHostHeader: resourceData["host-header"] || null,
tlsServerName: resourceData["tls-server-name"] || null, tlsServerName: resourceData["tls-server-name"] || null,
ssl: resourceData.ssl ? true : false ssl: resourceData.ssl ? true : false,
headers: headers || null
}) })
.returning(); .returning();

View File

@@ -44,7 +44,8 @@ export const ResourceSchema = z
targets: z.array(TargetSchema.nullable()).optional().default([]), targets: z.array(TargetSchema.nullable()).optional().default([]),
auth: AuthSchema.optional(), auth: AuthSchema.optional(),
"host-header": z.string().optional(), "host-header": z.string().optional(),
"tls-server-name": z.string().optional() "tls-server-name": z.string().optional(),
headers: z.array(z.record(z.string(), z.string())).optional().default([]),
}) })
.refine( .refine(
(resource) => { (resource) => {

View File

@@ -54,7 +54,8 @@ export async function traefikConfigProvider(
config.getRawConfig().traefik.site_types config.getRawConfig().traefik.site_types
); );
if (traefikConfig?.http?.middlewares) { // BECAUSE SOMETIMES THE CONFIG CAN BE EMPTY IF THERE IS NOTHING if (traefikConfig?.http?.middlewares) {
// BECAUSE SOMETIMES THE CONFIG CAN BE EMPTY IF THERE IS NOTHING
traefikConfig.http.middlewares[badgerMiddlewareName] = { traefikConfig.http.middlewares[badgerMiddlewareName] = {
plugin: { plugin: {
[badgerMiddlewareName]: { [badgerMiddlewareName]: {
@@ -124,6 +125,7 @@ export async function getTraefikConfig(
tlsServerName: resources.tlsServerName, tlsServerName: resources.tlsServerName,
setHostHeader: resources.setHostHeader, setHostHeader: resources.setHostHeader,
enableProxy: resources.enableProxy, enableProxy: resources.enableProxy,
headers: resources.headers,
// Target fields // Target fields
targetId: targets.targetId, targetId: targets.targetId,
targetEnabled: targets.enabled, targetEnabled: targets.enabled,
@@ -152,7 +154,7 @@ export async function getTraefikConfig(
inArray(sites.type, siteTypes), inArray(sites.type, siteTypes),
config.getRawConfig().traefik.allow_raw_resources config.getRawConfig().traefik.allow_raw_resources
? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true ? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true
: eq(resources.http, true), : eq(resources.http, true)
) )
); );
@@ -177,7 +179,8 @@ export async function getTraefikConfig(
tlsServerName: row.tlsServerName, tlsServerName: row.tlsServerName,
setHostHeader: row.setHostHeader, setHostHeader: row.setHostHeader,
enableProxy: row.enableProxy, enableProxy: row.enableProxy,
targets: [] targets: [],
headers: row.headers
}); });
} }
@@ -296,13 +299,52 @@ export async function getTraefikConfig(
const additionalMiddlewares = const additionalMiddlewares =
config.getRawConfig().traefik.additional_middlewares || []; config.getRawConfig().traefik.additional_middlewares || [];
let routerMiddlewares = [
badgerMiddlewareName,
...additionalMiddlewares
];
if (resource.headers && resource.headers.length > 0) {
const headersMiddlewareName = `${resource.resourceId}-headers-middleware`;
// if there are headers, parse them into an object
let headersObj: { [key: string]: string } = {};
const headersArr = resource.headers.split(",");
for (const header of headersArr) {
const [key, value] = header
.split(":")
.map((s: string) => s.trim());
if (key && value) {
headersObj[key] = value;
}
}
if (resource.setHostHeader) {
headersObj["Host"] = resource.setHostHeader;
}
// check if the object is not empty
if (Object.keys(headersObj).length > 0) {
// Add the headers middleware
if (!config_output.http.middlewares) {
config_output.http.middlewares = {};
}
config_output.http.middlewares[headersMiddlewareName] = {
headers: {
customRequestHeaders: headersObj
}
};
routerMiddlewares.push(headersMiddlewareName);
}
}
config_output.http.routers![routerName] = { config_output.http.routers![routerName] = {
entryPoints: [ entryPoints: [
resource.ssl resource.ssl
? config.getRawConfig().traefik.https_entrypoint ? config.getRawConfig().traefik.https_entrypoint
: config.getRawConfig().traefik.http_entrypoint : config.getRawConfig().traefik.http_entrypoint
], ],
middlewares: [badgerMiddlewareName, ...additionalMiddlewares], middlewares: routerMiddlewares,
service: serviceName, service: serviceName,
rule: `Host(\`${fullDomain}\`)`, rule: `Host(\`${fullDomain}\`)`,
priority: 100, priority: 100,
@@ -413,27 +455,6 @@ export async function getTraefikConfig(
serviceName serviceName
].loadBalancer.serversTransport = transportName; ].loadBalancer.serversTransport = transportName;
} }
// Add the host header middleware
if (resource.setHostHeader) {
if (!config_output.http.middlewares) {
config_output.http.middlewares = {};
}
config_output.http.middlewares[hostHeaderMiddlewareName] = {
headers: {
customRequestHeaders: {
Host: resource.setHostHeader
}
}
};
if (!config_output.http.routers![routerName].middlewares) {
config_output.http.routers![routerName].middlewares = [];
}
config_output.http.routers![routerName].middlewares = [
...config_output.http.routers![routerName].middlewares,
hostHeaderMiddlewareName
];
}
} else { } else {
// Non-HTTP (TCP/UDP) configuration // Non-HTTP (TCP/UDP) configuration
if (!resource.enableProxy) { if (!resource.enableProxy) {