Add dns server

This commit is contained in:
Owen
2026-09-09 10:43:28 -04:00
parent 82c5dcf16f
commit 9e23a0a6ee
10 changed files with 1542 additions and 0 deletions
+30
View File
@@ -61,6 +61,7 @@
"cors": "2.8.6", "cors": "2.8.6",
"crypto-js": "4.2.0", "crypto-js": "4.2.0",
"d3": "7.9.0", "d3": "7.9.0",
"dns-packet": "^5.6.1",
"drizzle-orm": "0.45.2", "drizzle-orm": "0.45.2",
"express": "5.2.1", "express": "5.2.1",
"express-rate-limit": "8.7.0", "express-rate-limit": "8.7.0",
@@ -124,6 +125,7 @@
"@types/cors": "2.8.19", "@types/cors": "2.8.19",
"@types/crypto-js": "4.2.2", "@types/crypto-js": "4.2.2",
"@types/d3": "7.4.3", "@types/d3": "7.4.3",
"@types/dns-packet": "^5.6.5",
"@types/express": "5.0.6", "@types/express": "5.0.6",
"@types/express-session": "1.19.0", "@types/express-session": "1.19.0",
"@types/jmespath": "0.15.2", "@types/jmespath": "0.15.2",
@@ -2445,6 +2447,12 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@leichtgewicht/ip-codec": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
"integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==",
"license": "MIT"
},
"node_modules/@levischuck/tiny-cbor": { "node_modules/@levischuck/tiny-cbor": {
"version": "0.2.11", "version": "0.2.11",
"resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz", "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz",
@@ -6764,6 +6772,16 @@
"@types/d3-selection": "*" "@types/d3-selection": "*"
} }
}, },
"node_modules/@types/dns-packet": {
"version": "5.6.5",
"resolved": "https://registry.npmjs.org/@types/dns-packet/-/dns-packet-5.6.5.tgz",
"integrity": "sha512-qXOC7XLOEe43ehtWJCMnQXvgcIpv6rPmQ1jXT98Ad8A3TB1Ue50jsCbSSSyuazScEuZ/Q026vHbrOTVkmwA+7Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/esrecurse": { "node_modules/@types/esrecurse": {
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
@@ -9261,6 +9279,18 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/dns-packet": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
"integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==",
"license": "MIT",
"dependencies": {
"@leichtgewicht/ip-codec": "^2.0.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/doctrine": { "node_modules/doctrine": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+2
View File
@@ -84,6 +84,7 @@
"cors": "2.8.6", "cors": "2.8.6",
"crypto-js": "4.2.0", "crypto-js": "4.2.0",
"d3": "7.9.0", "d3": "7.9.0",
"dns-packet": "^5.6.1",
"drizzle-orm": "0.45.2", "drizzle-orm": "0.45.2",
"express": "5.2.1", "express": "5.2.1",
"express-rate-limit": "8.7.0", "express-rate-limit": "8.7.0",
@@ -147,6 +148,7 @@
"@types/cors": "2.8.19", "@types/cors": "2.8.19",
"@types/crypto-js": "4.2.2", "@types/crypto-js": "4.2.2",
"@types/d3": "7.4.3", "@types/d3": "7.4.3",
"@types/dns-packet": "^5.6.5",
"@types/express": "5.0.6", "@types/express": "5.0.6",
"@types/express-session": "1.19.0", "@types/express-session": "1.19.0",
"@types/jmespath": "0.15.2", "@types/jmespath": "0.15.2",
+8
View File
@@ -0,0 +1,8 @@
export async function startDnsServer() {
// No-op: the authoritative DNS server is only available in builds
// that include the private/enterprise feature set.
}
export async function stopDnsServer() {
// No-op counterpart to startDnsServer.
}
+3
View File
@@ -25,6 +25,7 @@ import { setHostMeta } from "@server/lib/hostMeta";
import { TraefikConfigManager } from "@server/lib/traefik/TraefikConfigManager"; import { TraefikConfigManager } from "@server/lib/traefik/TraefikConfigManager";
import { initCleanup } from "#dynamic/cleanup"; import { initCleanup } from "#dynamic/cleanup";
import { startSchedulers } from "#dynamic/startSchedulers"; import { startSchedulers } from "#dynamic/startSchedulers";
import { startDnsServer } from "#dynamic/dns";
import license from "#dynamic/license/license"; import license from "#dynamic/license/license";
import { fetchServerIp } from "@server/lib/serverIpService"; import { fetchServerIp } from "@server/lib/serverIpService";
import { initAiModelCatalog } from "@server/lib/aiModelCatalog"; import { initAiModelCatalog } from "@server/lib/aiModelCatalog";
@@ -45,6 +46,8 @@ async function startServers() {
startSchedulers(); startSchedulers();
await startDnsServer();
// Start all servers // Start all servers
const apiServer = createApiServer(); const apiServer = createApiServer();
const internalServer = createInternalServer(); const internalServer = createInternalServer();
+2
View File
@@ -20,6 +20,7 @@ import { flushSiteBandwidthToDb } from "@server/routers/gerbil/receiveBandwidth"
import { stopPingAccumulator } from "@server/routers/newt/pingAccumulator"; import { stopPingAccumulator } from "@server/routers/newt/pingAccumulator";
import { shutdownUsageRecorder } from "@server/lib/aiBudgetEnforcement"; import { shutdownUsageRecorder } from "@server/lib/aiBudgetEnforcement";
import { shutdownAiSessionLogger } from "@server/routers/aiGateway/logAiSession"; import { shutdownAiSessionLogger } from "@server/routers/aiGateway/logAiSession";
import { stopDnsServer } from "./dns";
async function cleanup() { async function cleanup() {
await stopPingAccumulator(); await stopPingAccumulator();
@@ -31,6 +32,7 @@ async function cleanup() {
await rateLimitService.cleanup(); await rateLimitService.cleanup();
await wsCleanup(); await wsCleanup();
await logStreamingManager.shutdown(); await logStreamingManager.shutdown();
await stopDnsServer();
process.exit(0); process.exit(0);
} }
+47
View File
@@ -0,0 +1,47 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025-2026 Fossorial, Inc.
* All rights reserved.
*
* This file is licensed under the Fossorial Commercial License.
* You may not use this file except in compliance with the License.
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
*
* This file is not licensed under the AGPLv3.
*/
import { AuthoritativeDNSServer } from "#private/lib/dns";
import { privateConfig } from "#private/lib/config";
let dnsServer: AuthoritativeDNSServer | undefined;
export async function startDnsServer() {
const dnsConfig = privateConfig.getRawPrivateConfig().dns;
if (!dnsConfig) {
return;
}
const cacheOptions = {
stdTTL: 300, // 5 minutes default TTL
checkperiod: 60, // Check for expired keys every 60 seconds
useClones: false // Better performance
};
// Create DNS server
dnsServer = new AuthoritativeDNSServer(
dnsConfig.listen_port,
cacheOptions
);
await dnsServer.start();
}
export async function stopDnsServer() {
if (!dnsServer) {
return;
}
await dnsServer.stop();
dnsServer = undefined;
}
+4
View File
@@ -152,6 +152,10 @@ export class PrivateConfig {
return this.rawPrivateConfig; return this.rawPrivateConfig;
} }
public getRawConfig() {
return this.getRawPrivateConfig();
}
// `flags.enable_acme_cert_sync`, `flags.disable_private_http_placeholder`, // `flags.enable_acme_cert_sync`, `flags.disable_private_http_placeholder`,
// and `acme` used to live in the private config file. They now live in // and `acme` used to live in the private config file. They now live in
// the public config file. If an operator still has them set in the // the public config file. If an operator still has them set in the
+1
View File
@@ -0,0 +1 @@
export * from "./server";
File diff suppressed because it is too large Load Diff
+63
View File
@@ -95,6 +95,69 @@ export const privateConfigSchema = z
.optional() .optional()
}) })
.optional(), .optional(),
dns: z
.object({
listen_port: z.number(),
nameserver_name: z.string(),
cname_extension: z.string(),
site_extension: z.string(),
cname_alternate_extensions: z
.array(z.string())
.optional()
.default([]),
alternate_nameservers: z
.array(z.string())
.optional()
.default([]),
rate_limit: z
.object({
enabled: z.boolean().optional().default(true),
window_ms: z
.number()
.int()
.min(1000)
.max(600000)
.optional()
.default(60000),
max_requests: z
.number()
.int()
.min(50)
.max(100000)
.optional()
.default(1200),
max_requests_per_query_type: z
.number()
.int()
.min(10)
.max(50000)
.optional()
.default(600)
})
.default({
enabled: true,
window_ms: 60000,
max_requests: 1200,
max_requests_per_query_type: 600
}),
static_records: z
.array(
z.object({
domain: z.string(),
type: z.enum(["TXT", "CNAME", "A", "NS"]),
value: z.string(),
ttl: z
.number()
.int()
.positive()
.optional()
.default(300)
})
)
.optional()
.default([])
})
.optional(),
gerbil: z gerbil: z
.object({ .object({
local_exit_node_reachable_at: z local_exit_node_reachable_at: z