mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-11 21:41:41 +02:00
Add license checks to JobScheduler and AuthoritativeDNSServer
This commit is contained in:
@@ -17,6 +17,7 @@ import { certificateService } from "./certificate-service";
|
||||
import { privateConfig as config } from "#private/lib/config";
|
||||
import { dnsValidator } from "./dns-validator";
|
||||
import { domainReverifier } from "./domain-reverifier";
|
||||
import license from "#private/license/license";
|
||||
|
||||
// Backstop for runExclusive: no single job's own internal timeouts (e.g.
|
||||
// certificate-service's per-cert issuance timeout) are relied on here. This
|
||||
@@ -44,6 +45,12 @@ export class JobScheduler {
|
||||
label: string
|
||||
): () => Promise<void> {
|
||||
return async () => {
|
||||
if (!(await license.isUnlocked())) {
|
||||
logger.debug(
|
||||
`Skipping ${label} tick - license is not subscribed`
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (state.active) {
|
||||
logger.debug(
|
||||
`Skipping ${label} tick - previous run still in progress`
|
||||
|
||||
@@ -33,6 +33,7 @@ import * as dnsResolver from "dns";
|
||||
import logger from "@server/logger";
|
||||
import { listExitNodes } from "../exitNodes";
|
||||
import { rateLimitService } from "../rateLimit";
|
||||
import license from "#private/license/license";
|
||||
|
||||
type DNSRecord = {
|
||||
id: number;
|
||||
@@ -57,6 +58,13 @@ export class AuthoritativeDNSServer {
|
||||
private allDomains: Set<string> = new Set();
|
||||
private domainRefreshInterval: NodeJS.Timeout | null = null;
|
||||
|
||||
// Cached license/subscription status. license.isUnlocked() does a DB
|
||||
// round-trip on every call, so it can't be checked per-query on a UDP
|
||||
// server that may see very high query volume - instead it's polled on
|
||||
// the same cadence as the domain set refresh and read from memory here.
|
||||
private isLicensed: boolean = false;
|
||||
private licenseRefreshInterval: NodeJS.Timeout | null = null;
|
||||
|
||||
// Cache for per-queryName zone resolution and SOA records
|
||||
private authoritativeDomainCache: NodeCache = new NodeCache({
|
||||
stdTTL: 300,
|
||||
@@ -121,6 +129,15 @@ export class AuthoritativeDNSServer {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isLicensed) {
|
||||
logger.debug(
|
||||
"Refusing DNS query - license is not subscribed"
|
||||
);
|
||||
// REFUSED (rcode=5) indicates a policy refusal by this nameserver.
|
||||
this.sendResponse(packet, [], rinfo, false, 5, []);
|
||||
return;
|
||||
}
|
||||
|
||||
const question = packet.questions[0];
|
||||
const dnsRateLimit = config.getRawConfig().dns!.rate_limit;
|
||||
|
||||
@@ -1027,6 +1044,15 @@ export class AuthoritativeDNSServer {
|
||||
}
|
||||
}
|
||||
|
||||
private async refreshLicenseStatus(): Promise<void> {
|
||||
try {
|
||||
this.isLicensed = await license.isUnlocked();
|
||||
} catch (error) {
|
||||
logger.error("Failed to refresh license status:", error);
|
||||
this.isLicensed = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async start(): Promise<void> {
|
||||
await this.loadAllDomains();
|
||||
this.domainRefreshInterval = setInterval(() => {
|
||||
@@ -1035,6 +1061,11 @@ export class AuthoritativeDNSServer {
|
||||
);
|
||||
}, 60_000);
|
||||
|
||||
await this.refreshLicenseStatus();
|
||||
this.licenseRefreshInterval = setInterval(() => {
|
||||
this.refreshLicenseStatus();
|
||||
}, 60_000);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.server.bind(this.port, (err?: Error) => {
|
||||
if (err) {
|
||||
@@ -1050,6 +1081,9 @@ export class AuthoritativeDNSServer {
|
||||
if (this.domainRefreshInterval) {
|
||||
clearInterval(this.domainRefreshInterval);
|
||||
}
|
||||
if (this.licenseRefreshInterval) {
|
||||
clearInterval(this.licenseRefreshInterval);
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
this.server.close(() => {
|
||||
logger.info("DNS server stopped");
|
||||
|
||||
Reference in New Issue
Block a user