@serve.zone/dcrouter 11.14.0 → 11.16.0

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.
Files changed (207) hide show
  1. package/dist_serve/bundle.js +5 -9
  2. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  3. package/dist_ts/00_commitinfo_data.js +9 -0
  4. package/dist_ts/cache/classes.cache.cleaner.d.ts +47 -0
  5. package/dist_ts/cache/classes.cache.cleaner.js +130 -0
  6. package/dist_ts/cache/classes.cached.document.d.ts +76 -0
  7. package/dist_ts/cache/classes.cached.document.js +100 -0
  8. package/dist_ts/cache/classes.cachedb.d.ts +60 -0
  9. package/dist_ts/cache/classes.cachedb.js +126 -0
  10. package/dist_ts/cache/documents/classes.cached.email.d.ts +125 -0
  11. package/dist_ts/cache/documents/classes.cached.email.js +337 -0
  12. package/dist_ts/cache/documents/classes.cached.ip.reputation.d.ts +119 -0
  13. package/dist_ts/cache/documents/classes.cached.ip.reputation.js +323 -0
  14. package/dist_ts/cache/documents/index.d.ts +2 -0
  15. package/dist_ts/cache/documents/index.js +3 -0
  16. package/dist_ts/cache/index.d.ts +4 -0
  17. package/dist_ts/cache/index.js +7 -0
  18. package/dist_ts/classes.cert-provision-scheduler.d.ts +54 -0
  19. package/dist_ts/classes.cert-provision-scheduler.js +118 -0
  20. package/dist_ts/classes.dcrouter.d.ts +399 -0
  21. package/dist_ts/classes.dcrouter.js +1697 -0
  22. package/dist_ts/classes.storage-cert-manager.d.ts +18 -0
  23. package/dist_ts/classes.storage-cert-manager.js +43 -0
  24. package/dist_ts/config/classes.api-token-manager.d.ts +46 -0
  25. package/dist_ts/config/classes.api-token-manager.js +150 -0
  26. package/dist_ts/config/classes.route-config-manager.d.ts +38 -0
  27. package/dist_ts/config/classes.route-config-manager.js +257 -0
  28. package/dist_ts/config/index.d.ts +3 -0
  29. package/dist_ts/config/index.js +5 -0
  30. package/dist_ts/config/validator.d.ts +104 -0
  31. package/dist_ts/config/validator.js +152 -0
  32. package/dist_ts/errors/base.errors.d.ts +224 -0
  33. package/dist_ts/errors/base.errors.js +320 -0
  34. package/dist_ts/errors/error-handler.d.ts +98 -0
  35. package/dist_ts/errors/error-handler.js +282 -0
  36. package/dist_ts/errors/error.codes.d.ts +115 -0
  37. package/dist_ts/errors/error.codes.js +136 -0
  38. package/dist_ts/errors/index.d.ts +54 -0
  39. package/dist_ts/errors/index.js +136 -0
  40. package/dist_ts/errors/reputation.errors.d.ts +183 -0
  41. package/dist_ts/errors/reputation.errors.js +292 -0
  42. package/dist_ts/http3/http3-route-augmentation.d.ts +50 -0
  43. package/dist_ts/http3/http3-route-augmentation.js +98 -0
  44. package/dist_ts/http3/index.d.ts +1 -0
  45. package/dist_ts/http3/index.js +2 -0
  46. package/dist_ts/index.d.ts +8 -0
  47. package/dist_ts/index.js +29 -0
  48. package/dist_ts/logger.d.ts +21 -0
  49. package/dist_ts/logger.js +81 -0
  50. package/dist_ts/monitoring/classes.metricscache.d.ts +32 -0
  51. package/dist_ts/monitoring/classes.metricscache.js +63 -0
  52. package/dist_ts/monitoring/classes.metricsmanager.d.ts +184 -0
  53. package/dist_ts/monitoring/classes.metricsmanager.js +744 -0
  54. package/dist_ts/monitoring/index.d.ts +1 -0
  55. package/dist_ts/monitoring/index.js +2 -0
  56. package/dist_ts/opsserver/classes.opsserver.d.ts +38 -0
  57. package/dist_ts/opsserver/classes.opsserver.js +87 -0
  58. package/dist_ts/opsserver/handlers/admin.handler.d.ts +31 -0
  59. package/dist_ts/opsserver/handlers/admin.handler.js +180 -0
  60. package/dist_ts/opsserver/handlers/api-token.handler.d.ts +6 -0
  61. package/dist_ts/opsserver/handlers/api-token.handler.js +62 -0
  62. package/dist_ts/opsserver/handlers/certificate.handler.d.ts +32 -0
  63. package/dist_ts/opsserver/handlers/certificate.handler.js +421 -0
  64. package/dist_ts/opsserver/handlers/config.handler.d.ts +7 -0
  65. package/dist_ts/opsserver/handlers/config.handler.js +192 -0
  66. package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +30 -0
  67. package/dist_ts/opsserver/handlers/email-ops.handler.js +227 -0
  68. package/dist_ts/opsserver/handlers/index.d.ts +12 -0
  69. package/dist_ts/opsserver/handlers/index.js +13 -0
  70. package/dist_ts/opsserver/handlers/logs.handler.d.ts +25 -0
  71. package/dist_ts/opsserver/handlers/logs.handler.js +256 -0
  72. package/dist_ts/opsserver/handlers/radius.handler.d.ts +6 -0
  73. package/dist_ts/opsserver/handlers/radius.handler.js +295 -0
  74. package/dist_ts/opsserver/handlers/remoteingress.handler.d.ts +6 -0
  75. package/dist_ts/opsserver/handlers/remoteingress.handler.js +156 -0
  76. package/dist_ts/opsserver/handlers/route-management.handler.d.ts +14 -0
  77. package/dist_ts/opsserver/handlers/route-management.handler.js +117 -0
  78. package/dist_ts/opsserver/handlers/security.handler.d.ts +9 -0
  79. package/dist_ts/opsserver/handlers/security.handler.js +233 -0
  80. package/dist_ts/opsserver/handlers/stats.handler.d.ts +11 -0
  81. package/dist_ts/opsserver/handlers/stats.handler.js +403 -0
  82. package/dist_ts/opsserver/handlers/vpn.handler.d.ts +6 -0
  83. package/dist_ts/opsserver/handlers/vpn.handler.js +197 -0
  84. package/dist_ts/opsserver/helpers/guards.d.ts +27 -0
  85. package/dist_ts/opsserver/helpers/guards.js +43 -0
  86. package/dist_ts/opsserver/index.d.ts +1 -0
  87. package/dist_ts/opsserver/index.js +2 -0
  88. package/dist_ts/paths.d.ts +26 -0
  89. package/dist_ts/paths.js +45 -0
  90. package/dist_ts/plugins.d.ts +81 -0
  91. package/dist_ts/plugins.js +115 -0
  92. package/dist_ts/radius/classes.accounting.manager.d.ts +231 -0
  93. package/dist_ts/radius/classes.accounting.manager.js +462 -0
  94. package/dist_ts/radius/classes.radius.server.d.ts +171 -0
  95. package/dist_ts/radius/classes.radius.server.js +386 -0
  96. package/dist_ts/radius/classes.vlan.manager.d.ts +128 -0
  97. package/dist_ts/radius/classes.vlan.manager.js +279 -0
  98. package/dist_ts/radius/index.d.ts +13 -0
  99. package/dist_ts/radius/index.js +14 -0
  100. package/dist_ts/remoteingress/classes.remoteingress-manager.d.ts +94 -0
  101. package/dist_ts/remoteingress/classes.remoteingress-manager.js +271 -0
  102. package/dist_ts/remoteingress/classes.tunnel-manager.d.ts +59 -0
  103. package/dist_ts/remoteingress/classes.tunnel-manager.js +165 -0
  104. package/dist_ts/remoteingress/index.d.ts +2 -0
  105. package/dist_ts/remoteingress/index.js +3 -0
  106. package/dist_ts/security/classes.contentscanner.d.ts +164 -0
  107. package/dist_ts/security/classes.contentscanner.js +642 -0
  108. package/dist_ts/security/classes.ipreputationchecker.d.ts +160 -0
  109. package/dist_ts/security/classes.ipreputationchecker.js +537 -0
  110. package/dist_ts/security/classes.securitylogger.d.ts +144 -0
  111. package/dist_ts/security/classes.securitylogger.js +235 -0
  112. package/dist_ts/security/index.d.ts +3 -0
  113. package/dist_ts/security/index.js +4 -0
  114. package/dist_ts/sms/classes.smsservice.d.ts +15 -0
  115. package/dist_ts/sms/classes.smsservice.js +72 -0
  116. package/dist_ts/sms/config/sms.config.d.ts +93 -0
  117. package/dist_ts/sms/config/sms.config.js +2 -0
  118. package/dist_ts/sms/config/sms.schema.d.ts +5 -0
  119. package/dist_ts/sms/config/sms.schema.js +121 -0
  120. package/dist_ts/sms/index.d.ts +1 -0
  121. package/dist_ts/sms/index.js +2 -0
  122. package/dist_ts/storage/classes.storagemanager.d.ts +83 -0
  123. package/dist_ts/storage/classes.storagemanager.js +348 -0
  124. package/dist_ts/storage/index.d.ts +1 -0
  125. package/dist_ts/storage/index.js +3 -0
  126. package/dist_ts/vpn/classes.vpn-manager.d.ts +129 -0
  127. package/dist_ts/vpn/classes.vpn-manager.js +329 -0
  128. package/dist_ts/vpn/index.d.ts +1 -0
  129. package/dist_ts/vpn/index.js +2 -0
  130. package/dist_ts_apiclient/classes.apitoken.d.ts +41 -0
  131. package/dist_ts_apiclient/classes.apitoken.js +115 -0
  132. package/dist_ts_apiclient/classes.certificate.d.ts +57 -0
  133. package/dist_ts_apiclient/classes.certificate.js +69 -0
  134. package/dist_ts_apiclient/classes.config.d.ts +7 -0
  135. package/dist_ts_apiclient/classes.config.js +11 -0
  136. package/dist_ts_apiclient/classes.dcrouterapiclient.d.ts +41 -0
  137. package/dist_ts_apiclient/classes.dcrouterapiclient.js +81 -0
  138. package/dist_ts_apiclient/classes.email.d.ts +30 -0
  139. package/dist_ts_apiclient/classes.email.js +52 -0
  140. package/dist_ts_apiclient/classes.logs.d.ts +21 -0
  141. package/dist_ts_apiclient/classes.logs.js +14 -0
  142. package/dist_ts_apiclient/classes.radius.d.ts +59 -0
  143. package/dist_ts_apiclient/classes.radius.js +95 -0
  144. package/dist_ts_apiclient/classes.remoteingress.d.ts +54 -0
  145. package/dist_ts_apiclient/classes.remoteingress.js +136 -0
  146. package/dist_ts_apiclient/classes.route.d.ts +42 -0
  147. package/dist_ts_apiclient/classes.route.js +154 -0
  148. package/dist_ts_apiclient/classes.stats.d.ts +47 -0
  149. package/dist_ts_apiclient/classes.stats.js +38 -0
  150. package/dist_ts_apiclient/index.d.ts +10 -0
  151. package/dist_ts_apiclient/index.js +14 -0
  152. package/dist_ts_apiclient/plugins.d.ts +3 -0
  153. package/dist_ts_apiclient/plugins.js +5 -0
  154. package/dist_ts_interfaces/data/remoteingress.d.ts +2 -0
  155. package/dist_ts_interfaces/data/vpn.d.ts +1 -2
  156. package/dist_ts_interfaces/requests/vpn.d.ts +1 -1
  157. package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
  158. package/dist_ts_web/00_commitinfo_data.js +9 -0
  159. package/dist_ts_web/appstate.d.ts +238 -0
  160. package/dist_ts_web/appstate.js +1174 -0
  161. package/dist_ts_web/elements/index.d.ts +13 -0
  162. package/dist_ts_web/elements/index.js +14 -0
  163. package/dist_ts_web/elements/ops-dashboard.d.ts +23 -0
  164. package/dist_ts_web/elements/ops-dashboard.js +323 -0
  165. package/dist_ts_web/elements/ops-view-apitokens.d.ts +13 -0
  166. package/dist_ts_web/elements/ops-view-apitokens.js +371 -0
  167. package/dist_ts_web/elements/ops-view-certificates.d.ts +22 -0
  168. package/dist_ts_web/elements/ops-view-certificates.js +528 -0
  169. package/dist_ts_web/elements/ops-view-config.d.ts +19 -0
  170. package/dist_ts_web/elements/ops-view-config.js +339 -0
  171. package/dist_ts_web/elements/ops-view-emails.d.ts +21 -0
  172. package/dist_ts_web/elements/ops-view-emails.js +165 -0
  173. package/dist_ts_web/elements/ops-view-logs.d.ts +13 -0
  174. package/dist_ts_web/elements/ops-view-logs.js +159 -0
  175. package/dist_ts_web/elements/ops-view-network.d.ts +71 -0
  176. package/dist_ts_web/elements/ops-view-network.js +764 -0
  177. package/dist_ts_web/elements/ops-view-overview.d.ts +22 -0
  178. package/dist_ts_web/elements/ops-view-overview.js +456 -0
  179. package/dist_ts_web/elements/ops-view-remoteingress.d.ts +20 -0
  180. package/dist_ts_web/elements/ops-view-remoteingress.js +494 -0
  181. package/dist_ts_web/elements/ops-view-routes.d.ts +12 -0
  182. package/dist_ts_web/elements/ops-view-routes.js +404 -0
  183. package/dist_ts_web/elements/ops-view-security.d.ts +21 -0
  184. package/dist_ts_web/elements/ops-view-security.js +574 -0
  185. package/dist_ts_web/elements/ops-view-vpn.d.ts +14 -0
  186. package/dist_ts_web/elements/ops-view-vpn.js +365 -0
  187. package/dist_ts_web/elements/shared/css.d.ts +1 -0
  188. package/dist_ts_web/elements/shared/css.js +10 -0
  189. package/dist_ts_web/elements/shared/index.d.ts +2 -0
  190. package/dist_ts_web/elements/shared/index.js +3 -0
  191. package/dist_ts_web/elements/shared/ops-sectionheading.d.ts +5 -0
  192. package/dist_ts_web/elements/shared/ops-sectionheading.js +82 -0
  193. package/dist_ts_web/index.d.ts +1 -0
  194. package/dist_ts_web/index.js +10 -0
  195. package/dist_ts_web/plugins.d.ts +6 -0
  196. package/dist_ts_web/plugins.js +11 -0
  197. package/dist_ts_web/router.d.ts +19 -0
  198. package/dist_ts_web/router.js +91 -0
  199. package/package.json +2 -2
  200. package/ts/00_commitinfo_data.ts +1 -1
  201. package/ts/classes.dcrouter.ts +51 -20
  202. package/ts/config/classes.route-config-manager.ts +7 -6
  203. package/ts/opsserver/handlers/vpn.handler.ts +3 -5
  204. package/ts/vpn/classes.vpn-manager.ts +68 -19
  205. package/ts_web/00_commitinfo_data.ts +1 -1
  206. package/ts_web/appstate.ts +2 -2
  207. package/ts_web/elements/ops-view-vpn.ts +5 -9
@@ -0,0 +1,1697 @@
1
+ import * as plugins from './plugins.js';
2
+ import * as paths from './paths.js';
3
+ // Certificate types are available via plugins.tsclass
4
+ // Import the email server and its configuration from smartmta
5
+ import { UnifiedEmailServer, } from '@push.rocks/smartmta';
6
+ import { logger } from './logger.js';
7
+ // Import storage manager
8
+ import { StorageManager } from './storage/index.js';
9
+ import { StorageBackedCertManager } from './classes.storage-cert-manager.js';
10
+ import { CertProvisionScheduler } from './classes.cert-provision-scheduler.js';
11
+ // Import cache system
12
+ import { CacheDb, CacheCleaner } from './cache/index.js';
13
+ import { OpsServer } from './opsserver/index.js';
14
+ import { MetricsManager } from './monitoring/index.js';
15
+ import { RadiusServer } from './radius/index.js';
16
+ import { RemoteIngressManager, TunnelManager } from './remoteingress/index.js';
17
+ import { VpnManager } from './vpn/index.js';
18
+ import { RouteConfigManager, ApiTokenManager } from './config/index.js';
19
+ import { SecurityLogger, ContentScanner, IPReputationChecker } from './security/index.js';
20
+ import { augmentRoutesWithHttp3 } from './http3/index.js';
21
+ export class DcRouter {
22
+ options;
23
+ resolvedPaths;
24
+ // Core services
25
+ smartProxy;
26
+ smartAcme;
27
+ dnsServer;
28
+ emailServer;
29
+ radiusServer;
30
+ storageManager;
31
+ opsServer;
32
+ metricsManager;
33
+ // Cache system (smartdata + LocalTsmDb)
34
+ cacheDb;
35
+ cacheCleaner;
36
+ // Remote Ingress
37
+ remoteIngressManager;
38
+ tunnelManager;
39
+ // VPN
40
+ vpnManager;
41
+ // Programmatic config API
42
+ routeConfigManager;
43
+ apiTokenManager;
44
+ // Auto-discovered public IP (populated by generateAuthoritativeRecords)
45
+ detectedPublicIp = null;
46
+ // DNS query logging rate limiter state
47
+ dnsLogWindowSecond = 0; // epoch second of current window
48
+ dnsLogWindowCount = 0; // queries logged this second
49
+ dnsBatchCount = 0;
50
+ dnsBatchTimer = null;
51
+ // Certificate status tracking from SmartProxy events (keyed by domain)
52
+ certificateStatusMap = new Map();
53
+ // Certificate provisioning scheduler with per-domain backoff
54
+ certProvisionScheduler;
55
+ // Service lifecycle management
56
+ serviceManager;
57
+ serviceSubjectSubscription;
58
+ smartAcmeReady = false;
59
+ // TypedRouter for API endpoints
60
+ typedrouter = new plugins.typedrequest.TypedRouter();
61
+ // Cached constructor routes (computed once during setupSmartProxy, used by RouteConfigManager)
62
+ constructorRoutes = [];
63
+ // Environment access
64
+ qenv = new plugins.qenv.Qenv('./', '.nogit/');
65
+ constructor(optionsArg) {
66
+ // Set defaults in options
67
+ this.options = {
68
+ ...optionsArg
69
+ };
70
+ // Resolve all data paths from baseDir
71
+ this.resolvedPaths = paths.resolvePaths(this.options.baseDir);
72
+ // Default storage to filesystem if not configured
73
+ if (!this.options.storage) {
74
+ this.options.storage = {
75
+ fsPath: this.resolvedPaths.defaultStoragePath,
76
+ };
77
+ }
78
+ // Initialize storage manager
79
+ this.storageManager = new StorageManager(this.options.storage);
80
+ // Initialize service manager and register all services
81
+ this.serviceManager = new plugins.taskbuffer.ServiceManager({
82
+ name: 'dcrouter',
83
+ startupTimeoutMs: 120_000,
84
+ shutdownTimeoutMs: 30_000,
85
+ });
86
+ this.registerServices();
87
+ }
88
+ /**
89
+ * Register all dcrouter services with the ServiceManager.
90
+ * Services are started in dependency order, with failure isolation for optional services.
91
+ */
92
+ registerServices() {
93
+ // OpsServer: critical, no dependencies — provides visibility
94
+ this.serviceManager.addService(new plugins.taskbuffer.Service('OpsServer')
95
+ .critical()
96
+ .withStart(async () => {
97
+ this.opsServer = new OpsServer(this);
98
+ await this.opsServer.start();
99
+ })
100
+ .withStop(async () => {
101
+ await this.opsServer?.stop();
102
+ })
103
+ .withRetry({ maxRetries: 0 }));
104
+ // CacheDb: optional, no dependencies
105
+ if (this.options.cacheConfig?.enabled !== false) {
106
+ this.serviceManager.addService(new plugins.taskbuffer.Service('CacheDb')
107
+ .optional()
108
+ .withStart(async () => {
109
+ await this.setupCacheDb();
110
+ })
111
+ .withStop(async () => {
112
+ if (this.cacheCleaner) {
113
+ this.cacheCleaner.stop();
114
+ this.cacheCleaner = undefined;
115
+ }
116
+ if (this.cacheDb) {
117
+ await this.cacheDb.stop();
118
+ CacheDb.resetInstance();
119
+ this.cacheDb = undefined;
120
+ }
121
+ })
122
+ .withRetry({ maxRetries: 2, baseDelayMs: 1000, maxDelayMs: 5000 }));
123
+ }
124
+ // MetricsManager: optional, depends on OpsServer
125
+ this.serviceManager.addService(new plugins.taskbuffer.Service('MetricsManager')
126
+ .optional()
127
+ .dependsOn('OpsServer')
128
+ .withStart(async () => {
129
+ this.metricsManager = new MetricsManager(this);
130
+ await this.metricsManager.start();
131
+ })
132
+ .withStop(async () => {
133
+ if (this.metricsManager) {
134
+ await this.metricsManager.stop();
135
+ this.metricsManager = undefined;
136
+ }
137
+ })
138
+ .withRetry({ maxRetries: 1, baseDelayMs: 1000 }));
139
+ // SmartProxy: critical, depends on CacheDb (if enabled)
140
+ const smartProxyDeps = [];
141
+ if (this.options.cacheConfig?.enabled !== false) {
142
+ smartProxyDeps.push('CacheDb');
143
+ }
144
+ this.serviceManager.addService(new plugins.taskbuffer.Service('SmartProxy')
145
+ .critical()
146
+ .dependsOn(...smartProxyDeps)
147
+ .withStart(async () => {
148
+ await this.setupSmartProxy();
149
+ })
150
+ .withStop(async () => {
151
+ if (this.smartProxy) {
152
+ this.smartProxy.removeAllListeners();
153
+ await this.smartProxy.stop();
154
+ this.smartProxy = undefined;
155
+ }
156
+ })
157
+ .withRetry({ maxRetries: 0 }));
158
+ // SmartAcme: optional, depends on SmartProxy — aggressive retry for rate limits
159
+ // Only registered if DNS challenge is configured
160
+ if (this.options.dnsChallenge?.cloudflareApiKey) {
161
+ this.serviceManager.addService(new plugins.taskbuffer.Service('SmartAcme')
162
+ .optional()
163
+ .dependsOn('SmartProxy')
164
+ .withStart(async () => {
165
+ if (this.smartAcme) {
166
+ await this.smartAcme.start();
167
+ this.smartAcmeReady = true;
168
+ logger.log('info', 'SmartAcme DNS-01 provider is now ready');
169
+ // Re-trigger certificate provisioning for all auto-cert routes.
170
+ // During startup, certProvisionFunction returned 'http01' (SmartAcme not ready),
171
+ // but Rust ACME is disabled when certProvisionFunction is set — so all domains
172
+ // failed silently (SmartProxy doesn't emit certificate-failed for this path).
173
+ // Calling updateRoutes() re-triggers provisionCertificatesViaCallback internally,
174
+ // which calls certProvisionFunction again — now with smartAcmeReady === true.
175
+ if (this.smartProxy) {
176
+ if (this.certProvisionScheduler) {
177
+ this.certProvisionScheduler.clear();
178
+ }
179
+ const currentRoutes = this.smartProxy.routeManager.getRoutes();
180
+ logger.log('info', `Re-triggering certificate provisioning for ${currentRoutes.length} routes`);
181
+ this.smartProxy.updateRoutes(currentRoutes).catch((err) => {
182
+ logger.log('warn', `Failed to re-trigger cert provisioning: ${err?.message || err}`);
183
+ });
184
+ }
185
+ }
186
+ })
187
+ .withStop(async () => {
188
+ this.smartAcmeReady = false;
189
+ if (this.smartAcme) {
190
+ await this.smartAcme.stop();
191
+ this.smartAcme = undefined;
192
+ }
193
+ })
194
+ .withRetry({ maxRetries: 20, baseDelayMs: 5000, maxDelayMs: 3_600_000, backoffFactor: 2 }));
195
+ }
196
+ // ConfigManagers: optional, depends on SmartProxy
197
+ this.serviceManager.addService(new plugins.taskbuffer.Service('ConfigManagers')
198
+ .optional()
199
+ .dependsOn('SmartProxy')
200
+ .withStart(async () => {
201
+ this.routeConfigManager = new RouteConfigManager(this.storageManager, () => this.getConstructorRoutes(), () => this.smartProxy, () => this.options.http3, this.options.vpnConfig?.enabled
202
+ ? (tags) => {
203
+ if (tags?.length && this.vpnManager) {
204
+ return this.vpnManager.getClientIpsForServerDefinedTags(tags);
205
+ }
206
+ return [this.options.vpnConfig?.subnet || '10.8.0.0/24'];
207
+ }
208
+ : undefined);
209
+ this.apiTokenManager = new ApiTokenManager(this.storageManager);
210
+ await this.apiTokenManager.initialize();
211
+ await this.routeConfigManager.initialize();
212
+ })
213
+ .withStop(async () => {
214
+ this.routeConfigManager = undefined;
215
+ this.apiTokenManager = undefined;
216
+ })
217
+ .withRetry({ maxRetries: 2, baseDelayMs: 1000 }));
218
+ // Email Server: optional, depends on SmartProxy
219
+ if (this.options.emailConfig) {
220
+ this.serviceManager.addService(new plugins.taskbuffer.Service('EmailServer')
221
+ .optional()
222
+ .dependsOn('SmartProxy')
223
+ .withStart(async () => {
224
+ await this.setupUnifiedEmailHandling();
225
+ })
226
+ .withStop(async () => {
227
+ if (this.emailServer) {
228
+ if (this.emailServer.deliverySystem) {
229
+ this.emailServer.deliverySystem.removeAllListeners();
230
+ }
231
+ this.emailServer.removeAllListeners();
232
+ await this.emailServer.stop();
233
+ this.emailServer = undefined;
234
+ }
235
+ })
236
+ .withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }));
237
+ }
238
+ // DNS Server: optional, depends on SmartProxy
239
+ if (this.options.dnsNsDomains && this.options.dnsNsDomains.length > 0 && this.options.dnsScopes && this.options.dnsScopes.length > 0) {
240
+ this.serviceManager.addService(new plugins.taskbuffer.Service('DnsServer')
241
+ .optional()
242
+ .dependsOn('SmartProxy')
243
+ .withStart(async () => {
244
+ await this.setupDnsWithSocketHandler();
245
+ })
246
+ .withStop(async () => {
247
+ // Flush pending DNS batch log
248
+ if (this.dnsBatchTimer) {
249
+ clearTimeout(this.dnsBatchTimer);
250
+ if (this.dnsBatchCount > 0) {
251
+ logger.log('info', `DNS: ${this.dnsBatchCount} queries processed (final flush)`, { zone: 'dns' });
252
+ }
253
+ this.dnsBatchTimer = null;
254
+ this.dnsBatchCount = 0;
255
+ this.dnsLogWindowSecond = 0;
256
+ this.dnsLogWindowCount = 0;
257
+ }
258
+ if (this.dnsServer) {
259
+ this.dnsServer.removeAllListeners();
260
+ await this.dnsServer.stop();
261
+ this.dnsServer = undefined;
262
+ }
263
+ })
264
+ .withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }));
265
+ }
266
+ // RADIUS Server: optional, no dependency on SmartProxy
267
+ if (this.options.radiusConfig) {
268
+ this.serviceManager.addService(new plugins.taskbuffer.Service('RadiusServer')
269
+ .optional()
270
+ .withStart(async () => {
271
+ await this.setupRadiusServer();
272
+ })
273
+ .withStop(async () => {
274
+ if (this.radiusServer) {
275
+ await this.radiusServer.stop();
276
+ this.radiusServer = undefined;
277
+ }
278
+ })
279
+ .withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }));
280
+ }
281
+ // Remote Ingress: optional, depends on SmartProxy
282
+ if (this.options.remoteIngressConfig?.enabled) {
283
+ this.serviceManager.addService(new plugins.taskbuffer.Service('RemoteIngress')
284
+ .optional()
285
+ .dependsOn('SmartProxy')
286
+ .withStart(async () => {
287
+ await this.setupRemoteIngress();
288
+ })
289
+ .withStop(async () => {
290
+ if (this.tunnelManager) {
291
+ await this.tunnelManager.stop();
292
+ this.tunnelManager = undefined;
293
+ }
294
+ this.remoteIngressManager = undefined;
295
+ })
296
+ .withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }));
297
+ }
298
+ // VPN Server: optional, depends on SmartProxy
299
+ if (this.options.vpnConfig?.enabled) {
300
+ this.serviceManager.addService(new plugins.taskbuffer.Service('VpnServer')
301
+ .optional()
302
+ .dependsOn('SmartProxy')
303
+ .withStart(async () => {
304
+ await this.setupVpnServer();
305
+ })
306
+ .withStop(async () => {
307
+ if (this.vpnManager) {
308
+ await this.vpnManager.stop();
309
+ this.vpnManager = undefined;
310
+ }
311
+ })
312
+ .withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }));
313
+ }
314
+ // Wire up aggregated events for logging
315
+ this.serviceSubjectSubscription = this.serviceManager.serviceSubject.subscribe((event) => {
316
+ const level = event.type === 'failed' ? 'error' : event.type === 'retrying' ? 'warn' : 'info';
317
+ logger.log(level, `Service '${event.serviceName}': ${event.type}`, {
318
+ state: event.state,
319
+ ...(event.error ? { error: event.error } : {}),
320
+ ...(event.attempt ? { attempt: event.attempt } : {}),
321
+ });
322
+ });
323
+ }
324
+ async start() {
325
+ await this.checkSystemLimits();
326
+ logger.log('info', 'Starting DcRouter Services');
327
+ await this.serviceManager.start();
328
+ this.logStartupSummary();
329
+ }
330
+ /**
331
+ * Detect OS-level resource limits and warn if they are too low for production use.
332
+ * This is detection only — no attempts to raise limits.
333
+ */
334
+ async checkSystemLimits() {
335
+ try {
336
+ const fs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
337
+ const limitsContent = await fs.file('/proc/self/limits').encoding('utf8').read();
338
+ const nofileLine = limitsContent.split('\n').find((line) => line.startsWith('Max open files'));
339
+ if (nofileLine) {
340
+ const parts = nofileLine.split(/\s{2,}/);
341
+ const softLimit = parseInt(parts[1], 10);
342
+ const hardLimit = parseInt(parts[2], 10);
343
+ if (softLimit < 65536) {
344
+ logger.log('warn', `File descriptor soft limit is ${softLimit} (hard: ${hardLimit}). ` +
345
+ `For production use, set --ulimit nofile=65536:65536 on the container runtime.`);
346
+ }
347
+ else {
348
+ logger.log('info', `File descriptor limits: soft=${softLimit}, hard=${hardLimit}`);
349
+ }
350
+ }
351
+ }
352
+ catch {
353
+ // Non-Linux or /proc not available — silently skip
354
+ }
355
+ }
356
+ /**
357
+ * Log comprehensive startup summary
358
+ */
359
+ logStartupSummary() {
360
+ logger.log('info', 'DcRouter Started Successfully');
361
+ // Metrics summary
362
+ if (this.metricsManager) {
363
+ logger.log('info', 'Metrics Service: SmartMetrics active, SmartProxy stats active, real-time tracking enabled');
364
+ }
365
+ // SmartProxy summary
366
+ if (this.smartProxy) {
367
+ const routeCount = this.options.smartProxyConfig?.routes?.length || 0;
368
+ const acmeEnabled = this.options.smartProxyConfig?.acme?.enabled || false;
369
+ const acmeMode = acmeEnabled
370
+ ? `email=${this.options.smartProxyConfig.acme.email || 'not set'}, mode=${this.options.smartProxyConfig.acme.useProduction ? 'production' : 'staging'}`
371
+ : 'disabled';
372
+ logger.log('info', `SmartProxy Service: ${routeCount} routes, ACME: ${acmeMode}`);
373
+ }
374
+ // Email service summary
375
+ if (this.emailServer && this.options.emailConfig) {
376
+ const ports = this.options.emailConfig.ports || [];
377
+ const domainCount = this.options.emailConfig.domains?.length || 0;
378
+ const domainNames = this.options.emailConfig.domains?.map(d => `${d.domain} (${d.dnsMode || 'default'})`).join(', ') || 'none';
379
+ logger.log('info', `Email Service: ports=[${ports.join(', ')}], hostname=${this.options.emailConfig.hostname || 'localhost'}, domains=${domainCount} [${domainNames}], DKIM initialized`);
380
+ }
381
+ // DNS service summary
382
+ if (this.dnsServer && this.options.dnsNsDomains && this.options.dnsScopes) {
383
+ logger.log('info', `DNS Service: nameservers=[${this.options.dnsNsDomains.join(', ')}], authoritative for ${this.options.dnsScopes.length} domains [${this.options.dnsScopes.join(', ')}], UDP:53, DoH enabled`);
384
+ }
385
+ // RADIUS service summary
386
+ if (this.radiusServer && this.options.radiusConfig) {
387
+ const vlanStats = this.radiusServer.getVlanManager().getStats();
388
+ logger.log('info', `RADIUS Service: auth=${this.options.radiusConfig.authPort || 1812}, acct=${this.options.radiusConfig.acctPort || 1813}, clients=${this.options.radiusConfig.clients?.length || 0}, VLANs=${vlanStats.totalMappings}, accounting=${this.options.radiusConfig.accounting?.enabled ? 'enabled' : 'disabled'}`);
389
+ }
390
+ // VPN summary
391
+ if (this.vpnManager && this.options.vpnConfig?.enabled) {
392
+ const subnet = this.vpnManager.getSubnet();
393
+ const wgPort = this.options.vpnConfig.wgListenPort ?? 51820;
394
+ const clientCount = this.vpnManager.listClients().length;
395
+ logger.log('info', `VPN Service: subnet=${subnet}, wg=:${wgPort}, clients=${clientCount}`);
396
+ }
397
+ // Remote Ingress summary
398
+ if (this.tunnelManager && this.options.remoteIngressConfig?.enabled) {
399
+ const edgeCount = this.remoteIngressManager?.getAllEdges().length || 0;
400
+ const connectedCount = this.tunnelManager.getConnectedCount();
401
+ logger.log('info', `Remote Ingress: tunnel port=${this.options.remoteIngressConfig.tunnelPort || 8443}, edges=${edgeCount} registered/${connectedCount} connected`);
402
+ }
403
+ // Storage summary
404
+ if (this.storageManager && this.options.storage) {
405
+ logger.log('info', `Storage: path=${this.options.storage.fsPath || 'default'}`);
406
+ }
407
+ // Cache database summary
408
+ if (this.cacheDb) {
409
+ logger.log('info', `Cache Database: storage=${this.cacheDb.getStoragePath()}, db=${this.cacheDb.getDbName()}, cleaner=${this.cacheCleaner?.isActive() ? 'active' : 'inactive'} (${(this.options.cacheConfig?.cleanupIntervalHours || 1)}h interval)`);
410
+ }
411
+ // Service status summary from ServiceManager
412
+ const health = this.serviceManager.getHealth();
413
+ const statuses = health.services;
414
+ const running = statuses.filter(s => s.state === 'running').length;
415
+ const failed = statuses.filter(s => s.state === 'failed').length;
416
+ const retrying = statuses.filter(s => s.state === 'starting' || s.state === 'degraded').length;
417
+ if (failed > 0) {
418
+ const failedNames = statuses.filter(s => s.state === 'failed').map(s => `${s.name}: ${s.lastError || 'unknown'}`);
419
+ logger.log('warn', `DcRouter started in degraded mode — ${running} running, ${failed} failed: ${failedNames.join('; ')}`);
420
+ }
421
+ else if (retrying > 0) {
422
+ logger.log('info', `DcRouter started — ${running} running, ${retrying} still initializing`);
423
+ }
424
+ else {
425
+ logger.log('info', `All ${running} services are running`);
426
+ }
427
+ }
428
+ /**
429
+ * Set up the cache database (smartdata + LocalTsmDb)
430
+ */
431
+ async setupCacheDb() {
432
+ logger.log('info', 'Setting up CacheDb...');
433
+ const cacheConfig = this.options.cacheConfig || {};
434
+ // Initialize CacheDb singleton
435
+ this.cacheDb = CacheDb.getInstance({
436
+ storagePath: cacheConfig.storagePath || this.resolvedPaths.defaultTsmDbPath,
437
+ dbName: cacheConfig.dbName || 'dcrouter',
438
+ debug: false,
439
+ });
440
+ await this.cacheDb.start();
441
+ // Start the cache cleaner
442
+ const cleanupIntervalMs = (cacheConfig.cleanupIntervalHours || 1) * 60 * 60 * 1000;
443
+ this.cacheCleaner = new CacheCleaner(this.cacheDb, {
444
+ intervalMs: cleanupIntervalMs,
445
+ verbose: false,
446
+ });
447
+ this.cacheCleaner.start();
448
+ logger.log('info', `CacheDb initialized at ${this.cacheDb.getStoragePath()}`);
449
+ }
450
+ /**
451
+ * Set up SmartProxy with direct configuration and automatic email routes
452
+ */
453
+ async setupSmartProxy() {
454
+ logger.log('info', 'Setting up SmartProxy...');
455
+ // Clean up any existing SmartProxy instance (e.g. from a retry)
456
+ if (this.smartProxy) {
457
+ this.smartProxy.removeAllListeners();
458
+ this.smartProxy = undefined;
459
+ }
460
+ let routes = [];
461
+ let acmeConfig;
462
+ // If user provides full SmartProxy config, use it directly
463
+ if (this.options.smartProxyConfig) {
464
+ routes = this.options.smartProxyConfig.routes || [];
465
+ acmeConfig = this.options.smartProxyConfig.acme;
466
+ logger.log('info', `Found ${routes.length} routes in config, ACME config present: ${!!acmeConfig}`);
467
+ }
468
+ // If email config exists, automatically add email routes
469
+ if (this.options.emailConfig) {
470
+ const emailRoutes = this.generateEmailRoutes(this.options.emailConfig);
471
+ logger.log('debug', 'Email routes generated', { routes: JSON.stringify(emailRoutes) });
472
+ routes = [...routes, ...emailRoutes]; // Enable email routing through SmartProxy
473
+ }
474
+ // If DNS is configured, add DNS routes
475
+ if (this.options.dnsNsDomains && this.options.dnsNsDomains.length > 0) {
476
+ const dnsRoutes = this.generateDnsRoutes();
477
+ logger.log('debug', `DNS routes for nameservers ${this.options.dnsNsDomains.join(', ')}`, { routes: JSON.stringify(dnsRoutes) });
478
+ routes = [...routes, ...dnsRoutes];
479
+ }
480
+ // Merge TLS/ACME configuration if provided at root level
481
+ if (this.options.tls && !acmeConfig) {
482
+ acmeConfig = {
483
+ accountEmail: this.options.tls.contactEmail,
484
+ enabled: true,
485
+ useProduction: true,
486
+ autoRenew: true,
487
+ renewThresholdDays: 30
488
+ };
489
+ }
490
+ // Configure DNS challenge if available
491
+ let challengeHandlers = [];
492
+ if (this.options.dnsChallenge?.cloudflareApiKey) {
493
+ logger.log('info', 'Configuring Cloudflare DNS challenge for ACME');
494
+ const cloudflareAccount = new plugins.cloudflare.CloudflareAccount(this.options.dnsChallenge.cloudflareApiKey);
495
+ const dns01Handler = new plugins.smartacme.handlers.Dns01Handler(cloudflareAccount);
496
+ challengeHandlers.push(dns01Handler);
497
+ }
498
+ // HTTP/3 augmentation (enabled by default unless explicitly disabled)
499
+ if (this.options.http3?.enabled !== false) {
500
+ const http3Config = { enabled: true, ...this.options.http3 };
501
+ routes = augmentRoutesWithHttp3(routes, http3Config);
502
+ logger.log('info', 'HTTP/3: Augmented qualifying HTTPS routes with QUIC/H3 configuration');
503
+ }
504
+ // VPN route security injection: restrict vpn.required routes to VPN subnet
505
+ if (this.options.vpnConfig?.enabled) {
506
+ routes = this.injectVpnSecurity(routes);
507
+ }
508
+ // Cache constructor routes for RouteConfigManager
509
+ this.constructorRoutes = [...routes];
510
+ // If we have routes or need a basic SmartProxy instance, create it
511
+ if (routes.length > 0 || this.options.smartProxyConfig) {
512
+ logger.log('info', 'Setting up SmartProxy with combined configuration');
513
+ // Track cert entries loaded from cert store so we can populate certificateStatusMap after start
514
+ const loadedCertEntries = [];
515
+ // Create SmartProxy configuration with sensible gateway defaults.
516
+ // User's smartProxyConfig overrides these defaults via spread.
517
+ const smartProxyConfig = {
518
+ // --- dcrouter gateway defaults ---
519
+ maxConnectionsPerIP: 100,
520
+ connectionRateLimitPerMinute: 600,
521
+ socketTimeout: 120_000,
522
+ inactivityTimeout: 120_000,
523
+ keepAlive: true,
524
+ noDelay: true,
525
+ gracefulShutdownTimeout: 30_000,
526
+ // --- user overrides ---
527
+ ...this.options.smartProxyConfig,
528
+ // --- deep-merge defaults.security so user can override maxConnections ---
529
+ defaults: {
530
+ ...this.options.smartProxyConfig?.defaults,
531
+ security: {
532
+ maxConnections: 50_000,
533
+ ...this.options.smartProxyConfig?.defaults?.security,
534
+ },
535
+ },
536
+ // --- always set by dcrouter (after spread) ---
537
+ routes,
538
+ acme: acmeConfig,
539
+ certStore: {
540
+ loadAll: async () => {
541
+ const keys = await this.storageManager.list('/proxy-certs/');
542
+ const certs = [];
543
+ for (const key of keys) {
544
+ const data = await this.storageManager.getJSON(key);
545
+ if (data) {
546
+ certs.push(data);
547
+ loadedCertEntries.push({ domain: data.domain, publicKey: data.publicKey, validUntil: data.validUntil, validFrom: data.validFrom });
548
+ }
549
+ }
550
+ return certs;
551
+ },
552
+ save: async (domain, publicKey, privateKey, ca) => {
553
+ let validUntil;
554
+ let validFrom;
555
+ try {
556
+ const x509 = new plugins.crypto.X509Certificate(publicKey);
557
+ validUntil = new Date(x509.validTo).getTime();
558
+ validFrom = new Date(x509.validFrom).getTime();
559
+ }
560
+ catch { /* PEM parsing failed */ }
561
+ await this.storageManager.setJSON(`/proxy-certs/${domain}`, {
562
+ domain, publicKey, privateKey, ca, validUntil, validFrom,
563
+ });
564
+ },
565
+ remove: async (domain) => {
566
+ await this.storageManager.delete(`/proxy-certs/${domain}`);
567
+ },
568
+ },
569
+ };
570
+ // Initialize cert provision scheduler
571
+ this.certProvisionScheduler = new CertProvisionScheduler(this.storageManager);
572
+ // If we have DNS challenge handlers, create SmartAcme instance and wire certProvisionFunction
573
+ // Note: SmartAcme.start() is NOT called here — it runs as a separate optional service
574
+ // via the ServiceManager, with aggressive retry for rate-limit resilience.
575
+ if (challengeHandlers.length > 0) {
576
+ // Stop old SmartAcme if it exists (e.g., during updateSmartProxyConfig)
577
+ if (this.smartAcme) {
578
+ this.smartAcmeReady = false;
579
+ await this.smartAcme.stop().catch(err => logger.log('error', 'Error stopping old SmartAcme', { error: String(err) }));
580
+ }
581
+ this.smartAcme = new plugins.smartacme.SmartAcme({
582
+ accountEmail: acmeConfig?.accountEmail || this.options.tls?.contactEmail || 'admin@example.com',
583
+ certManager: new StorageBackedCertManager(this.storageManager),
584
+ environment: 'production',
585
+ challengeHandlers: challengeHandlers,
586
+ challengePriority: ['dns-01'],
587
+ });
588
+ const scheduler = this.certProvisionScheduler;
589
+ smartProxyConfig.certProvisionFunction = async (domain, eventComms) => {
590
+ // If SmartAcme is not yet ready (still starting or retrying), fall back to HTTP-01
591
+ if (!this.smartAcmeReady) {
592
+ eventComms.warn(`SmartAcme not yet initialized, falling back to http-01 for ${domain}`);
593
+ return 'http01';
594
+ }
595
+ // Check backoff before attempting provision
596
+ if (await scheduler.isInBackoff(domain)) {
597
+ const info = await scheduler.getBackoffInfo(domain);
598
+ const msg = `Domain ${domain} is in backoff (${info?.failures} failures), retry after ${info?.retryAfter}`;
599
+ eventComms.warn(msg);
600
+ throw new Error(msg);
601
+ }
602
+ try {
603
+ // smartacme v9 handles concurrency, per-domain dedup, and rate limiting internally
604
+ eventComms.log(`Attempting DNS-01 via SmartAcme for ${domain}`);
605
+ eventComms.setSource('smartacme-dns-01');
606
+ const isWildcardDomain = domain.startsWith('*.');
607
+ const cert = await this.smartAcme.getCertificateForDomain(domain, {
608
+ includeWildcard: !isWildcardDomain,
609
+ });
610
+ // Parse real X509 expiry from PEM (defense-in-depth over SmartAcme's estimate)
611
+ let realValidUntil = cert.validUntil;
612
+ if (cert.publicKey) {
613
+ try {
614
+ const x509 = new plugins.crypto.X509Certificate(cert.publicKey);
615
+ realValidUntil = new Date(x509.validTo).getTime();
616
+ }
617
+ catch { /* fallback to SmartAcme's value */ }
618
+ }
619
+ if (realValidUntil) {
620
+ eventComms.setExpiryDate(new Date(realValidUntil));
621
+ }
622
+ const result = {
623
+ id: cert.id,
624
+ domainName: cert.domainName,
625
+ created: cert.created,
626
+ validUntil: realValidUntil,
627
+ privateKey: cert.privateKey,
628
+ publicKey: cert.publicKey,
629
+ csr: cert.csr,
630
+ };
631
+ // Success — clear any backoff
632
+ await scheduler.clearBackoff(domain);
633
+ return result;
634
+ }
635
+ catch (err) {
636
+ // Record failure for backoff tracking
637
+ await scheduler.recordFailure(domain, err.message);
638
+ eventComms.warn(`SmartAcme DNS-01 failed for ${domain}: ${err.message}, falling back to http-01`);
639
+ return 'http01';
640
+ }
641
+ };
642
+ }
643
+ // When remoteIngress is enabled, the hub binary forwards tunneled connections
644
+ // to SmartProxy with PROXY protocol v1 headers to preserve client IPs.
645
+ if (this.options.remoteIngressConfig?.enabled) {
646
+ smartProxyConfig.acceptProxyProtocol = true;
647
+ smartProxyConfig.proxyIPs = ['127.0.0.1'];
648
+ }
649
+ // VPN uses socket mode with PP v2 — SmartProxy must accept proxy protocol from localhost
650
+ if (this.options.vpnConfig?.enabled) {
651
+ smartProxyConfig.acceptProxyProtocol = true;
652
+ if (!smartProxyConfig.proxyIPs) {
653
+ smartProxyConfig.proxyIPs = [];
654
+ }
655
+ if (!smartProxyConfig.proxyIPs.includes('127.0.0.1')) {
656
+ smartProxyConfig.proxyIPs.push('127.0.0.1');
657
+ }
658
+ }
659
+ // Create SmartProxy instance
660
+ logger.log('info', `Creating SmartProxy instance: routes=${smartProxyConfig.routes?.length}, acme=${smartProxyConfig.acme?.enabled}, certProvisionFunction=${!!smartProxyConfig.certProvisionFunction}`);
661
+ this.smartProxy = new plugins.smartproxy.SmartProxy(smartProxyConfig);
662
+ // Set up event listeners
663
+ this.smartProxy.on('error', (err) => {
664
+ logger.log('error', `SmartProxy error: ${err.message}`, { stack: err.stack });
665
+ });
666
+ // Always listen for certificate events — emitted by both ACME and certProvisionFunction paths
667
+ // Events are keyed by domain for domain-centric certificate tracking
668
+ this.smartProxy.on('certificate-issued', (event) => {
669
+ logger.log('info', `Certificate issued for ${event.domain} via ${event.source}, expires ${event.expiryDate}`);
670
+ const routeNames = this.findRouteNamesForDomain(event.domain);
671
+ this.certificateStatusMap.set(event.domain, {
672
+ status: 'valid', routeNames,
673
+ expiryDate: event.expiryDate, issuedAt: new Date().toISOString(),
674
+ source: event.source,
675
+ });
676
+ });
677
+ this.smartProxy.on('certificate-renewed', (event) => {
678
+ logger.log('info', `Certificate renewed for ${event.domain} via ${event.source}, expires ${event.expiryDate}`);
679
+ const routeNames = this.findRouteNamesForDomain(event.domain);
680
+ this.certificateStatusMap.set(event.domain, {
681
+ status: 'valid', routeNames,
682
+ expiryDate: event.expiryDate, issuedAt: new Date().toISOString(),
683
+ source: event.source,
684
+ });
685
+ });
686
+ this.smartProxy.on('certificate-failed', (event) => {
687
+ logger.log('error', `Certificate failed for ${event.domain} (${event.source}): ${event.error}`);
688
+ const routeNames = this.findRouteNamesForDomain(event.domain);
689
+ this.certificateStatusMap.set(event.domain, {
690
+ status: 'failed', routeNames, error: event.error,
691
+ source: event.source,
692
+ });
693
+ });
694
+ // Start SmartProxy
695
+ logger.log('info', 'Starting SmartProxy...');
696
+ await this.smartProxy.start();
697
+ logger.log('info', 'SmartProxy started successfully');
698
+ // Populate certificateStatusMap for certs loaded from store at startup
699
+ for (const entry of loadedCertEntries) {
700
+ if (!this.certificateStatusMap.has(entry.domain)) {
701
+ const routeNames = this.findRouteNamesForDomain(entry.domain);
702
+ let expiryDate;
703
+ let issuedAt;
704
+ // Use validUntil/validFrom from stored proxy-certs data if available
705
+ if (entry.validUntil) {
706
+ expiryDate = new Date(entry.validUntil).toISOString();
707
+ }
708
+ if (entry.validFrom) {
709
+ issuedAt = new Date(entry.validFrom).toISOString();
710
+ }
711
+ // Try SmartAcme /certs/ metadata as secondary source
712
+ if (!expiryDate) {
713
+ try {
714
+ const cleanDomain = entry.domain.replace(/^\*\.?/, '');
715
+ const certMeta = await this.storageManager.getJSON(`/certs/${cleanDomain}`);
716
+ if (certMeta?.validUntil) {
717
+ expiryDate = new Date(certMeta.validUntil).toISOString();
718
+ }
719
+ if (certMeta?.created && !issuedAt) {
720
+ issuedAt = new Date(certMeta.created).toISOString();
721
+ }
722
+ }
723
+ catch { /* no metadata available */ }
724
+ }
725
+ // Fallback: parse X509 from PEM to get expiry
726
+ if (!expiryDate && entry.publicKey) {
727
+ try {
728
+ const x509 = new plugins.crypto.X509Certificate(entry.publicKey);
729
+ expiryDate = new Date(x509.validTo).toISOString();
730
+ if (!issuedAt) {
731
+ issuedAt = new Date(x509.validFrom).toISOString();
732
+ }
733
+ }
734
+ catch { /* PEM parsing failed */ }
735
+ }
736
+ this.certificateStatusMap.set(entry.domain, {
737
+ status: 'valid',
738
+ routeNames,
739
+ expiryDate,
740
+ issuedAt,
741
+ source: 'cert-store',
742
+ });
743
+ }
744
+ }
745
+ if (loadedCertEntries.length > 0) {
746
+ logger.log('info', `Populated certificate status for ${loadedCertEntries.length} store-loaded domain(s)`);
747
+ }
748
+ logger.log('info', `SmartProxy started with ${routes.length} routes`);
749
+ }
750
+ }
751
+ /**
752
+ * Generate SmartProxy routes for email configuration
753
+ */
754
+ generateEmailRoutes(emailConfig) {
755
+ const emailRoutes = [];
756
+ // Create routes for each email port
757
+ for (const port of emailConfig.ports) {
758
+ // Create a descriptive name for the route based on the port
759
+ let routeName = 'email-route';
760
+ let tlsMode = 'passthrough';
761
+ // Handle different email ports differently
762
+ switch (port) {
763
+ case 25: // SMTP
764
+ routeName = 'smtp-route';
765
+ tlsMode = 'passthrough'; // STARTTLS handled by email server
766
+ break;
767
+ case 587: // Submission
768
+ routeName = 'submission-route';
769
+ tlsMode = 'passthrough'; // STARTTLS handled by email server
770
+ break;
771
+ case 465: // SMTPS
772
+ routeName = 'smtps-route';
773
+ tlsMode = 'terminate'; // Terminate TLS and re-encrypt to email server
774
+ break;
775
+ default:
776
+ routeName = `email-port-${port}-route`;
777
+ tlsMode = 'passthrough';
778
+ // Check if we have specific settings for this port
779
+ if (this.options.emailPortConfig?.portSettings &&
780
+ this.options.emailPortConfig.portSettings[port]) {
781
+ const portSettings = this.options.emailPortConfig.portSettings[port];
782
+ // If this port requires TLS termination, set the mode accordingly
783
+ if (portSettings.terminateTls) {
784
+ tlsMode = 'terminate';
785
+ }
786
+ // Override the route name if specified
787
+ if (portSettings.routeName) {
788
+ routeName = portSettings.routeName;
789
+ }
790
+ }
791
+ break;
792
+ }
793
+ // Create forward action to route to internal email server ports
794
+ const defaultPortMapping = {
795
+ 25: 10025, // SMTP
796
+ 587: 10587, // Submission
797
+ 465: 10465 // SMTPS
798
+ };
799
+ const portMapping = this.options.emailPortConfig?.portMapping || defaultPortMapping;
800
+ const internalPort = portMapping[port] || port + 10000;
801
+ let action = {
802
+ type: 'forward',
803
+ targets: [{
804
+ host: 'localhost', // Forward to internal email server
805
+ port: internalPort
806
+ }],
807
+ tls: {
808
+ mode: tlsMode
809
+ }
810
+ };
811
+ // For TLS terminate mode, add certificate info
812
+ if (tlsMode === 'terminate' && action.tls) {
813
+ action.tls.certificate = 'auto';
814
+ }
815
+ // Create the route configuration
816
+ const routeConfig = {
817
+ name: routeName,
818
+ match: {
819
+ ports: [port]
820
+ },
821
+ action: action
822
+ };
823
+ // Add the route to our list
824
+ emailRoutes.push(routeConfig);
825
+ }
826
+ return emailRoutes;
827
+ }
828
+ /**
829
+ * Generate SmartProxy routes for DNS configuration
830
+ */
831
+ generateDnsRoutes() {
832
+ if (!this.options.dnsNsDomains || this.options.dnsNsDomains.length === 0) {
833
+ return [];
834
+ }
835
+ const dnsRoutes = [];
836
+ // Create routes for DNS-over-HTTPS paths
837
+ const dohPaths = ['/dns-query', '/resolve'];
838
+ // Use the first nameserver domain for DoH routes
839
+ const primaryNameserver = this.options.dnsNsDomains[0];
840
+ for (const path of dohPaths) {
841
+ const dohRoute = {
842
+ name: `dns-over-https-${path.replace('/', '')}`,
843
+ match: {
844
+ ports: [443], // HTTPS port for DoH
845
+ domains: [primaryNameserver],
846
+ path: path
847
+ },
848
+ action: {
849
+ type: 'socket-handler',
850
+ socketHandler: this.createDnsSocketHandler()
851
+ }
852
+ };
853
+ dnsRoutes.push(dohRoute);
854
+ }
855
+ return dnsRoutes;
856
+ }
857
+ /**
858
+ * Check if a domain matches a pattern (including wildcard support)
859
+ * @param domain The domain to check
860
+ * @param pattern The pattern to match against (e.g., "*.example.com")
861
+ * @returns Whether the domain matches the pattern
862
+ */
863
+ isDomainMatch(domain, pattern) {
864
+ domain = domain.toLowerCase();
865
+ pattern = pattern.toLowerCase();
866
+ if (domain === pattern)
867
+ return true;
868
+ // Routing-glob: *example.com matches example.com, sub.example.com, *.example.com
869
+ if (pattern.startsWith('*') && !pattern.startsWith('*.')) {
870
+ const baseDomain = pattern.slice(1); // *nevermind.cloud → nevermind.cloud
871
+ if (domain === baseDomain || domain === `*.${baseDomain}`)
872
+ return true;
873
+ if (domain.endsWith(baseDomain) && domain.length > baseDomain.length)
874
+ return true;
875
+ }
876
+ // Standard wildcard: *.example.com matches sub.example.com and example.com
877
+ if (pattern.startsWith('*.')) {
878
+ const suffix = pattern.slice(2);
879
+ if (domain === suffix)
880
+ return true;
881
+ return domain.endsWith(suffix) && domain.length > suffix.length;
882
+ }
883
+ return false;
884
+ }
885
+ /**
886
+ * Find ALL route names that match a given domain
887
+ */
888
+ findRouteNamesForDomain(domain) {
889
+ if (!this.smartProxy)
890
+ return [];
891
+ const names = [];
892
+ for (const route of this.smartProxy.routeManager.getRoutes()) {
893
+ if (!route.match.domains || !route.name)
894
+ continue;
895
+ const routeDomains = Array.isArray(route.match.domains)
896
+ ? route.match.domains
897
+ : [route.match.domains];
898
+ for (const pattern of routeDomains) {
899
+ if (this.isDomainMatch(domain, pattern)) {
900
+ names.push(route.name);
901
+ break; // This route already matched, no need to check other patterns
902
+ }
903
+ }
904
+ }
905
+ return names;
906
+ }
907
+ /**
908
+ * Get the routes derived from constructor config (smartProxy + email + DNS).
909
+ * Used by RouteConfigManager as the "hardcoded" base.
910
+ */
911
+ getConstructorRoutes() {
912
+ return this.constructorRoutes;
913
+ }
914
+ async stop() {
915
+ logger.log('info', 'Stopping DcRouter services...');
916
+ // Unsubscribe from service events before stopping services
917
+ if (this.serviceSubjectSubscription) {
918
+ this.serviceSubjectSubscription.unsubscribe();
919
+ this.serviceSubjectSubscription = undefined;
920
+ }
921
+ // ServiceManager handles reverse-dependency-ordered shutdown
922
+ await this.serviceManager.stop();
923
+ // Clear backoff cache in cert scheduler
924
+ if (this.certProvisionScheduler) {
925
+ this.certProvisionScheduler.clear();
926
+ this.certProvisionScheduler = undefined;
927
+ }
928
+ this.certificateStatusMap.clear();
929
+ // Reset security singletons to allow GC
930
+ SecurityLogger.resetInstance();
931
+ ContentScanner.resetInstance();
932
+ IPReputationChecker.resetInstance();
933
+ logger.log('info', 'All DcRouter services stopped');
934
+ }
935
+ /**
936
+ * Update SmartProxy configuration
937
+ * @param config New SmartProxy configuration
938
+ */
939
+ async updateSmartProxyConfig(config) {
940
+ // Stop existing SmartProxy if running
941
+ if (this.smartProxy) {
942
+ this.smartProxy.removeAllListeners();
943
+ await this.smartProxy.stop();
944
+ this.smartProxy = undefined;
945
+ }
946
+ // Update configuration
947
+ this.options.smartProxyConfig = config;
948
+ // Update routes on RemoteIngressManager so derived ports stay in sync
949
+ if (this.remoteIngressManager && config.routes) {
950
+ this.remoteIngressManager.setRoutes(config.routes);
951
+ }
952
+ // Start new SmartProxy with updated configuration (will include email routes if configured)
953
+ await this.setupSmartProxy();
954
+ // Re-apply programmatic routes and overrides after SmartProxy restart
955
+ if (this.routeConfigManager) {
956
+ await this.routeConfigManager.initialize();
957
+ }
958
+ logger.log('info', 'SmartProxy configuration updated');
959
+ }
960
+ /**
961
+ * Set up unified email handling with pattern-based routing
962
+ * This implements the consolidated emailConfig approach
963
+ */
964
+ async setupUnifiedEmailHandling() {
965
+ if (!this.options.emailConfig) {
966
+ throw new Error('Email configuration is required for unified email handling');
967
+ }
968
+ // Apply port mapping if behind SmartProxy
969
+ const portMapping = this.options.emailPortConfig?.portMapping || {
970
+ 25: 10025, // SMTP
971
+ 587: 10587, // Submission
972
+ 465: 10465 // SMTPS
973
+ };
974
+ // Transform domains if they are provided as strings
975
+ let transformedDomains = this.options.emailConfig.domains;
976
+ if (transformedDomains && transformedDomains.length > 0) {
977
+ // Check if domains are strings (for backward compatibility)
978
+ if (typeof transformedDomains[0] === 'string') {
979
+ transformedDomains = transformedDomains.map((domain) => ({
980
+ domain,
981
+ dnsMode: 'external-dns',
982
+ dkim: {
983
+ selector: 'default',
984
+ keySize: 2048,
985
+ rotateKeys: false,
986
+ rotationInterval: 90
987
+ }
988
+ }));
989
+ }
990
+ }
991
+ // Create config with mapped ports
992
+ const emailConfig = {
993
+ ...this.options.emailConfig,
994
+ domains: transformedDomains,
995
+ ports: this.options.emailConfig.ports.map(port => portMapping[port] || port + 10000),
996
+ hostname: 'localhost' // Listen on localhost for SmartProxy forwarding
997
+ };
998
+ // Create unified email server
999
+ this.emailServer = new UnifiedEmailServer(this, emailConfig);
1000
+ // Set up error handling
1001
+ this.emailServer.on('error', (err) => {
1002
+ logger.log('error', `UnifiedEmailServer error: ${err.message}`);
1003
+ });
1004
+ // Start the server
1005
+ await this.emailServer.start();
1006
+ // Wire delivery events to MetricsManager and logger
1007
+ if (this.metricsManager && this.emailServer.deliverySystem) {
1008
+ this.emailServer.deliverySystem.on('deliveryStart', (item) => {
1009
+ this.metricsManager.trackEmailReceived(item?.from);
1010
+ logger.log('info', `Email delivery started: ${item?.from} → ${item?.to}`, { zone: 'email' });
1011
+ });
1012
+ this.emailServer.deliverySystem.on('deliverySuccess', (item) => {
1013
+ this.metricsManager.trackEmailSent(item?.to);
1014
+ logger.log('info', `Email delivered to ${item?.to}`, { zone: 'email' });
1015
+ });
1016
+ this.emailServer.deliverySystem.on('deliveryFailed', (item, error) => {
1017
+ this.metricsManager.trackEmailFailed(item?.to, error?.message);
1018
+ logger.log('warn', `Email delivery failed to ${item?.to}: ${error?.message}`, { zone: 'email' });
1019
+ });
1020
+ }
1021
+ if (this.metricsManager && this.emailServer) {
1022
+ this.emailServer.on('bounceProcessed', () => {
1023
+ this.metricsManager.trackEmailBounced();
1024
+ logger.log('warn', 'Email bounce processed', { zone: 'email' });
1025
+ });
1026
+ }
1027
+ logger.log('info', `Email server started on ports: ${emailConfig.ports.join(', ')}`);
1028
+ }
1029
+ /**
1030
+ * Update the unified email configuration
1031
+ * @param config New email configuration
1032
+ */
1033
+ async updateEmailConfig(config) {
1034
+ // Stop existing email components
1035
+ await this.stopUnifiedEmailComponents();
1036
+ // Update configuration
1037
+ this.options.emailConfig = config;
1038
+ // Start email handling with new configuration
1039
+ await this.setupUnifiedEmailHandling();
1040
+ logger.log('info', 'Unified email configuration updated');
1041
+ }
1042
+ /**
1043
+ * Stop all unified email components
1044
+ */
1045
+ async stopUnifiedEmailComponents() {
1046
+ try {
1047
+ // Stop the unified email server which contains all components
1048
+ if (this.emailServer) {
1049
+ // Remove listeners before stopping to prevent leaks on config update cycles
1050
+ if (this.emailServer.deliverySystem) {
1051
+ this.emailServer.deliverySystem.removeAllListeners();
1052
+ }
1053
+ this.emailServer.removeAllListeners();
1054
+ await this.emailServer.stop();
1055
+ logger.log('info', 'Unified email server stopped');
1056
+ this.emailServer = undefined;
1057
+ }
1058
+ logger.log('info', 'All unified email components stopped');
1059
+ }
1060
+ catch (error) {
1061
+ logger.log('error', `Error stopping unified email components: ${error.message}`);
1062
+ throw error;
1063
+ }
1064
+ }
1065
+ /**
1066
+ * Update domain rules for email routing
1067
+ * @param rules New domain rules to apply
1068
+ */
1069
+ async updateEmailRoutes(routes) {
1070
+ // Validate that email config exists
1071
+ if (!this.options.emailConfig) {
1072
+ throw new Error('Email configuration is required before updating routes');
1073
+ }
1074
+ // Update the configuration
1075
+ this.options.emailConfig.routes = routes;
1076
+ // Update the unified email server if it exists
1077
+ if (this.emailServer) {
1078
+ this.emailServer.updateEmailRoutes(routes);
1079
+ }
1080
+ logger.log('info', `Email routes updated with ${routes.length} routes`);
1081
+ }
1082
+ /**
1083
+ * Get statistics from all components
1084
+ */
1085
+ getStats() {
1086
+ const stats = {
1087
+ emailServer: this.emailServer?.getStats()
1088
+ };
1089
+ return stats;
1090
+ }
1091
+ /**
1092
+ * Register DNS records with the DNS server
1093
+ * @param records Array of DNS records to register
1094
+ */
1095
+ registerDnsRecords(records) {
1096
+ if (!this.dnsServer)
1097
+ return;
1098
+ // Register a separate handler for each record
1099
+ // This ensures multiple records of the same type (like NS records) are all served
1100
+ for (const record of records) {
1101
+ // Register handler for this specific record
1102
+ this.dnsServer.registerHandler(record.name, [record.type], (question) => {
1103
+ // Check if this handler matches the question
1104
+ if (question.name === record.name && question.type === record.type) {
1105
+ return {
1106
+ name: record.name,
1107
+ type: record.type,
1108
+ class: 'IN',
1109
+ ttl: record.ttl || 300,
1110
+ data: this.parseDnsRecordData(record.type, record.value)
1111
+ };
1112
+ }
1113
+ return null;
1114
+ });
1115
+ }
1116
+ logger.log('info', `Registered ${records.length} DNS handlers (one per record)`);
1117
+ }
1118
+ /**
1119
+ * Parse DNS record data based on record type
1120
+ * @param type DNS record type
1121
+ * @param value DNS record value
1122
+ * @returns Parsed data for the DNS response
1123
+ */
1124
+ parseDnsRecordData(type, value) {
1125
+ switch (type) {
1126
+ case 'A':
1127
+ return value; // IP address as string
1128
+ case 'MX':
1129
+ const [priority, exchange] = value.split(' ');
1130
+ return { priority: parseInt(priority), exchange };
1131
+ case 'TXT':
1132
+ return value;
1133
+ case 'NS':
1134
+ return value;
1135
+ case 'SOA':
1136
+ // SOA format: primary-ns admin-email serial refresh retry expire minimum
1137
+ const parts = value.split(' ');
1138
+ return {
1139
+ mname: parts[0],
1140
+ rname: parts[1],
1141
+ serial: parseInt(parts[2]),
1142
+ refresh: parseInt(parts[3]),
1143
+ retry: parseInt(parts[4]),
1144
+ expire: parseInt(parts[5]),
1145
+ minimum: parseInt(parts[6])
1146
+ };
1147
+ default:
1148
+ return value;
1149
+ }
1150
+ }
1151
+ /**
1152
+ * Set up DNS server with socket handler for DoH
1153
+ */
1154
+ async setupDnsWithSocketHandler() {
1155
+ if (!this.options.dnsNsDomains || this.options.dnsNsDomains.length === 0) {
1156
+ throw new Error('dnsNsDomains is required for DNS server setup');
1157
+ }
1158
+ if (!this.options.dnsScopes || this.options.dnsScopes.length === 0) {
1159
+ throw new Error('dnsScopes is required for DNS server setup');
1160
+ }
1161
+ const primaryNameserver = this.options.dnsNsDomains[0];
1162
+ logger.log('info', `Setting up DNS server with primary nameserver: ${primaryNameserver}`);
1163
+ // Get VM IP address for UDP binding
1164
+ const networkInterfaces = plugins.os.networkInterfaces();
1165
+ let vmIpAddress = '0.0.0.0'; // Default to all interfaces
1166
+ // Try to find the VM's internal IP address
1167
+ for (const [_name, interfaces] of Object.entries(networkInterfaces)) {
1168
+ if (interfaces) {
1169
+ for (const iface of interfaces) {
1170
+ if (!iface.internal && iface.family === 'IPv4') {
1171
+ vmIpAddress = iface.address;
1172
+ break;
1173
+ }
1174
+ }
1175
+ }
1176
+ }
1177
+ // Create DNS server instance with manual HTTPS mode
1178
+ this.dnsServer = new plugins.smartdns.dnsServerMod.DnsServer({
1179
+ udpPort: 53,
1180
+ udpBindInterface: vmIpAddress,
1181
+ httpsPort: 443, // Required but won't bind due to manual mode
1182
+ manualHttpsMode: true, // Enable manual HTTPS socket handling
1183
+ dnssecZone: primaryNameserver,
1184
+ primaryNameserver: primaryNameserver, // Automatically generates correct SOA records
1185
+ // For now, use self-signed cert until we integrate with Let's Encrypt
1186
+ httpsKey: '',
1187
+ httpsCert: ''
1188
+ });
1189
+ // Start the DNS server (UDP only)
1190
+ await this.dnsServer.start();
1191
+ logger.log('info', `DNS server started on UDP ${vmIpAddress}:53`);
1192
+ // Wire DNS query events to MetricsManager and logger with adaptive rate limiting
1193
+ if (this.metricsManager && this.dnsServer) {
1194
+ const flushDnsBatch = () => {
1195
+ if (this.dnsBatchCount > 0) {
1196
+ logger.log('info', `DNS: ${this.dnsBatchCount} queries processed (rate limited)`, { zone: 'dns' });
1197
+ this.dnsBatchCount = 0;
1198
+ }
1199
+ this.dnsBatchTimer = null;
1200
+ };
1201
+ this.dnsServer.on('query', (event) => {
1202
+ // Metrics tracking
1203
+ for (const question of event.questions) {
1204
+ this.metricsManager?.trackDnsQuery(question.type, question.name, false, event.responseTimeMs, event.answered);
1205
+ }
1206
+ // Adaptive logging: individual logs up to 2/sec, then batch
1207
+ const nowSec = Math.floor(Date.now() / 1000);
1208
+ if (nowSec !== this.dnsLogWindowSecond) {
1209
+ this.dnsLogWindowSecond = nowSec;
1210
+ this.dnsLogWindowCount = 0;
1211
+ }
1212
+ if (this.dnsLogWindowCount < 2) {
1213
+ this.dnsLogWindowCount++;
1214
+ const summary = event.questions.map(q => `${q.type} ${q.name}`).join(', ');
1215
+ logger.log('info', `DNS query: ${summary} (${event.responseTimeMs}ms, ${event.answered ? 'answered' : 'unanswered'})`, { zone: 'dns' });
1216
+ }
1217
+ else {
1218
+ this.dnsBatchCount++;
1219
+ if (!this.dnsBatchTimer) {
1220
+ this.dnsBatchTimer = setTimeout(flushDnsBatch, 5000);
1221
+ }
1222
+ }
1223
+ });
1224
+ }
1225
+ // Validate DNS configuration
1226
+ await this.validateDnsConfiguration();
1227
+ // Generate and register authoritative records
1228
+ const authoritativeRecords = await this.generateAuthoritativeRecords();
1229
+ // Generate email DNS records
1230
+ const emailDnsRecords = await this.generateEmailDnsRecords();
1231
+ // Initialize DKIM for all email domains
1232
+ await this.initializeDkimForEmailDomains();
1233
+ // Load DKIM records from JSON files (they should now exist)
1234
+ const dkimRecords = await this.loadDkimRecords();
1235
+ // Combine all records: authoritative, email, DKIM, and user-defined
1236
+ const allRecords = [...authoritativeRecords, ...emailDnsRecords, ...dkimRecords];
1237
+ if (this.options.dnsRecords && this.options.dnsRecords.length > 0) {
1238
+ allRecords.push(...this.options.dnsRecords);
1239
+ }
1240
+ // Apply proxy IP replacement if configured
1241
+ await this.applyProxyIpReplacement(allRecords);
1242
+ // Register all DNS records
1243
+ if (allRecords.length > 0) {
1244
+ this.registerDnsRecords(allRecords);
1245
+ logger.log('info', `Registered ${allRecords.length} DNS records (${authoritativeRecords.length} authoritative, ${emailDnsRecords.length} email, ${dkimRecords.length} DKIM, ${this.options.dnsRecords?.length || 0} user-defined)`);
1246
+ }
1247
+ }
1248
+ /**
1249
+ * Create DNS socket handler for DoH
1250
+ */
1251
+ createDnsSocketHandler() {
1252
+ return async (socket) => {
1253
+ if (!this.dnsServer) {
1254
+ logger.log('error', 'DNS socket handler called but DNS server not initialized');
1255
+ socket.end();
1256
+ return;
1257
+ }
1258
+ // Prevent uncaught exception from socket 'error' events
1259
+ socket.on('error', (err) => {
1260
+ logger.log('error', `DNS socket error: ${err.message}`);
1261
+ if (!socket.destroyed) {
1262
+ socket.destroy();
1263
+ }
1264
+ });
1265
+ logger.log('debug', 'DNS socket handler: passing socket to DnsServer');
1266
+ try {
1267
+ // Use the built-in socket handler from smartdns
1268
+ // This handles HTTP/2, DoH protocol, etc.
1269
+ await this.dnsServer.handleHttpsSocket(socket);
1270
+ }
1271
+ catch (error) {
1272
+ logger.log('error', `DNS socket handler error: ${error.message}`);
1273
+ if (!socket.destroyed) {
1274
+ socket.destroy();
1275
+ }
1276
+ }
1277
+ };
1278
+ }
1279
+ /**
1280
+ * Validate DNS configuration
1281
+ */
1282
+ async validateDnsConfiguration() {
1283
+ if (!this.options.dnsNsDomains || !this.options.dnsScopes) {
1284
+ return;
1285
+ }
1286
+ logger.log('info', 'Validating DNS configuration...');
1287
+ // Check if email domains with internal-dns are in dnsScopes
1288
+ if (this.options.emailConfig?.domains) {
1289
+ for (const domainConfig of this.options.emailConfig.domains) {
1290
+ if (domainConfig.dnsMode === 'internal-dns' &&
1291
+ !this.options.dnsScopes.includes(domainConfig.domain)) {
1292
+ logger.log('warn', `Email domain '${domainConfig.domain}' with internal-dns mode is not in dnsScopes. It should be added to dnsScopes.`);
1293
+ }
1294
+ }
1295
+ }
1296
+ // Validate user-provided DNS records are within scopes
1297
+ if (this.options.dnsRecords) {
1298
+ for (const record of this.options.dnsRecords) {
1299
+ const recordDomain = this.extractDomain(record.name);
1300
+ const isInScope = this.options.dnsScopes.some(scope => recordDomain === scope || recordDomain.endsWith(`.${scope}`));
1301
+ if (!isInScope) {
1302
+ logger.log('warn', `DNS record for '${record.name}' is outside defined scopes [${this.options.dnsScopes.join(', ')}]`);
1303
+ }
1304
+ }
1305
+ }
1306
+ }
1307
+ /**
1308
+ * Generate email DNS records for domains with internal-dns mode
1309
+ */
1310
+ async generateEmailDnsRecords() {
1311
+ const records = [];
1312
+ if (!this.options.emailConfig?.domains) {
1313
+ return records;
1314
+ }
1315
+ // Filter domains with internal-dns mode
1316
+ const internalDnsDomains = this.options.emailConfig.domains.filter(domain => domain.dnsMode === 'internal-dns');
1317
+ for (const domainConfig of internalDnsDomains) {
1318
+ const domain = domainConfig.domain;
1319
+ const ttl = domainConfig.dns?.internal?.ttl || 3600;
1320
+ const mxPriority = domainConfig.dns?.internal?.mxPriority || 10;
1321
+ // MX record - points to the domain itself for email handling
1322
+ records.push({
1323
+ name: domain,
1324
+ type: 'MX',
1325
+ value: `${mxPriority} ${domain}`,
1326
+ ttl
1327
+ });
1328
+ // SPF record - using sensible defaults
1329
+ const spfRecord = 'v=spf1 a mx ~all';
1330
+ records.push({
1331
+ name: domain,
1332
+ type: 'TXT',
1333
+ value: spfRecord,
1334
+ ttl
1335
+ });
1336
+ // DMARC record - using sensible defaults
1337
+ const dmarcPolicy = 'none'; // Start with 'none' policy for monitoring
1338
+ const dmarcEmail = `dmarc@${domain}`;
1339
+ records.push({
1340
+ name: `_dmarc.${domain}`,
1341
+ type: 'TXT',
1342
+ value: `v=DMARC1; p=${dmarcPolicy}; rua=mailto:${dmarcEmail}`,
1343
+ ttl
1344
+ });
1345
+ // Note: DKIM records will be generated later when DKIM keys are available
1346
+ // They require the DKIMCreator which is part of the email server
1347
+ }
1348
+ logger.log('info', `Generated ${records.length} email DNS records for ${internalDnsDomains.length} internal-dns domains`);
1349
+ return records;
1350
+ }
1351
+ /**
1352
+ * Load DKIM records from JSON files
1353
+ * Reads all *.dkimrecord.json files from the DNS records directory
1354
+ */
1355
+ async loadDkimRecords() {
1356
+ const records = [];
1357
+ try {
1358
+ // Ensure paths are imported
1359
+ const dnsDir = this.resolvedPaths.dnsRecordsDir;
1360
+ // Check if directory exists
1361
+ if (!plugins.fs.existsSync(dnsDir)) {
1362
+ logger.log('debug', 'No DNS records directory found, skipping DKIM record loading');
1363
+ return records;
1364
+ }
1365
+ // Read all files in the directory
1366
+ const files = plugins.fs.readdirSync(dnsDir);
1367
+ const dkimFiles = files.filter(f => f.endsWith('.dkimrecord.json'));
1368
+ logger.log('info', `Found ${dkimFiles.length} DKIM record files`);
1369
+ // Load each DKIM record
1370
+ for (const file of dkimFiles) {
1371
+ try {
1372
+ const filePath = plugins.path.join(dnsDir, file);
1373
+ const fileContent = plugins.fs.readFileSync(filePath, 'utf8');
1374
+ const dkimRecord = JSON.parse(fileContent);
1375
+ // Validate record structure
1376
+ if (dkimRecord.name && dkimRecord.type === 'TXT' && dkimRecord.value) {
1377
+ records.push({
1378
+ name: dkimRecord.name,
1379
+ type: 'TXT',
1380
+ value: dkimRecord.value,
1381
+ ttl: 3600 // Standard DKIM TTL
1382
+ });
1383
+ logger.log('info', `Loaded DKIM record for ${dkimRecord.name}`);
1384
+ }
1385
+ else {
1386
+ logger.log('warn', `Invalid DKIM record structure in ${file}`);
1387
+ }
1388
+ }
1389
+ catch (error) {
1390
+ logger.log('error', `Failed to load DKIM record from ${file}: ${error.message}`);
1391
+ }
1392
+ }
1393
+ }
1394
+ catch (error) {
1395
+ logger.log('error', `Failed to load DKIM records: ${error.message}`);
1396
+ }
1397
+ return records;
1398
+ }
1399
+ /**
1400
+ * Initialize DKIM keys for all configured email domains
1401
+ * This ensures DKIM records are available immediately at startup
1402
+ */
1403
+ async initializeDkimForEmailDomains() {
1404
+ if (!this.options.emailConfig?.domains || !this.emailServer) {
1405
+ return;
1406
+ }
1407
+ logger.log('info', 'Initializing DKIM keys for email domains...');
1408
+ // Get DKIMCreator instance from email server (public in smartmta)
1409
+ const dkimCreator = this.emailServer.dkimCreator;
1410
+ if (!dkimCreator) {
1411
+ logger.log('warn', 'DKIMCreator not available, skipping DKIM initialization');
1412
+ return;
1413
+ }
1414
+ // Ensure necessary directories exist
1415
+ paths.ensureDataDirectories(this.resolvedPaths);
1416
+ // Generate DKIM keys for each email domain
1417
+ for (const domainConfig of this.options.emailConfig.domains) {
1418
+ try {
1419
+ // Generate DKIM keys for all domains, regardless of DNS mode
1420
+ // This ensures keys are ready even if DNS mode changes later
1421
+ await dkimCreator.handleDKIMKeysForDomain(domainConfig.domain);
1422
+ logger.log('info', `DKIM keys initialized for ${domainConfig.domain}`);
1423
+ }
1424
+ catch (error) {
1425
+ logger.log('error', `Failed to initialize DKIM for ${domainConfig.domain}: ${error.message}`);
1426
+ }
1427
+ }
1428
+ logger.log('info', 'DKIM initialization complete');
1429
+ }
1430
+ /**
1431
+ * Generate authoritative DNS records (NS only) for all domains in dnsScopes
1432
+ * SOA records are now automatically generated by smartdns with primaryNameserver setting
1433
+ */
1434
+ async generateAuthoritativeRecords() {
1435
+ const records = [];
1436
+ if (!this.options.dnsNsDomains || !this.options.dnsScopes) {
1437
+ return records;
1438
+ }
1439
+ // Determine the public IP for nameserver A records
1440
+ let publicIp = null;
1441
+ // Use proxy IPs if configured (these should be public IPs)
1442
+ if (this.options.proxyIps && this.options.proxyIps.length > 0) {
1443
+ publicIp = this.options.proxyIps[0]; // Use first proxy IP
1444
+ logger.log('info', `Using proxy IP for nameserver A records: ${publicIp}`);
1445
+ }
1446
+ else if (this.options.publicIp) {
1447
+ // Use explicitly configured public IP
1448
+ publicIp = this.options.publicIp;
1449
+ this.detectedPublicIp = publicIp;
1450
+ logger.log('info', `Using configured public IP for nameserver A records: ${publicIp}`);
1451
+ }
1452
+ else {
1453
+ // Auto-discover public IP using smartnetwork
1454
+ try {
1455
+ logger.log('info', 'Auto-discovering public IP address...');
1456
+ const smartNetwork = new plugins.smartnetwork.SmartNetwork();
1457
+ const publicIps = await smartNetwork.getPublicIps();
1458
+ if (publicIps.v4) {
1459
+ publicIp = publicIps.v4;
1460
+ this.detectedPublicIp = publicIp;
1461
+ logger.log('info', `Auto-discovered public IPv4: ${publicIp}`);
1462
+ }
1463
+ else {
1464
+ logger.log('warn', 'Could not auto-discover public IPv4 address');
1465
+ }
1466
+ }
1467
+ catch (error) {
1468
+ logger.log('error', `Failed to auto-discover public IP: ${error.message}`);
1469
+ }
1470
+ if (!publicIp) {
1471
+ logger.log('warn', 'No public IP available. Nameserver A records require either proxyIps, publicIp, or successful auto-discovery.');
1472
+ }
1473
+ }
1474
+ // Generate A records for nameservers if we have a public IP
1475
+ if (publicIp) {
1476
+ for (const nsDomain of this.options.dnsNsDomains) {
1477
+ records.push({
1478
+ name: nsDomain,
1479
+ type: 'A',
1480
+ value: publicIp,
1481
+ ttl: 3600
1482
+ });
1483
+ }
1484
+ logger.log('info', `Generated A records for ${this.options.dnsNsDomains.length} nameservers`);
1485
+ }
1486
+ // Generate NS records for each domain in scopes
1487
+ for (const domain of this.options.dnsScopes) {
1488
+ // Add NS records for all nameservers
1489
+ for (const nsDomain of this.options.dnsNsDomains) {
1490
+ records.push({
1491
+ name: domain,
1492
+ type: 'NS',
1493
+ value: nsDomain,
1494
+ ttl: 3600
1495
+ });
1496
+ }
1497
+ // SOA records are now automatically generated by smartdns DnsServer
1498
+ // with the primaryNameserver configuration option
1499
+ }
1500
+ logger.log('info', `Generated ${records.length} total records (A + NS) for ${this.options.dnsScopes.length} domains`);
1501
+ return records;
1502
+ }
1503
+ /**
1504
+ * Extract the base domain from a DNS record name
1505
+ */
1506
+ extractDomain(recordName) {
1507
+ // Handle wildcards
1508
+ if (recordName.startsWith('*.')) {
1509
+ recordName = recordName.substring(2);
1510
+ }
1511
+ return recordName;
1512
+ }
1513
+ /**
1514
+ * Apply proxy IP replacement logic to DNS records
1515
+ */
1516
+ async applyProxyIpReplacement(records) {
1517
+ if (!this.options.proxyIps || this.options.proxyIps.length === 0) {
1518
+ return; // No proxy IPs configured, skip replacement
1519
+ }
1520
+ // Get server's public IP
1521
+ const serverIp = await this.detectServerPublicIp();
1522
+ if (!serverIp) {
1523
+ logger.log('warn', 'Could not detect server public IP, skipping proxy IP replacement');
1524
+ return;
1525
+ }
1526
+ logger.log('info', `Applying proxy IP replacement. Server IP: ${serverIp}, Proxy IPs: ${this.options.proxyIps.join(', ')}`);
1527
+ let proxyIndex = 0;
1528
+ for (const record of records) {
1529
+ if (record.type === 'A' &&
1530
+ record.value === serverIp &&
1531
+ record.useIngressProxy !== false) {
1532
+ // Round-robin through proxy IPs
1533
+ const proxyIp = this.options.proxyIps[proxyIndex % this.options.proxyIps.length];
1534
+ logger.log('info', `Replacing A record for ${record.name}: ${record.value} → ${proxyIp}`);
1535
+ record.value = proxyIp;
1536
+ proxyIndex++;
1537
+ }
1538
+ }
1539
+ }
1540
+ /**
1541
+ * Detect the server's public IP address
1542
+ */
1543
+ async detectServerPublicIp() {
1544
+ try {
1545
+ const smartNetwork = new plugins.smartnetwork.SmartNetwork();
1546
+ const publicIps = await smartNetwork.getPublicIps();
1547
+ if (publicIps.v4) {
1548
+ return publicIps.v4;
1549
+ }
1550
+ return null;
1551
+ }
1552
+ catch (error) {
1553
+ logger.log('warn', `Failed to detect public IP: ${error.message}`);
1554
+ return null;
1555
+ }
1556
+ }
1557
+ /**
1558
+ * Set up Remote Ingress hub for edge tunnel connections
1559
+ */
1560
+ async setupRemoteIngress() {
1561
+ if (!this.options.remoteIngressConfig?.enabled) {
1562
+ return;
1563
+ }
1564
+ logger.log('info', 'Setting up Remote Ingress hub...');
1565
+ // Initialize the edge registration manager
1566
+ this.remoteIngressManager = new RemoteIngressManager(this.storageManager);
1567
+ await this.remoteIngressManager.initialize();
1568
+ // Pass current routes so the manager can derive edge ports from remoteIngress-tagged routes
1569
+ const currentRoutes = this.constructorRoutes;
1570
+ this.remoteIngressManager.setRoutes(currentRoutes);
1571
+ // Resolve TLS certs for tunnel: explicit paths > ACME for hubDomain > self-signed (Rust default)
1572
+ const riCfg = this.options.remoteIngressConfig;
1573
+ let tlsConfig;
1574
+ // Priority 1: Explicit cert/key file paths
1575
+ if (riCfg.tls?.certPath && riCfg.tls?.keyPath) {
1576
+ try {
1577
+ const certPem = plugins.fs.readFileSync(riCfg.tls.certPath, 'utf8');
1578
+ const keyPem = plugins.fs.readFileSync(riCfg.tls.keyPath, 'utf8');
1579
+ tlsConfig = { certPem, keyPem };
1580
+ logger.log('info', 'Using explicit TLS cert/key for RemoteIngress tunnel');
1581
+ }
1582
+ catch (err) {
1583
+ logger.log('warn', `Failed to read RemoteIngress TLS cert/key files: ${err.message}`);
1584
+ }
1585
+ }
1586
+ // Priority 2: Existing cert from SmartProxy cert store for hubDomain
1587
+ if (!tlsConfig && riCfg.hubDomain) {
1588
+ try {
1589
+ const stored = await this.storageManager.getJSON(`/proxy-certs/${riCfg.hubDomain}`);
1590
+ if (stored?.publicKey && stored?.privateKey) {
1591
+ tlsConfig = { certPem: stored.publicKey, keyPem: stored.privateKey };
1592
+ logger.log('info', `Using stored ACME cert for RemoteIngress tunnel TLS: ${riCfg.hubDomain}`);
1593
+ }
1594
+ }
1595
+ catch { /* no stored cert, fall through */ }
1596
+ }
1597
+ if (!tlsConfig) {
1598
+ logger.log('info', 'No TLS cert configured for RemoteIngress tunnel — using auto-generated self-signed');
1599
+ }
1600
+ // Create and start the tunnel manager
1601
+ this.tunnelManager = new TunnelManager(this.remoteIngressManager, {
1602
+ tunnelPort: riCfg.tunnelPort ?? 8443,
1603
+ targetHost: '127.0.0.1',
1604
+ tls: tlsConfig,
1605
+ });
1606
+ await this.tunnelManager.start();
1607
+ const edgeCount = this.remoteIngressManager.getAllEdges().length;
1608
+ logger.log('info', `Remote Ingress hub started on port ${this.options.remoteIngressConfig.tunnelPort || 8443} with ${edgeCount} registered edge(s)`);
1609
+ }
1610
+ /**
1611
+ * Set up VPN server for VPN-based route access control.
1612
+ */
1613
+ async setupVpnServer() {
1614
+ if (!this.options.vpnConfig?.enabled) {
1615
+ return;
1616
+ }
1617
+ logger.log('info', 'Setting up VPN server...');
1618
+ this.vpnManager = new VpnManager(this.storageManager, {
1619
+ subnet: this.options.vpnConfig.subnet,
1620
+ wgListenPort: this.options.vpnConfig.wgListenPort,
1621
+ dns: this.options.vpnConfig.dns,
1622
+ serverEndpoint: this.options.vpnConfig.serverEndpoint,
1623
+ initialClients: this.options.vpnConfig.clients,
1624
+ destinationPolicy: this.options.vpnConfig.destinationPolicy,
1625
+ onClientChanged: () => {
1626
+ // Re-apply routes so tag-based ipAllowLists get updated
1627
+ this.routeConfigManager?.applyRoutes();
1628
+ },
1629
+ });
1630
+ await this.vpnManager.start();
1631
+ }
1632
+ /**
1633
+ * Inject VPN security into routes that have vpn.required === true.
1634
+ * Adds the VPN subnet to security.ipAllowList so only VPN clients can access them.
1635
+ */
1636
+ injectVpnSecurity(routes) {
1637
+ const vpnSubnet = this.options.vpnConfig?.subnet || '10.8.0.0/24';
1638
+ let injectedCount = 0;
1639
+ const result = routes.map((route) => {
1640
+ const dcrouterRoute = route;
1641
+ if (dcrouterRoute.vpn?.required) {
1642
+ injectedCount++;
1643
+ const existing = route.security?.ipAllowList || [];
1644
+ let vpnAllowList;
1645
+ if (dcrouterRoute.vpn.allowedServerDefinedClientTags?.length && this.vpnManager) {
1646
+ // Tag-based: only specific client IPs
1647
+ vpnAllowList = this.vpnManager.getClientIpsForServerDefinedTags(dcrouterRoute.vpn.allowedServerDefinedClientTags);
1648
+ }
1649
+ else {
1650
+ // No tags specified: entire VPN subnet
1651
+ vpnAllowList = [vpnSubnet];
1652
+ }
1653
+ return {
1654
+ ...route,
1655
+ security: {
1656
+ ...route.security,
1657
+ ipAllowList: [...existing, ...vpnAllowList],
1658
+ },
1659
+ };
1660
+ }
1661
+ return route;
1662
+ });
1663
+ if (injectedCount > 0) {
1664
+ logger.log('info', `VPN: Injected ipAllowList into ${injectedCount} VPN-protected route(s)`);
1665
+ }
1666
+ return result;
1667
+ }
1668
+ /**
1669
+ * Set up RADIUS server for network authentication
1670
+ */
1671
+ async setupRadiusServer() {
1672
+ if (!this.options.radiusConfig) {
1673
+ return;
1674
+ }
1675
+ logger.log('info', 'Setting up RADIUS server...');
1676
+ this.radiusServer = new RadiusServer(this.options.radiusConfig, this.storageManager);
1677
+ await this.radiusServer.start();
1678
+ logger.log('info', `RADIUS server started on ports ${this.options.radiusConfig.authPort || 1812} (auth) and ${this.options.radiusConfig.acctPort || 1813} (acct)`);
1679
+ }
1680
+ /**
1681
+ * Update RADIUS configuration at runtime
1682
+ */
1683
+ async updateRadiusConfig(config) {
1684
+ // Stop existing RADIUS server if running
1685
+ if (this.radiusServer) {
1686
+ await this.radiusServer.stop();
1687
+ this.radiusServer = undefined;
1688
+ }
1689
+ // Update configuration
1690
+ this.options.radiusConfig = config;
1691
+ // Start with new configuration
1692
+ await this.setupRadiusServer();
1693
+ logger.log('info', 'RADIUS configuration updated');
1694
+ }
1695
+ }
1696
+ export default DcRouter;
1697
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kY3JvdXRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2NsYXNzZXMuZGNyb3V0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLEtBQUssTUFBTSxZQUFZLENBQUM7QUFFcEMsc0RBQXNEO0FBRXRELDhEQUE4RDtBQUM5RCxPQUFPLEVBQ0wsa0JBQWtCLEdBSW5CLE1BQU0sc0JBQXNCLENBQUM7QUFDOUIsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNyQyx5QkFBeUI7QUFDekIsT0FBTyxFQUFFLGNBQWMsRUFBdUIsTUFBTSxvQkFBb0IsQ0FBQztBQUN6RSxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUM3RSxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUMvRSxzQkFBc0I7QUFDdEIsT0FBTyxFQUFFLE9BQU8sRUFBRSxZQUFZLEVBQXdCLE1BQU0sa0JBQWtCLENBQUM7QUFFL0UsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUN2RCxPQUFPLEVBQUUsWUFBWSxFQUE0QixNQUFNLG1CQUFtQixDQUFDO0FBQzNFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxhQUFhLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMvRSxPQUFPLEVBQUUsVUFBVSxFQUEwQixNQUFNLGdCQUFnQixDQUFDO0FBQ3BFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxlQUFlLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN4RSxPQUFPLEVBQUUsY0FBYyxFQUFFLGNBQWMsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzFGLE9BQU8sRUFBcUIsc0JBQXNCLEVBQUUs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