@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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '17.9.2',
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@serve.zone/dcrouter",
3
3
  "private": false,
4
- "version": "17.9.2",
4
+ "version": "17.9.3",
5
5
  "description": "A multifaceted routing service handling mail and SMS delivery functions.",
6
6
  "type": "module",
7
7
  "bin": {
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '17.9.2',
6
+ version: '17.9.3',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -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('SmartProxy', ...((this.options.dbConfig?.enabled !== false) ? ['EmailServer'] : []))
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
  */
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '17.9.2',
6
+ version: '17.9.3',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }