mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-04 09:46:40 +00:00
traefik config update for custom Cert Resolver
This commit is contained in:
@@ -19,7 +19,7 @@ export const domains = pgTable("domains", {
|
|||||||
verified: boolean("verified").notNull().default(false),
|
verified: boolean("verified").notNull().default(false),
|
||||||
failed: boolean("failed").notNull().default(false),
|
failed: boolean("failed").notNull().default(false),
|
||||||
tries: integer("tries").notNull().default(0),
|
tries: integer("tries").notNull().default(0),
|
||||||
certResolver: varchar("certResolver").default("letsencrypt"),
|
certResolver: varchar("certResolver"),
|
||||||
customCertResolver: varchar("customCertResolver")
|
customCertResolver: varchar("customCertResolver")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const domains = sqliteTable("domains", {
|
|||||||
verified: integer("verified", { mode: "boolean" }).notNull().default(false),
|
verified: integer("verified", { mode: "boolean" }).notNull().default(false),
|
||||||
failed: integer("failed", { mode: "boolean" }).notNull().default(false),
|
failed: integer("failed", { mode: "boolean" }).notNull().default(false),
|
||||||
tries: integer("tries").notNull().default(0),
|
tries: integer("tries").notNull().default(0),
|
||||||
certResolver: text("certResolver").default("letsencrypt"),
|
certResolver: text("certResolver"),
|
||||||
customCertResolver: text("customCertResolver")
|
customCertResolver: text("customCertResolver")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export const configSchema = z
|
|||||||
.nonempty("base_domain must not be empty")
|
.nonempty("base_domain must not be empty")
|
||||||
.transform((url) => url.toLowerCase()),
|
.transform((url) => url.toLowerCase()),
|
||||||
cert_resolver: z.string().optional().default("letsencrypt"),
|
cert_resolver: z.string().optional().default("letsencrypt"),
|
||||||
|
custom_cert_resolver: z.string().optional(),
|
||||||
prefer_wildcard_cert: z.boolean().optional().default(false)
|
prefer_wildcard_cert: z.boolean().optional().default(false)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -187,6 +188,7 @@ export const configSchema = z
|
|||||||
https_entrypoint: z.string().optional().default("websecure"),
|
https_entrypoint: z.string().optional().default("websecure"),
|
||||||
additional_middlewares: z.array(z.string()).optional(),
|
additional_middlewares: z.array(z.string()).optional(),
|
||||||
cert_resolver: z.string().optional().default("letsencrypt"),
|
cert_resolver: z.string().optional().default("letsencrypt"),
|
||||||
|
custom_cert_resolver: z.string().optional(),
|
||||||
prefer_wildcard_cert: z.boolean().optional().default(false),
|
prefer_wildcard_cert: z.boolean().optional().default(false),
|
||||||
certificates_path: z.string().default("/var/certificates"),
|
certificates_path: z.string().default("/var/certificates"),
|
||||||
monitor_interval: z.number().default(5000),
|
monitor_interval: z.number().default(5000),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { db, targetHealthCheck } from "@server/db";
|
import { db, targetHealthCheck, domains } from "@server/db";
|
||||||
import {
|
import {
|
||||||
and,
|
and,
|
||||||
eq,
|
eq,
|
||||||
@@ -75,11 +75,15 @@ export async function getTraefikConfig(
|
|||||||
siteType: sites.type,
|
siteType: sites.type,
|
||||||
siteOnline: sites.online,
|
siteOnline: sites.online,
|
||||||
subnet: sites.subnet,
|
subnet: sites.subnet,
|
||||||
exitNodeId: sites.exitNodeId
|
exitNodeId: sites.exitNodeId,
|
||||||
|
// Domain cert resolver fields
|
||||||
|
domainCertResolver: domains.certResolver,
|
||||||
|
domainCustomCertResolver: domains.customCertResolver
|
||||||
})
|
})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
||||||
.innerJoin(resources, eq(resources.resourceId, targets.resourceId))
|
.innerJoin(resources, eq(resources.resourceId, targets.resourceId))
|
||||||
|
.leftJoin(domains, eq(domains.domainId, resources.domainId))
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
targetHealthCheck,
|
targetHealthCheck,
|
||||||
eq(targetHealthCheck.targetId, targets.targetId)
|
eq(targetHealthCheck.targetId, targets.targetId)
|
||||||
@@ -161,7 +165,10 @@ export async function getTraefikConfig(
|
|||||||
pathMatchType: row.pathMatchType, // the targets will all have the same pathMatchType
|
pathMatchType: row.pathMatchType, // the targets will all have the same pathMatchType
|
||||||
rewritePath: row.rewritePath,
|
rewritePath: row.rewritePath,
|
||||||
rewritePathType: row.rewritePathType,
|
rewritePathType: row.rewritePathType,
|
||||||
priority: priority // may be null, we fallback later
|
priority: priority,
|
||||||
|
// Store domain cert resolver fields
|
||||||
|
domainCertResolver: row.domainCertResolver,
|
||||||
|
domainCustomCertResolver: row.domainCustomCertResolver
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,18 +249,73 @@ export async function getTraefikConfig(
|
|||||||
|
|
||||||
const configDomain = config.getDomain(resource.domainId);
|
const configDomain = config.getDomain(resource.domainId);
|
||||||
|
|
||||||
let certResolver: string, preferWildcardCert: boolean;
|
let certResolverFromConfig: string | undefined;
|
||||||
if (!configDomain) {
|
let preferWildcardCert = false;
|
||||||
certResolver = config.getRawConfig().traefik.cert_resolver;
|
|
||||||
preferWildcardCert =
|
const rawTraefikCfg = config.getRawConfig().traefik || {};
|
||||||
config.getRawConfig().traefik.prefer_wildcard_cert;
|
const globalDefaultResolver: string | undefined = rawTraefikCfg.cert_resolver;
|
||||||
|
const availableResolvers = rawTraefikCfg.custom_cert_resolver
|
||||||
|
? Object.keys(rawTraefikCfg.custom_cert_resolver)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
// Priority 1: Read from YAML config (if exists)
|
||||||
|
if (configDomain) {
|
||||||
|
certResolverFromConfig =
|
||||||
|
configDomain.cert_resolver ??
|
||||||
|
configDomain.custom_cert_resolver;
|
||||||
|
preferWildcardCert = !!(configDomain.prefer_wildcard_cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Priority 2: Override with database domain settings (if exists)
|
||||||
|
let finalCertResolver: string | undefined;
|
||||||
|
let finalCustomCertResolver: string | undefined;
|
||||||
|
|
||||||
|
if (resource.domainCertResolver) {
|
||||||
|
finalCertResolver = resource.domainCertResolver;
|
||||||
|
if (resource.domainCertResolver === "custom" && resource.domainCustomCertResolver) {
|
||||||
|
finalCustomCertResolver = resource.domainCustomCertResolver;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
certResolver = configDomain.cert_resolver;
|
// Fall back to config
|
||||||
preferWildcardCert = configDomain.prefer_wildcard_cert;
|
finalCertResolver = certResolverFromConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the final resolver name
|
||||||
|
let resolverName: string | undefined;
|
||||||
|
|
||||||
|
if (finalCertResolver) {
|
||||||
|
if (finalCertResolver === "custom") {
|
||||||
|
// Check database custom resolver first, then config
|
||||||
|
const customResolver = finalCustomCertResolver || configDomain?.custom_cert_resolver;
|
||||||
|
|
||||||
|
if (customResolver && typeof customResolver === "string" && customResolver.trim()) {
|
||||||
|
resolverName = customResolver.trim();
|
||||||
|
} else {
|
||||||
|
resolverName = globalDefaultResolver;
|
||||||
|
logger.warn(
|
||||||
|
`Domain ${resource.domainId} requested custom cert resolver but none set; falling back to global resolver ${resolverName}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Validate against available resolvers
|
||||||
|
if (
|
||||||
|
availableResolvers.length === 0 ||
|
||||||
|
availableResolvers.includes(finalCertResolver)
|
||||||
|
) {
|
||||||
|
resolverName = finalCertResolver;
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
`Unknown cert resolver "${finalCertResolver}" for domain ${resource.domainId}; falling back to global resolver.`
|
||||||
|
);
|
||||||
|
resolverName = globalDefaultResolver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolverName = globalDefaultResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tls = {
|
const tls = {
|
||||||
certResolver: certResolver,
|
certResolver: resolverName,
|
||||||
...(preferWildcardCert
|
...(preferWildcardCert
|
||||||
? {
|
? {
|
||||||
domains: [
|
domains: [
|
||||||
|
|||||||
Reference in New Issue
Block a user