@push.rocks/smartproxy 21.1.7 → 22.6.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 (155) hide show
  1. package/changelog.md +109 -0
  2. package/dist_rust/rustproxy +0 -0
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/core/utils/shared-security-manager.d.ts +17 -0
  5. package/dist_ts/core/utils/shared-security-manager.js +66 -1
  6. package/dist_ts/index.d.ts +1 -5
  7. package/dist_ts/index.js +3 -9
  8. package/dist_ts/protocols/common/fragment-handler.js +5 -1
  9. package/dist_ts/proxies/http-proxy/default-certificates.d.ts +54 -0
  10. package/dist_ts/proxies/http-proxy/default-certificates.js +127 -0
  11. package/dist_ts/proxies/http-proxy/http-proxy.d.ts +1 -1
  12. package/dist_ts/proxies/http-proxy/http-proxy.js +9 -14
  13. package/dist_ts/proxies/http-proxy/index.d.ts +5 -1
  14. package/dist_ts/proxies/http-proxy/index.js +6 -2
  15. package/dist_ts/proxies/http-proxy/security-manager.d.ts +4 -12
  16. package/dist_ts/proxies/http-proxy/security-manager.js +66 -99
  17. package/dist_ts/proxies/index.d.ts +1 -5
  18. package/dist_ts/proxies/index.js +2 -6
  19. package/dist_ts/proxies/nftables-proxy/index.d.ts +1 -0
  20. package/dist_ts/proxies/nftables-proxy/index.js +2 -1
  21. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +4 -26
  22. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +84 -236
  23. package/dist_ts/proxies/nftables-proxy/utils/index.d.ts +9 -0
  24. package/dist_ts/proxies/nftables-proxy/utils/index.js +12 -0
  25. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.d.ts +66 -0
  26. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.js +131 -0
  27. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.d.ts +39 -0
  28. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.js +112 -0
  29. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.d.ts +59 -0
  30. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.js +130 -0
  31. package/dist_ts/proxies/smart-proxy/certificate-manager.js +4 -3
  32. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +13 -2
  33. package/dist_ts/proxies/smart-proxy/connection-manager.js +16 -6
  34. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +35 -10
  35. package/dist_ts/proxies/smart-proxy/index.d.ts +5 -10
  36. package/dist_ts/proxies/smart-proxy/index.js +7 -13
  37. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -3
  38. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +17 -0
  39. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +72 -9
  40. package/dist_ts/proxies/smart-proxy/route-preprocessor.d.ts +37 -0
  41. package/dist_ts/proxies/smart-proxy/route-preprocessor.js +103 -0
  42. package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +23 -0
  43. package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +104 -0
  44. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +74 -0
  45. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +146 -0
  46. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +49 -0
  47. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +259 -0
  48. package/dist_ts/proxies/smart-proxy/security-manager.d.ts +14 -12
  49. package/dist_ts/proxies/smart-proxy/security-manager.js +80 -74
  50. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +39 -157
  51. package/dist_ts/proxies/smart-proxy/smart-proxy.js +224 -622
  52. package/dist_ts/proxies/smart-proxy/socket-handler-server.d.ts +45 -0
  53. package/dist_ts/proxies/smart-proxy/socket-handler-server.js +253 -0
  54. package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +2 -9
  55. package/dist_ts/proxies/smart-proxy/tls-manager.js +3 -26
  56. package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -1
  57. package/dist_ts/proxies/smart-proxy/utils/index.js +3 -4
  58. package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.d.ts +49 -0
  59. package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.js +108 -0
  60. package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.d.ts +57 -0
  61. package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.js +89 -0
  62. package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.d.ts +17 -0
  63. package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.js +32 -0
  64. package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.d.ts +68 -0
  65. package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.js +117 -0
  66. package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.d.ts +17 -0
  67. package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.js +27 -0
  68. package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.d.ts +63 -0
  69. package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.js +105 -0
  70. package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.d.ts +83 -0
  71. package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.js +126 -0
  72. package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.d.ts +47 -0
  73. package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.js +66 -0
  74. package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.d.ts +70 -0
  75. package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.js +287 -0
  76. package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.d.ts +46 -0
  77. package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.js +67 -0
  78. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +4 -457
  79. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +6 -950
  80. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +2 -2
  81. package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +67 -1
  82. package/dist_ts/proxies/smart-proxy/utils/route-validator.js +251 -3
  83. package/dist_ts/routing/index.d.ts +1 -1
  84. package/dist_ts/routing/index.js +3 -3
  85. package/dist_ts/routing/models/http-types.d.ts +119 -4
  86. package/dist_ts/routing/models/http-types.js +93 -5
  87. package/npmextra.json +12 -6
  88. package/package.json +34 -24
  89. package/readme.hints.md +184 -1
  90. package/readme.md +580 -266
  91. package/ts/00_commitinfo_data.ts +1 -1
  92. package/ts/core/utils/shared-security-manager.ts +98 -13
  93. package/ts/index.ts +4 -12
  94. package/ts/protocols/common/fragment-handler.ts +4 -0
  95. package/ts/proxies/index.ts +1 -9
  96. package/ts/proxies/nftables-proxy/index.ts +1 -0
  97. package/ts/proxies/nftables-proxy/nftables-proxy.ts +116 -290
  98. package/ts/proxies/nftables-proxy/utils/index.ts +38 -0
  99. package/ts/proxies/nftables-proxy/utils/nft-command-executor.ts +162 -0
  100. package/ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts +125 -0
  101. package/ts/proxies/nftables-proxy/utils/nft-rule-validator.ts +156 -0
  102. package/ts/proxies/smart-proxy/index.ts +6 -13
  103. package/ts/proxies/smart-proxy/models/interfaces.ts +6 -5
  104. package/ts/proxies/smart-proxy/route-preprocessor.ts +122 -0
  105. package/ts/proxies/smart-proxy/rust-binary-locator.ts +112 -0
  106. package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +161 -0
  107. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +310 -0
  108. package/ts/proxies/smart-proxy/smart-proxy.ts +282 -800
  109. package/ts/proxies/smart-proxy/socket-handler-server.ts +279 -0
  110. package/ts/proxies/smart-proxy/utils/index.ts +3 -5
  111. package/ts/proxies/smart-proxy/utils/route-helpers/api-helpers.ts +144 -0
  112. package/ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.ts +124 -0
  113. package/ts/proxies/smart-proxy/utils/route-helpers/http-helpers.ts +40 -0
  114. package/ts/proxies/smart-proxy/utils/route-helpers/https-helpers.ts +163 -0
  115. package/ts/proxies/smart-proxy/utils/route-helpers/index.ts +62 -0
  116. package/ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.ts +154 -0
  117. package/ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.ts +202 -0
  118. package/ts/proxies/smart-proxy/utils/route-helpers/security-helpers.ts +96 -0
  119. package/ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.ts +337 -0
  120. package/ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.ts +98 -0
  121. package/ts/proxies/smart-proxy/utils/route-helpers.ts +5 -1302
  122. package/ts/proxies/smart-proxy/utils/route-utils.ts +1 -1
  123. package/ts/proxies/smart-proxy/utils/route-validator.ts +274 -4
  124. package/ts/routing/index.ts +2 -2
  125. package/ts/routing/models/http-types.ts +147 -4
  126. package/ts/proxies/http-proxy/certificate-manager.ts +0 -244
  127. package/ts/proxies/http-proxy/connection-pool.ts +0 -228
  128. package/ts/proxies/http-proxy/context-creator.ts +0 -145
  129. package/ts/proxies/http-proxy/function-cache.ts +0 -279
  130. package/ts/proxies/http-proxy/handlers/index.ts +0 -5
  131. package/ts/proxies/http-proxy/http-proxy.ts +0 -675
  132. package/ts/proxies/http-proxy/http-request-handler.ts +0 -331
  133. package/ts/proxies/http-proxy/http2-request-handler.ts +0 -255
  134. package/ts/proxies/http-proxy/index.ts +0 -13
  135. package/ts/proxies/http-proxy/models/http-types.ts +0 -148
  136. package/ts/proxies/http-proxy/models/index.ts +0 -5
  137. package/ts/proxies/http-proxy/models/types.ts +0 -125
  138. package/ts/proxies/http-proxy/request-handler.ts +0 -878
  139. package/ts/proxies/http-proxy/security-manager.ts +0 -433
  140. package/ts/proxies/http-proxy/websocket-handler.ts +0 -581
  141. package/ts/proxies/smart-proxy/acme-state-manager.ts +0 -112
  142. package/ts/proxies/smart-proxy/cert-store.ts +0 -92
  143. package/ts/proxies/smart-proxy/certificate-manager.ts +0 -894
  144. package/ts/proxies/smart-proxy/connection-manager.ts +0 -796
  145. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +0 -187
  146. package/ts/proxies/smart-proxy/metrics-collector.ts +0 -453
  147. package/ts/proxies/smart-proxy/nftables-manager.ts +0 -271
  148. package/ts/proxies/smart-proxy/port-manager.ts +0 -358
  149. package/ts/proxies/smart-proxy/route-connection-handler.ts +0 -1640
  150. package/ts/proxies/smart-proxy/route-orchestrator.ts +0 -297
  151. package/ts/proxies/smart-proxy/security-manager.ts +0 -257
  152. package/ts/proxies/smart-proxy/throughput-tracker.ts +0 -138
  153. package/ts/proxies/smart-proxy/timeout-manager.ts +0 -196
  154. package/ts/proxies/smart-proxy/tls-manager.ts +0 -207
  155. package/ts/proxies/smart-proxy/utils/route-validators.ts +0 -283
@@ -1,297 +0,0 @@
1
- import { logger } from '../../core/utils/logger.js';
2
- import type { IRouteConfig } from './models/route-types.js';
3
- import type { ILogger } from '../http-proxy/models/types.js';
4
- import { RouteValidator } from './utils/route-validator.js';
5
- import { Mutex } from './utils/mutex.js';
6
- import type { PortManager } from './port-manager.js';
7
- import type { SharedRouteManager as RouteManager } from '../../core/routing/route-manager.js';
8
- import type { HttpProxyBridge } from './http-proxy-bridge.js';
9
- import type { NFTablesManager } from './nftables-manager.js';
10
- import type { SmartCertManager } from './certificate-manager.js';
11
-
12
- /**
13
- * Orchestrates route updates and coordination between components
14
- * Extracted from SmartProxy to reduce class complexity
15
- */
16
- export class RouteOrchestrator {
17
- private routeUpdateLock: Mutex;
18
- private portManager: PortManager;
19
- private routeManager: RouteManager;
20
- private httpProxyBridge: HttpProxyBridge;
21
- private nftablesManager: NFTablesManager;
22
- private certManager: SmartCertManager | null = null;
23
- private logger: ILogger;
24
-
25
- constructor(
26
- portManager: PortManager,
27
- routeManager: RouteManager,
28
- httpProxyBridge: HttpProxyBridge,
29
- nftablesManager: NFTablesManager,
30
- certManager: SmartCertManager | null,
31
- logger: ILogger
32
- ) {
33
- this.portManager = portManager;
34
- this.routeManager = routeManager;
35
- this.httpProxyBridge = httpProxyBridge;
36
- this.nftablesManager = nftablesManager;
37
- this.certManager = certManager;
38
- this.logger = logger;
39
- this.routeUpdateLock = new Mutex();
40
- }
41
-
42
- /**
43
- * Set or update certificate manager reference
44
- */
45
- public setCertManager(certManager: SmartCertManager | null): void {
46
- this.certManager = certManager;
47
- }
48
-
49
- /**
50
- * Get certificate manager reference
51
- */
52
- public getCertManager(): SmartCertManager | null {
53
- return this.certManager;
54
- }
55
-
56
- /**
57
- * Update routes with validation and coordination
58
- */
59
- public async updateRoutes(
60
- oldRoutes: IRouteConfig[],
61
- newRoutes: IRouteConfig[],
62
- options: {
63
- acmePort?: number;
64
- acmeOptions?: any;
65
- acmeState?: any;
66
- globalChallengeRouteActive?: boolean;
67
- createCertificateManager?: (
68
- routes: IRouteConfig[],
69
- certStore: string,
70
- acmeOptions?: any,
71
- initialState?: any
72
- ) => Promise<SmartCertManager>;
73
- verifyChallengeRouteRemoved?: () => Promise<void>;
74
- } = {}
75
- ): Promise<{
76
- portUsageMap: Map<number, Set<string>>;
77
- newChallengeRouteActive: boolean;
78
- newCertManager?: SmartCertManager;
79
- }> {
80
- return this.routeUpdateLock.runExclusive(async () => {
81
- // Validate route configurations
82
- const validation = RouteValidator.validateRoutes(newRoutes);
83
- if (!validation.valid) {
84
- RouteValidator.logValidationErrors(validation.errors);
85
- throw new Error(`Route validation failed: ${validation.errors.size} route(s) have errors`);
86
- }
87
-
88
- // Track port usage before and after updates
89
- const oldPortUsage = this.updatePortUsageMap(oldRoutes);
90
- const newPortUsage = this.updatePortUsageMap(newRoutes);
91
-
92
- // Get the lists of currently listening ports and new ports needed
93
- const currentPorts = new Set(this.portManager.getListeningPorts());
94
- const newPortsSet = new Set(newPortUsage.keys());
95
-
96
- // Log the port usage for debugging
97
- this.logger.debug(`Current listening ports: ${Array.from(currentPorts).join(', ')}`);
98
- this.logger.debug(`Ports needed for new routes: ${Array.from(newPortsSet).join(', ')}`);
99
-
100
- // Find orphaned ports - ports that no longer have any routes
101
- const orphanedPorts = this.findOrphanedPorts(oldPortUsage, newPortUsage);
102
-
103
- // Find new ports that need binding (only ports that we aren't already listening on)
104
- const newBindingPorts = Array.from(newPortsSet).filter(p => !currentPorts.has(p));
105
-
106
- // Check for ACME challenge port to give it special handling
107
- const acmePort = options.acmePort || 80;
108
- const acmePortNeeded = newPortsSet.has(acmePort);
109
- const acmePortListed = newBindingPorts.includes(acmePort);
110
-
111
- if (acmePortNeeded && acmePortListed) {
112
- this.logger.info(`Adding ACME challenge port ${acmePort} to routes`);
113
- }
114
-
115
- // Update NFTables routes
116
- await this.updateNfTablesRoutes(oldRoutes, newRoutes);
117
-
118
- // Update routes in RouteManager
119
- this.routeManager.updateRoutes(newRoutes);
120
-
121
- // Release orphaned ports first to free resources
122
- if (orphanedPorts.length > 0) {
123
- this.logger.info(`Releasing ${orphanedPorts.length} orphaned ports: ${orphanedPorts.join(', ')}`);
124
- await this.portManager.removePorts(orphanedPorts);
125
- }
126
-
127
- // Add new ports if needed
128
- if (newBindingPorts.length > 0) {
129
- this.logger.info(`Binding to ${newBindingPorts.length} new ports: ${newBindingPorts.join(', ')}`);
130
-
131
- // Handle port binding with improved error recovery
132
- try {
133
- await this.portManager.addPorts(newBindingPorts);
134
- } catch (error) {
135
- // Special handling for port binding errors
136
- if ((error as any).code === 'EADDRINUSE') {
137
- const port = (error as any).port || newBindingPorts[0];
138
- const isAcmePort = port === acmePort;
139
-
140
- if (isAcmePort) {
141
- this.logger.warn(`Could not bind to ACME challenge port ${port}. It may be in use by another application.`);
142
-
143
- // Re-throw with more helpful message
144
- throw new Error(
145
- `ACME challenge port ${port} is already in use by another application. ` +
146
- `Configure a different port in settings.acme.port (e.g., 8080) or free up port ${port}.`
147
- );
148
- }
149
- }
150
-
151
- // Re-throw the original error for other cases
152
- throw error;
153
- }
154
- }
155
-
156
- // If HttpProxy is initialized, resync the configurations
157
- if (this.httpProxyBridge.getHttpProxy()) {
158
- await this.httpProxyBridge.syncRoutesToHttpProxy(newRoutes);
159
- }
160
-
161
- // Update certificate manager if needed
162
- let newCertManager: SmartCertManager | undefined;
163
- let newChallengeRouteActive = options.globalChallengeRouteActive || false;
164
-
165
- if (this.certManager && options.createCertificateManager) {
166
- const existingAcmeOptions = this.certManager.getAcmeOptions();
167
- const existingState = this.certManager.getState();
168
-
169
- // Store global state before stopping
170
- newChallengeRouteActive = existingState.challengeRouteActive;
171
-
172
- // Keep certificate manager routes in sync before stopping
173
- this.certManager.setRoutes(newRoutes);
174
-
175
- await this.certManager.stop();
176
-
177
- // Verify the challenge route has been properly removed
178
- if (options.verifyChallengeRouteRemoved) {
179
- await options.verifyChallengeRouteRemoved();
180
- }
181
-
182
- // Create new certificate manager with preserved state
183
- newCertManager = await options.createCertificateManager(
184
- newRoutes,
185
- './certs',
186
- existingAcmeOptions,
187
- { challengeRouteActive: newChallengeRouteActive }
188
- );
189
-
190
- this.certManager = newCertManager;
191
- }
192
-
193
- return {
194
- portUsageMap: newPortUsage,
195
- newChallengeRouteActive,
196
- newCertManager
197
- };
198
- });
199
- }
200
-
201
- /**
202
- * Update port usage map based on the provided routes
203
- */
204
- public updatePortUsageMap(routes: IRouteConfig[]): Map<number, Set<string>> {
205
- const portUsage = new Map<number, Set<string>>();
206
-
207
- for (const route of routes) {
208
- // Get the ports for this route
209
- const portsConfig = Array.isArray(route.match.ports)
210
- ? route.match.ports
211
- : [route.match.ports];
212
-
213
- // Expand port range objects to individual port numbers
214
- const expandedPorts: number[] = [];
215
- for (const portConfig of portsConfig) {
216
- if (typeof portConfig === 'number') {
217
- expandedPorts.push(portConfig);
218
- } else if (typeof portConfig === 'object' && 'from' in portConfig && 'to' in portConfig) {
219
- // Expand the port range
220
- for (let p = portConfig.from; p <= portConfig.to; p++) {
221
- expandedPorts.push(p);
222
- }
223
- }
224
- }
225
-
226
- // Use route name if available, otherwise generate a unique ID
227
- const routeName = route.name || `unnamed_${Math.random().toString(36).substring(2, 9)}`;
228
-
229
- // Add each port to the usage map
230
- for (const port of expandedPorts) {
231
- if (!portUsage.has(port)) {
232
- portUsage.set(port, new Set());
233
- }
234
- portUsage.get(port)!.add(routeName);
235
- }
236
- }
237
-
238
- // Log port usage for debugging
239
- for (const [port, routes] of portUsage.entries()) {
240
- this.logger.debug(`Port ${port} is used by ${routes.size} routes: ${Array.from(routes).join(', ')}`);
241
- }
242
-
243
- return portUsage;
244
- }
245
-
246
- /**
247
- * Find ports that have no routes in the new configuration
248
- */
249
- private findOrphanedPorts(oldUsage: Map<number, Set<string>>, newUsage: Map<number, Set<string>>): number[] {
250
- const orphanedPorts: number[] = [];
251
-
252
- for (const [port, routes] of oldUsage.entries()) {
253
- if (!newUsage.has(port) || newUsage.get(port)!.size === 0) {
254
- orphanedPorts.push(port);
255
- }
256
- }
257
-
258
- return orphanedPorts;
259
- }
260
-
261
- /**
262
- * Update NFTables routes
263
- */
264
- private async updateNfTablesRoutes(oldRoutes: IRouteConfig[], newRoutes: IRouteConfig[]): Promise<void> {
265
- // Get existing routes that use NFTables and update them
266
- const oldNfTablesRoutes = oldRoutes.filter(
267
- r => r.action.forwardingEngine === 'nftables'
268
- );
269
-
270
- const newNfTablesRoutes = newRoutes.filter(
271
- r => r.action.forwardingEngine === 'nftables'
272
- );
273
-
274
- // Update existing NFTables routes
275
- for (const oldRoute of oldNfTablesRoutes) {
276
- const newRoute = newNfTablesRoutes.find(r => r.name === oldRoute.name);
277
-
278
- if (!newRoute) {
279
- // Route was removed
280
- await this.nftablesManager.deprovisionRoute(oldRoute);
281
- } else {
282
- // Route was updated
283
- await this.nftablesManager.updateRoute(oldRoute, newRoute);
284
- }
285
- }
286
-
287
- // Add new NFTables routes
288
- for (const newRoute of newNfTablesRoutes) {
289
- const oldRoute = oldNfTablesRoutes.find(r => r.name === newRoute.name);
290
-
291
- if (!oldRoute) {
292
- // New route
293
- await this.nftablesManager.provisionRoute(newRoute);
294
- }
295
- }
296
- }
297
- }
@@ -1,257 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import type { SmartProxy } from './smart-proxy.js';
3
- import { logger } from '../../core/utils/logger.js';
4
- import { connectionLogDeduplicator } from '../../core/utils/log-deduplicator.js';
5
-
6
- /**
7
- * Handles security aspects like IP tracking, rate limiting, and authorization
8
- */
9
- export class SecurityManager {
10
- private connectionsByIP: Map<string, Set<string>> = new Map();
11
- private connectionRateByIP: Map<string, number[]> = new Map();
12
- private cleanupInterval: NodeJS.Timeout | null = null;
13
-
14
- constructor(private smartProxy: SmartProxy) {
15
- // Start periodic cleanup every 60 seconds
16
- this.startPeriodicCleanup();
17
- }
18
-
19
- /**
20
- * Get connections count by IP
21
- */
22
- public getConnectionCountByIP(ip: string): number {
23
- return this.connectionsByIP.get(ip)?.size || 0;
24
- }
25
-
26
- /**
27
- * Check and update connection rate for an IP
28
- * @returns true if within rate limit, false if exceeding limit
29
- */
30
- public checkConnectionRate(ip: string): boolean {
31
- const now = Date.now();
32
- const minute = 60 * 1000;
33
-
34
- if (!this.connectionRateByIP.has(ip)) {
35
- this.connectionRateByIP.set(ip, [now]);
36
- return true;
37
- }
38
-
39
- // Get timestamps and filter out entries older than 1 minute
40
- const timestamps = this.connectionRateByIP.get(ip)!.filter((time) => now - time < minute);
41
- timestamps.push(now);
42
- this.connectionRateByIP.set(ip, timestamps);
43
-
44
- // Check if rate exceeds limit
45
- return timestamps.length <= this.smartProxy.settings.connectionRateLimitPerMinute!;
46
- }
47
-
48
- /**
49
- * Track connection by IP
50
- */
51
- public trackConnectionByIP(ip: string, connectionId: string): void {
52
- if (!this.connectionsByIP.has(ip)) {
53
- this.connectionsByIP.set(ip, new Set());
54
- }
55
- this.connectionsByIP.get(ip)!.add(connectionId);
56
- }
57
-
58
- /**
59
- * Remove connection tracking for an IP
60
- */
61
- public removeConnectionByIP(ip: string, connectionId: string): void {
62
- if (this.connectionsByIP.has(ip)) {
63
- const connections = this.connectionsByIP.get(ip)!;
64
- connections.delete(connectionId);
65
- if (connections.size === 0) {
66
- this.connectionsByIP.delete(ip);
67
- }
68
- }
69
- }
70
-
71
- /**
72
- * Check if an IP is authorized using security rules
73
- *
74
- * This method is used to determine if an IP is allowed to connect, based on security
75
- * rules configured in the route configuration. The allowed and blocked IPs are
76
- * typically derived from route.security.ipAllowList and ipBlockList.
77
- *
78
- * @param ip - The IP address to check
79
- * @param allowedIPs - Array of allowed IP patterns from security.ipAllowList
80
- * @param blockedIPs - Array of blocked IP patterns from security.ipBlockList
81
- * @returns true if IP is authorized, false if blocked
82
- */
83
- public isIPAuthorized(ip: string, allowedIPs: string[], blockedIPs: string[] = []): boolean {
84
- // Skip IP validation if allowedIPs is empty
85
- if (!ip || (allowedIPs.length === 0 && blockedIPs.length === 0)) {
86
- return true;
87
- }
88
-
89
- // First check if IP is blocked - blocked IPs take precedence
90
- if (blockedIPs.length > 0 && this.isGlobIPMatch(ip, blockedIPs)) {
91
- return false;
92
- }
93
-
94
- // Then check if IP is allowed
95
- return this.isGlobIPMatch(ip, allowedIPs);
96
- }
97
-
98
- /**
99
- * Check if the IP matches any of the glob patterns from security configuration
100
- *
101
- * This method checks IP addresses against glob patterns and handles IPv4/IPv6 normalization.
102
- * It's used to implement IP filtering based on the route.security configuration.
103
- *
104
- * @param ip - The IP address to check
105
- * @param patterns - Array of glob patterns from security.ipAllowList or ipBlockList
106
- * @returns true if IP matches any pattern, false otherwise
107
- */
108
- private isGlobIPMatch(ip: string, patterns: string[]): boolean {
109
- if (!ip || !patterns || patterns.length === 0) return false;
110
-
111
- // Handle IPv4/IPv6 normalization for proper matching
112
- const normalizeIP = (ip: string): string[] => {
113
- if (!ip) return [];
114
- // Handle IPv4-mapped IPv6 addresses (::ffff:127.0.0.1)
115
- if (ip.startsWith('::ffff:')) {
116
- const ipv4 = ip.slice(7);
117
- return [ip, ipv4];
118
- }
119
- // Handle IPv4 addresses by also checking IPv4-mapped form
120
- if (/^\d{1,3}(\.\d{1,3}){3}$/.test(ip)) {
121
- return [ip, `::ffff:${ip}`];
122
- }
123
- return [ip];
124
- };
125
-
126
- // Normalize the IP being checked
127
- const normalizedIPVariants = normalizeIP(ip);
128
- if (normalizedIPVariants.length === 0) return false;
129
-
130
- // Expand shorthand patterns and normalize IPs for consistent comparison
131
- const expandShorthand = (pattern: string): string => {
132
- // Expand shorthand IP patterns like '192.168.*' to '192.168.*.*'
133
- if (pattern.includes('*') && !pattern.includes(':')) {
134
- const parts = pattern.split('.');
135
- while (parts.length < 4) {
136
- parts.push('*');
137
- }
138
- return parts.join('.');
139
- }
140
- return pattern;
141
- };
142
-
143
- const expandedPatterns = patterns.map(expandShorthand).flatMap(normalizeIP);
144
-
145
- // Check for any match between normalized IP variants and patterns
146
- return normalizedIPVariants.some((ipVariant) =>
147
- expandedPatterns.some((pattern) => plugins.minimatch(ipVariant, pattern))
148
- );
149
- }
150
-
151
- /**
152
- * Check if IP should be allowed considering connection rate and max connections
153
- * @returns Object with result and reason
154
- */
155
- public validateIP(ip: string): { allowed: boolean; reason?: string } {
156
- // Check connection count limit
157
- if (
158
- this.smartProxy.settings.maxConnectionsPerIP &&
159
- this.getConnectionCountByIP(ip) >= this.smartProxy.settings.maxConnectionsPerIP
160
- ) {
161
- return {
162
- allowed: false,
163
- reason: `Maximum connections per IP (${this.smartProxy.settings.maxConnectionsPerIP}) exceeded`
164
- };
165
- }
166
-
167
- // Check connection rate limit
168
- if (
169
- this.smartProxy.settings.connectionRateLimitPerMinute &&
170
- !this.checkConnectionRate(ip)
171
- ) {
172
- return {
173
- allowed: false,
174
- reason: `Connection rate limit (${this.smartProxy.settings.connectionRateLimitPerMinute}/min) exceeded`
175
- };
176
- }
177
-
178
- return { allowed: true };
179
- }
180
-
181
- /**
182
- * Clears all IP tracking data (for shutdown)
183
- */
184
- public clearIPTracking(): void {
185
- if (this.cleanupInterval) {
186
- clearInterval(this.cleanupInterval);
187
- this.cleanupInterval = null;
188
- }
189
- this.connectionsByIP.clear();
190
- this.connectionRateByIP.clear();
191
- }
192
-
193
- /**
194
- * Start periodic cleanup of expired data
195
- */
196
- private startPeriodicCleanup(): void {
197
- this.cleanupInterval = setInterval(() => {
198
- this.performCleanup();
199
- }, 60000); // Run every minute
200
-
201
- // Unref the timer so it doesn't keep the process alive
202
- if (this.cleanupInterval.unref) {
203
- this.cleanupInterval.unref();
204
- }
205
- }
206
-
207
- /**
208
- * Perform cleanup of expired rate limits and empty IP entries
209
- */
210
- private performCleanup(): void {
211
- const now = Date.now();
212
- const minute = 60 * 1000;
213
- let cleanedRateLimits = 0;
214
- let cleanedIPs = 0;
215
-
216
- // Clean up expired rate limit timestamps
217
- for (const [ip, timestamps] of this.connectionRateByIP.entries()) {
218
- const validTimestamps = timestamps.filter(time => now - time < minute);
219
-
220
- if (validTimestamps.length === 0) {
221
- // No valid timestamps, remove the IP entry
222
- this.connectionRateByIP.delete(ip);
223
- cleanedRateLimits++;
224
- } else if (validTimestamps.length < timestamps.length) {
225
- // Some timestamps expired, update with valid ones
226
- this.connectionRateByIP.set(ip, validTimestamps);
227
- }
228
- }
229
-
230
- // Clean up IPs with no active connections
231
- for (const [ip, connections] of this.connectionsByIP.entries()) {
232
- if (connections.size === 0) {
233
- this.connectionsByIP.delete(ip);
234
- cleanedIPs++;
235
- }
236
- }
237
-
238
- // Log cleanup stats if anything was cleaned
239
- if (cleanedRateLimits > 0 || cleanedIPs > 0) {
240
- if (this.smartProxy.settings.enableDetailedLogging) {
241
- connectionLogDeduplicator.log(
242
- 'ip-cleanup',
243
- 'debug',
244
- 'IP tracking cleanup completed',
245
- {
246
- cleanedRateLimits,
247
- cleanedIPs,
248
- remainingIPs: this.connectionsByIP.size,
249
- remainingRateLimits: this.connectionRateByIP.size,
250
- component: 'security-manager'
251
- },
252
- 'periodic-cleanup'
253
- );
254
- }
255
- }
256
- }
257
- }
@@ -1,138 +0,0 @@
1
- import type { IThroughputSample, IThroughputData, IThroughputHistoryPoint } from './models/metrics-types.js';
2
-
3
- /**
4
- * Tracks throughput data using time-series sampling
5
- */
6
- export class ThroughputTracker {
7
- private samples: IThroughputSample[] = [];
8
- private readonly maxSamples: number;
9
- private accumulatedBytesIn: number = 0;
10
- private accumulatedBytesOut: number = 0;
11
- private lastSampleTime: number = 0;
12
-
13
- constructor(retentionSeconds: number = 3600) {
14
- // Keep samples for the retention period at 1 sample per second
15
- this.maxSamples = retentionSeconds;
16
- }
17
-
18
- /**
19
- * Record bytes transferred (called on every data transfer)
20
- */
21
- public recordBytes(bytesIn: number, bytesOut: number): void {
22
- this.accumulatedBytesIn += bytesIn;
23
- this.accumulatedBytesOut += bytesOut;
24
- }
25
-
26
- /**
27
- * Take a sample of accumulated bytes (called every second)
28
- */
29
- public takeSample(): void {
30
- const now = Date.now();
31
-
32
- // Record accumulated bytes since last sample
33
- this.samples.push({
34
- timestamp: now,
35
- bytesIn: this.accumulatedBytesIn,
36
- bytesOut: this.accumulatedBytesOut
37
- });
38
-
39
- // Reset accumulators
40
- this.accumulatedBytesIn = 0;
41
- this.accumulatedBytesOut = 0;
42
- this.lastSampleTime = now;
43
-
44
- // Maintain circular buffer - remove oldest samples
45
- if (this.samples.length > this.maxSamples) {
46
- this.samples.shift();
47
- }
48
- }
49
-
50
- /**
51
- * Get throughput rate over specified window (bytes per second)
52
- */
53
- public getRate(windowSeconds: number): IThroughputData {
54
- if (this.samples.length === 0) {
55
- return { in: 0, out: 0 };
56
- }
57
-
58
- const now = Date.now();
59
- const windowStart = now - (windowSeconds * 1000);
60
-
61
- // Find samples within the window
62
- const relevantSamples = this.samples.filter(s => s.timestamp > windowStart);
63
-
64
- if (relevantSamples.length === 0) {
65
- return { in: 0, out: 0 };
66
- }
67
-
68
- // Calculate total bytes in window
69
- const totalBytesIn = relevantSamples.reduce((sum, s) => sum + s.bytesIn, 0);
70
- const totalBytesOut = relevantSamples.reduce((sum, s) => sum + s.bytesOut, 0);
71
-
72
- // Use actual number of seconds covered by samples for accurate rate
73
- const oldestSampleTime = relevantSamples[0].timestamp;
74
- const newestSampleTime = relevantSamples[relevantSamples.length - 1].timestamp;
75
- const actualSeconds = Math.max(1, (newestSampleTime - oldestSampleTime) / 1000 + 1);
76
-
77
- return {
78
- in: Math.round(totalBytesIn / actualSeconds),
79
- out: Math.round(totalBytesOut / actualSeconds)
80
- };
81
- }
82
-
83
- /**
84
- * Get throughput history for specified duration
85
- */
86
- public getHistory(durationSeconds: number): IThroughputHistoryPoint[] {
87
- const now = Date.now();
88
- const startTime = now - (durationSeconds * 1000);
89
-
90
- // Filter samples within duration
91
- const relevantSamples = this.samples.filter(s => s.timestamp > startTime);
92
-
93
- // Convert to history points with per-second rates
94
- const history: IThroughputHistoryPoint[] = [];
95
-
96
- for (let i = 0; i < relevantSamples.length; i++) {
97
- const sample = relevantSamples[i];
98
-
99
- // For the first sample or samples after gaps, we can't calculate rate
100
- if (i === 0 || sample.timestamp - relevantSamples[i - 1].timestamp > 2000) {
101
- history.push({
102
- timestamp: sample.timestamp,
103
- in: sample.bytesIn,
104
- out: sample.bytesOut
105
- });
106
- } else {
107
- // Calculate rate based on time since previous sample
108
- const prevSample = relevantSamples[i - 1];
109
- const timeDelta = (sample.timestamp - prevSample.timestamp) / 1000;
110
-
111
- history.push({
112
- timestamp: sample.timestamp,
113
- in: Math.round(sample.bytesIn / timeDelta),
114
- out: Math.round(sample.bytesOut / timeDelta)
115
- });
116
- }
117
- }
118
-
119
- return history;
120
- }
121
-
122
- /**
123
- * Clear all samples
124
- */
125
- public clear(): void {
126
- this.samples = [];
127
- this.accumulatedBytesIn = 0;
128
- this.accumulatedBytesOut = 0;
129
- this.lastSampleTime = 0;
130
- }
131
-
132
- /**
133
- * Get sample count for debugging
134
- */
135
- public getSampleCount(): number {
136
- return this.samples.length;
137
- }
138
- }