mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-12 14:01:18 +02:00
Seperate dns from certificates
This commit is contained in:
@@ -18,17 +18,26 @@ import { jobScheduler } from "./scheduler";
|
|||||||
|
|
||||||
export async function startCertificateManager() {
|
export async function startCertificateManager() {
|
||||||
const acmeConfig = privateConfig.getRawPrivateConfig().acme;
|
const acmeConfig = privateConfig.getRawPrivateConfig().acme;
|
||||||
if (!acmeConfig || acmeConfig.cert_mode !== "pangolin") {
|
if (
|
||||||
return;
|
acmeConfig &&
|
||||||
}
|
acmeConfig.cert_mode === "pangolin" &&
|
||||||
|
acmeConfig.enable_acme_client
|
||||||
|
) {
|
||||||
logger.info("Starting certificate management server...");
|
logger.info("Starting certificate management server...");
|
||||||
|
|
||||||
// Initialize ACME client
|
// Initialize ACME client
|
||||||
await acmeClientManager.initialize();
|
await acmeClientManager.initialize();
|
||||||
|
|
||||||
// Start job scheduler
|
// Start certificate issuance/renewal jobs
|
||||||
await jobScheduler.start();
|
await jobScheduler.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (privateConfig.getRawPrivateConfig().flags.use_pangolin_dns) {
|
||||||
|
// DNS record validation/reverification doesn't require certs, so it
|
||||||
|
// runs whenever Pangolin is acting as the authoritative DNS server,
|
||||||
|
// independent of the cert manager above.
|
||||||
|
await jobScheduler.startDnsJobs();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stopCertificateManager() {
|
export async function stopCertificateManager() {
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ import { domainReverifier } from "./domain-reverifier";
|
|||||||
const RUN_EXCLUSIVE_TIMEOUT_MS = 30 * 60 * 1000;
|
const RUN_EXCLUSIVE_TIMEOUT_MS = 30 * 60 * 1000;
|
||||||
|
|
||||||
export class JobScheduler {
|
export class JobScheduler {
|
||||||
private intervals: NodeJS.Timeout[] = [];
|
private certIntervals: NodeJS.Timeout[] = [];
|
||||||
private running = false;
|
private dnsIntervals: NodeJS.Timeout[] = [];
|
||||||
|
private certRunning = false;
|
||||||
|
private dnsRunning = false;
|
||||||
|
|
||||||
// Guards against a slow batch (e.g. 10 certs whose DNS challenges take a
|
// Guards against a slow batch (e.g. 10 certs whose DNS challenges take a
|
||||||
// while) still being processed when the next interval tick fires -
|
// while) still being processed when the next interval tick fires -
|
||||||
@@ -59,19 +61,19 @@ export class JobScheduler {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Certificate issuance/renewal - requires an ACME client, so this is
|
||||||
|
// only started when Pangolin is actually managing certs.
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
if (this.running) {
|
if (this.certRunning) {
|
||||||
logger.warn("Scheduler is already running");
|
logger.warn("Certificate job scheduler is already running");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.running = true;
|
this.certRunning = true;
|
||||||
logger.info("Starting job scheduler");
|
logger.info("Starting certificate job scheduler");
|
||||||
|
|
||||||
const newCertState = { active: false };
|
const newCertState = { active: false };
|
||||||
const renewalState = { active: false };
|
const renewalState = { active: false };
|
||||||
const dnsValidationState = { active: false };
|
|
||||||
const reverifyState = { active: false };
|
|
||||||
|
|
||||||
const runNewCertCheck = this.runExclusive(
|
const runNewCertCheck = this.runExclusive(
|
||||||
() => certificateService.processPendingCertificates(),
|
() => certificateService.processPendingCertificates(),
|
||||||
@@ -83,16 +85,6 @@ export class JobScheduler {
|
|||||||
renewalState,
|
renewalState,
|
||||||
"processing renewal candidates"
|
"processing renewal candidates"
|
||||||
);
|
);
|
||||||
const runDnsValidation = this.runExclusive(
|
|
||||||
() => dnsValidator.validateAll(),
|
|
||||||
dnsValidationState,
|
|
||||||
"validating DNS records"
|
|
||||||
);
|
|
||||||
const runReverify = this.runExclusive(
|
|
||||||
() => domainReverifier.reverifyAll(),
|
|
||||||
reverifyState,
|
|
||||||
"reverifying domains"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Schedule new certificate processing
|
// Schedule new certificate processing
|
||||||
const newCertInterval = setInterval(
|
const newCertInterval = setInterval(
|
||||||
@@ -106,10 +98,51 @@ export class JobScheduler {
|
|||||||
config.getRawConfig().acme!.renewal_check_interval_ms
|
config.getRawConfig().acme!.renewal_check_interval_ms
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.certIntervals.push(newCertInterval, renewalInterval);
|
||||||
|
|
||||||
|
// Run initial checks
|
||||||
|
setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
await runNewCertCheck();
|
||||||
|
// await runRenewalCheck();
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error in initial certificate processing:", error);
|
||||||
|
}
|
||||||
|
}, 1000); // Wait 1 second after startup
|
||||||
|
|
||||||
|
logger.info("Certificate job scheduler started successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS record validation/reverification - doesn't touch certs at all, so
|
||||||
|
// this runs independently whenever Pangolin is acting as the
|
||||||
|
// authoritative DNS server, regardless of cert_mode.
|
||||||
|
async startDnsJobs(): Promise<void> {
|
||||||
|
if (this.dnsRunning) {
|
||||||
|
logger.warn("DNS validation job scheduler is already running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dnsRunning = true;
|
||||||
|
logger.info("Starting DNS validation job scheduler");
|
||||||
|
|
||||||
|
const dnsValidationState = { active: false };
|
||||||
|
const reverifyState = { active: false };
|
||||||
|
|
||||||
|
const runDnsValidation = this.runExclusive(
|
||||||
|
() => dnsValidator.validateAll(),
|
||||||
|
dnsValidationState,
|
||||||
|
"validating DNS records"
|
||||||
|
);
|
||||||
|
const runReverify = this.runExclusive(
|
||||||
|
() => domainReverifier.reverifyAll(),
|
||||||
|
reverifyState,
|
||||||
|
"reverifying domains"
|
||||||
|
);
|
||||||
|
|
||||||
// Schedule DNS validation
|
// Schedule DNS validation
|
||||||
const dnsValidationInterval = setInterval(
|
const dnsValidationInterval = setInterval(
|
||||||
runDnsValidation,
|
runDnsValidation,
|
||||||
config.getRawConfig().acme?.dns_check_interval_ms
|
config.getRawConfig().acme?.dns_check_interval_ms ?? 60000
|
||||||
);
|
);
|
||||||
|
|
||||||
// Schedule periodic reverification of already-verified domains
|
// Schedule periodic reverification of already-verified domains
|
||||||
@@ -119,44 +152,38 @@ export class JobScheduler {
|
|||||||
3600000
|
3600000
|
||||||
);
|
);
|
||||||
|
|
||||||
this.intervals.push(
|
this.dnsIntervals.push(dnsValidationInterval, reverifyInterval);
|
||||||
newCertInterval,
|
|
||||||
renewalInterval,
|
|
||||||
dnsValidationInterval,
|
|
||||||
reverifyInterval
|
|
||||||
);
|
|
||||||
|
|
||||||
// Run initial checks
|
// Run an initial validation pass shortly after startup
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
await runNewCertCheck();
|
|
||||||
// await runRenewalCheck();
|
|
||||||
await runDnsValidation();
|
await runDnsValidation();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Error in initial certificate processing:", error);
|
logger.error("Error in initial DNS validation:", error);
|
||||||
}
|
}
|
||||||
}, 1000); // Wait 5 seconds after startup
|
}, 1000);
|
||||||
|
|
||||||
logger.info("Job scheduler started successfully");
|
logger.info("DNS validation job scheduler started successfully");
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop(): Promise<void> {
|
async stop(): Promise<void> {
|
||||||
if (!this.running) {
|
if (this.certRunning) {
|
||||||
return;
|
logger.info("Stopping certificate job scheduler");
|
||||||
|
this.certRunning = false;
|
||||||
|
this.certIntervals.forEach((interval) => clearInterval(interval));
|
||||||
|
this.certIntervals = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Stopping job scheduler");
|
if (this.dnsRunning) {
|
||||||
this.running = false;
|
logger.info("Stopping DNS validation job scheduler");
|
||||||
|
this.dnsRunning = false;
|
||||||
// Clear all intervals
|
this.dnsIntervals.forEach((interval) => clearInterval(interval));
|
||||||
this.intervals.forEach((interval) => clearInterval(interval));
|
this.dnsIntervals = [];
|
||||||
this.intervals = [];
|
}
|
||||||
|
|
||||||
logger.info("Job scheduler stopped");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isRunning(): boolean {
|
isRunning(): boolean {
|
||||||
return this.running;
|
return this.certRunning || this.dnsRunning;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ export const privateConfigSchema = z
|
|||||||
.enum(["traefik", "pangolin"])
|
.enum(["traefik", "pangolin"])
|
||||||
.optional()
|
.optional()
|
||||||
.default("traefik"),
|
.default("traefik"),
|
||||||
|
enable_acme_client: z.boolean().optional().default(false),
|
||||||
// @deprecated Moved to the public config file
|
// @deprecated Moved to the public config file
|
||||||
// (server/lib/readConfigFile.ts). Kept here only so existing private
|
// (server/lib/readConfigFile.ts). Kept here only so existing private
|
||||||
// config files keep parsing; any value set here is migrated into the
|
// config files keep parsing; any value set here is migrated into the
|
||||||
|
|||||||
Reference in New Issue
Block a user