@serve.zone/dcrouter 17.9.2 → 17.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deno.json +1 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.js +7 -2
- package/dist_ts/dns/classes.dns-server-runtime.d.ts +11 -0
- package/dist_ts/dns/classes.dns-server-runtime.js +81 -1
- package/dist_ts_migrations/index.js +70 -9
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +8 -1
- package/ts/dns/classes.dns-server-runtime.ts +88 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/dcrouter',
|
|
6
|
-
version: '17.9.
|
|
6
|
+
version: '17.9.3',
|
|
7
7
|
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfd2ViLzAwX2NvbW1pdGluZm9fZGF0YS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRztJQUN4QixJQUFJLEVBQUUsc0JBQXNCO0lBQzVCLE9BQU8sRUFBRSxRQUFRO0lBQ2pCLFdBQVcsRUFBRSwwRUFBMEU7Q0FDeEYsQ0FBQSJ9
|
package/package.json
CHANGED
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/classes.dcrouter.ts
CHANGED
|
@@ -743,6 +743,9 @@ export class DcRouter {
|
|
|
743
743
|
} catch (err: unknown) {
|
|
744
744
|
logger.log('error', `Failed to sync Remote Ingress allowed edges: ${(err as Error).message}`);
|
|
745
745
|
}
|
|
746
|
+
this.dnsServerRuntime.syncPrivateRouteOverrides(
|
|
747
|
+
routes as IDcRouterRouteConfig[],
|
|
748
|
+
);
|
|
746
749
|
},
|
|
747
750
|
(preparedRoutes) => buildHttpRedirectRuntimeRoutes(preparedRoutes || []),
|
|
748
751
|
(storedRoute: IRoute) => this.emailRouteBuilder.hydrateStoredRouteForRuntime(storedRoute),
|
|
@@ -832,10 +835,14 @@ export class DcRouter {
|
|
|
832
835
|
|
|
833
836
|
// DNS Server: optional, depends on SmartProxy
|
|
834
837
|
if (this.options.dnsNsDomains && this.options.dnsNsDomains.length > 0 && this.options.dnsScopes && this.options.dnsScopes.length > 0) {
|
|
838
|
+
const dnsServerDeps = ['SmartProxy'];
|
|
839
|
+
if (this.options.dbConfig?.enabled !== false) {
|
|
840
|
+
dnsServerDeps.push('ConfigManagers', 'EmailServer');
|
|
841
|
+
}
|
|
835
842
|
this.serviceManager.addService(
|
|
836
843
|
new plugins.taskbuffer.Service('DnsServer')
|
|
837
844
|
.optional()
|
|
838
|
-
.dependsOn(
|
|
845
|
+
.dependsOn(...dnsServerDeps)
|
|
839
846
|
.withStart(async () => {
|
|
840
847
|
await this.dnsServerRuntime.setup();
|
|
841
848
|
})
|
|
@@ -2,6 +2,7 @@ import * as plugins from '../plugins.js';
|
|
|
2
2
|
import { logger } from '../logger.js';
|
|
3
3
|
import { buildEmailDnsRecords } from '../email/index.js';
|
|
4
4
|
import type { DcRouter } from '../classes.dcrouter.js';
|
|
5
|
+
import type { IDcRouterRouteConfig } from '../../ts_interfaces/data/remoteingress.js';
|
|
5
6
|
|
|
6
7
|
type TDnsRecordSeed = { name: string; type: string; value: string; ttl?: number; useIngressProxy?: boolean };
|
|
7
8
|
|
|
@@ -18,6 +19,8 @@ export class DnsServerRuntime {
|
|
|
18
19
|
private batchCount = 0;
|
|
19
20
|
private batchTimer: ReturnType<typeof setTimeout> | null = null;
|
|
20
21
|
|
|
22
|
+
private privateRouteHostnames = new Set<string>();
|
|
23
|
+
private privateRouteTargetIp?: string;
|
|
21
24
|
constructor(private dcRouterRef: DcRouter) {}
|
|
22
25
|
|
|
23
26
|
/**
|
|
@@ -58,6 +61,10 @@ export class DnsServerRuntime {
|
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
|
|
64
|
+
this.privateRouteTargetIp = plugins.net.isIP(vmIpAddress)
|
|
65
|
+
&& vmIpAddress !== '0.0.0.0' && vmIpAddress !== '::'
|
|
66
|
+
? vmIpAddress
|
|
67
|
+
: undefined;
|
|
61
68
|
// Create DNS server instance with manual HTTPS mode
|
|
62
69
|
const dnsServer = new plugins.smartdns.dnsServerMod.DnsServer({
|
|
63
70
|
udpPort: 53,
|
|
@@ -72,6 +79,7 @@ export class DnsServerRuntime {
|
|
|
72
79
|
httpsCert: ''
|
|
73
80
|
});
|
|
74
81
|
this.dcRouterRef.dnsServer = dnsServer;
|
|
82
|
+
this.registerPrivateRouteHandler(dnsServer);
|
|
75
83
|
|
|
76
84
|
// Start the DNS server. smartdns owns UDP and DNS-over-TCP; dcrouter
|
|
77
85
|
// keeps DNS-over-HTTPS routed through SmartProxy manual HTTPS mode.
|
|
@@ -172,6 +180,86 @@ export class DnsServerRuntime {
|
|
|
172
180
|
this.dcRouterRef.mailDnsSync?.requestSync('DNS server attached');
|
|
173
181
|
}
|
|
174
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Keep an internal-only A-record overlay for exact route hostnames whose
|
|
185
|
+
* compiled source policy is private or whose only ingress is SmartVPN.
|
|
186
|
+
* Public routes compile without clientIp restrictions and are never added.
|
|
187
|
+
*/
|
|
188
|
+
public syncPrivateRouteOverrides(routesArg: IDcRouterRouteConfig[]): void {
|
|
189
|
+
const nextHostnames = new Set<string>();
|
|
190
|
+
for (const route of routesArg) {
|
|
191
|
+
if (route.action?.type !== 'forward') continue;
|
|
192
|
+
const ingress = route.ingress;
|
|
193
|
+
if (!ingress?.directHub && !ingress?.smartVpn) continue;
|
|
194
|
+
|
|
195
|
+
const clientIp = route.match?.clientIp;
|
|
196
|
+
const clientIpEntries = Array.isArray(clientIp)
|
|
197
|
+
? clientIp
|
|
198
|
+
: [];
|
|
199
|
+
const hasRestrictedSourceProfile = clientIpEntries
|
|
200
|
+
.map((entryArg) => entryArg.trim())
|
|
201
|
+
.filter(Boolean)
|
|
202
|
+
.some((entryArg) => entryArg !== '*');
|
|
203
|
+
const isSmartVpnOnly = ingress.smartVpn && !ingress.directHub;
|
|
204
|
+
if (!hasRestrictedSourceProfile && !isSmartVpnOnly) continue;
|
|
205
|
+
|
|
206
|
+
const domains = route.match?.domains;
|
|
207
|
+
const domainEntries = Array.isArray(domains)
|
|
208
|
+
? domains
|
|
209
|
+
: typeof domains === 'string'
|
|
210
|
+
? domains.split(',')
|
|
211
|
+
: [];
|
|
212
|
+
for (const domainArg of domainEntries) {
|
|
213
|
+
const hostname = this.normalizePrivateRouteHostname(domainArg);
|
|
214
|
+
if (hostname) nextHostnames.add(hostname);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const changed = nextHostnames.size !== this.privateRouteHostnames.size
|
|
219
|
+
|| [...nextHostnames].some((hostnameArg) => !this.privateRouteHostnames.has(hostnameArg));
|
|
220
|
+
this.privateRouteHostnames = nextHostnames;
|
|
221
|
+
if (changed) {
|
|
222
|
+
logger.log(
|
|
223
|
+
'info',
|
|
224
|
+
`DNS private-route overlay now serves ${nextHostnames.size} exact hostname(s)`,
|
|
225
|
+
{ zone: 'dns' },
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private registerPrivateRouteHandler(
|
|
231
|
+
dnsServerArg: plugins.smartdns.dnsServerMod.DnsServer,
|
|
232
|
+
): void {
|
|
233
|
+
dnsServerArg.registerHandler('*', ['A'], (questionArg) => {
|
|
234
|
+
const hostname = this.normalizePrivateRouteHostname(questionArg.name);
|
|
235
|
+
if (
|
|
236
|
+
!hostname
|
|
237
|
+
|| questionArg.type.toUpperCase() !== 'A'
|
|
238
|
+
|| !this.privateRouteTargetIp
|
|
239
|
+
|| !this.privateRouteHostnames.has(hostname)
|
|
240
|
+
) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
name: hostname,
|
|
245
|
+
type: 'A',
|
|
246
|
+
class: 'IN',
|
|
247
|
+
ttl: 60,
|
|
248
|
+
data: this.privateRouteTargetIp,
|
|
249
|
+
};
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private normalizePrivateRouteHostname(hostnameArg: string): string | undefined {
|
|
254
|
+
const hostname = hostnameArg.trim().toLowerCase().replace(/\.$/, '');
|
|
255
|
+
if (!hostname || hostname.includes('*') || hostname.includes(',')) return undefined;
|
|
256
|
+
if (hostname.length > 253) return undefined;
|
|
257
|
+
const labels = hostname.split('.');
|
|
258
|
+
if (labels.some((labelArg) => !labelArg || labelArg.length > 63
|
|
259
|
+
|| !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(labelArg))) return undefined;
|
|
260
|
+
return hostname;
|
|
261
|
+
}
|
|
262
|
+
|
|
175
263
|
/**
|
|
176
264
|
* Create the DoH socket handler SmartProxy routes hand TLS sockets to.
|
|
177
265
|
*/
|