@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,433 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import type { ILogger } from './models/types.js';
3
- import type { IRouteConfig } from '../smart-proxy/models/route-types.js';
4
- import type { IRouteContext } from '../../core/models/route-context.js';
5
-
6
- /**
7
- * Manages security features for the NetworkProxy
8
- * Implements Phase 5.4: Security features like IP filtering and rate limiting
9
- */
10
- export class SecurityManager {
11
- // Cache IP filtering results to avoid constant regex matching
12
- private ipFilterCache: Map<string, Map<string, boolean>> = new Map();
13
-
14
- // Store rate limits per route and key
15
- private rateLimits: Map<string, Map<string, { count: number, expiry: number }>> = new Map();
16
-
17
- // Connection tracking by IP
18
- private connectionsByIP: Map<string, Set<string>> = new Map();
19
- private connectionRateByIP: Map<string, number[]> = new Map();
20
-
21
- constructor(private logger: ILogger, private routes: IRouteConfig[] = [], private maxConnectionsPerIP: number = 100, private connectionRateLimitPerMinute: number = 300) {
22
- // Start periodic cleanup for connection tracking
23
- this.startPeriodicIpCleanup();
24
- }
25
-
26
- /**
27
- * Update the routes configuration
28
- */
29
- public setRoutes(routes: IRouteConfig[]): void {
30
- this.routes = routes;
31
- // Reset caches when routes change
32
- this.ipFilterCache.clear();
33
- }
34
-
35
- /**
36
- * Check if a client is allowed to access a specific route
37
- *
38
- * @param route The route to check access for
39
- * @param context The route context with client information
40
- * @returns True if access is allowed, false otherwise
41
- */
42
- public isAllowed(route: IRouteConfig, context: IRouteContext): boolean {
43
- if (!route.security) {
44
- return true; // No security restrictions
45
- }
46
-
47
- // --- IP filtering ---
48
- if (!this.isIpAllowed(route, context.clientIp)) {
49
- this.logger.debug(`IP ${context.clientIp} is blocked for route ${route.name || route.id || 'unnamed'}`);
50
- return false;
51
- }
52
-
53
- // --- Rate limiting ---
54
- if (route.security.rateLimit?.enabled && !this.isWithinRateLimit(route, context)) {
55
- this.logger.debug(`Rate limit exceeded for route ${route.name || route.id || 'unnamed'}`);
56
- return false;
57
- }
58
-
59
- // --- Basic Auth (handled at HTTP level) ---
60
- // Basic auth is not checked here as it requires HTTP headers
61
- // and is handled in the RequestHandler
62
-
63
- return true;
64
- }
65
-
66
- /**
67
- * Check if an IP is allowed based on route security settings
68
- */
69
- private isIpAllowed(route: IRouteConfig, clientIp: string): boolean {
70
- if (!route.security) {
71
- return true; // No security restrictions
72
- }
73
-
74
- const routeId = route.id || route.name || 'unnamed';
75
-
76
- // Check cache first
77
- if (!this.ipFilterCache.has(routeId)) {
78
- this.ipFilterCache.set(routeId, new Map());
79
- }
80
-
81
- const routeCache = this.ipFilterCache.get(routeId)!;
82
- if (routeCache.has(clientIp)) {
83
- return routeCache.get(clientIp)!;
84
- }
85
-
86
- let allowed = true;
87
-
88
- // Check block list first (deny has priority over allow)
89
- if (route.security.ipBlockList && route.security.ipBlockList.length > 0) {
90
- if (this.ipMatchesPattern(clientIp, route.security.ipBlockList)) {
91
- allowed = false;
92
- }
93
- }
94
-
95
- // Then check allow list (overrides block list if specified)
96
- if (route.security.ipAllowList && route.security.ipAllowList.length > 0) {
97
- // If allow list is specified, IP must match an entry to be allowed
98
- allowed = this.ipMatchesPattern(clientIp, route.security.ipAllowList);
99
- }
100
-
101
- // Cache the result
102
- routeCache.set(clientIp, allowed);
103
-
104
- return allowed;
105
- }
106
-
107
- /**
108
- * Check if IP matches any pattern in the list
109
- */
110
- private ipMatchesPattern(ip: string, patterns: string[]): boolean {
111
- for (const pattern of patterns) {
112
- // CIDR notation
113
- if (pattern.includes('/')) {
114
- if (this.ipMatchesCidr(ip, pattern)) {
115
- return true;
116
- }
117
- }
118
- // Wildcard notation
119
- else if (pattern.includes('*')) {
120
- const regex = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
121
- if (regex.test(ip)) {
122
- return true;
123
- }
124
- }
125
- // Exact match
126
- else if (pattern === ip) {
127
- return true;
128
- }
129
- }
130
- return false;
131
- }
132
-
133
- /**
134
- * Check if IP matches CIDR notation
135
- * Very basic implementation - for production use, consider a dedicated IP library
136
- */
137
- private ipMatchesCidr(ip: string, cidr: string): boolean {
138
- try {
139
- const [subnet, bits] = cidr.split('/');
140
- const mask = parseInt(bits, 10);
141
-
142
- // Convert IP to numeric format
143
- const ipParts = ip.split('.').map(part => parseInt(part, 10));
144
- const subnetParts = subnet.split('.').map(part => parseInt(part, 10));
145
-
146
- // Calculate the numeric IP and subnet
147
- const ipNum = (ipParts[0] << 24) | (ipParts[1] << 16) | (ipParts[2] << 8) | ipParts[3];
148
- const subnetNum = (subnetParts[0] << 24) | (subnetParts[1] << 16) | (subnetParts[2] << 8) | subnetParts[3];
149
-
150
- // Calculate the mask
151
- const maskNum = ~((1 << (32 - mask)) - 1);
152
-
153
- // Check if IP is in subnet
154
- return (ipNum & maskNum) === (subnetNum & maskNum);
155
- } catch (e) {
156
- this.logger.error(`Invalid CIDR notation: ${cidr}`);
157
- return false;
158
- }
159
- }
160
-
161
- /**
162
- * Check if request is within rate limit
163
- */
164
- private isWithinRateLimit(route: IRouteConfig, context: IRouteContext): boolean {
165
- if (!route.security?.rateLimit?.enabled) {
166
- return true;
167
- }
168
-
169
- const rateLimit = route.security.rateLimit;
170
- const routeId = route.id || route.name || 'unnamed';
171
-
172
- // Determine rate limit key (by IP, path, or header)
173
- let key = context.clientIp; // Default to IP
174
-
175
- if (rateLimit.keyBy === 'path' && context.path) {
176
- key = `${context.clientIp}:${context.path}`;
177
- } else if (rateLimit.keyBy === 'header' && rateLimit.headerName && context.headers) {
178
- const headerValue = context.headers[rateLimit.headerName.toLowerCase()];
179
- if (headerValue) {
180
- key = `${context.clientIp}:${headerValue}`;
181
- }
182
- }
183
-
184
- // Get or create rate limit tracking for this route
185
- if (!this.rateLimits.has(routeId)) {
186
- this.rateLimits.set(routeId, new Map());
187
- }
188
-
189
- const routeLimits = this.rateLimits.get(routeId)!;
190
- const now = Date.now();
191
-
192
- // Get or create rate limit tracking for this key
193
- let limit = routeLimits.get(key);
194
- if (!limit || limit.expiry < now) {
195
- // Create new rate limit or reset expired one
196
- limit = {
197
- count: 1,
198
- expiry: now + (rateLimit.window * 1000)
199
- };
200
- routeLimits.set(key, limit);
201
- return true;
202
- }
203
-
204
- // Increment the counter
205
- limit.count++;
206
-
207
- // Check if rate limit is exceeded
208
- return limit.count <= rateLimit.maxRequests;
209
- }
210
-
211
- /**
212
- * Clean up expired rate limits
213
- * Should be called periodically to prevent memory leaks
214
- */
215
- public cleanupExpiredRateLimits(): void {
216
- const now = Date.now();
217
- for (const [routeId, routeLimits] of this.rateLimits.entries()) {
218
- let removed = 0;
219
- for (const [key, limit] of routeLimits.entries()) {
220
- if (limit.expiry < now) {
221
- routeLimits.delete(key);
222
- removed++;
223
- }
224
- }
225
- if (removed > 0) {
226
- this.logger.debug(`Cleaned up ${removed} expired rate limits for route ${routeId}`);
227
- }
228
- }
229
- }
230
-
231
- /**
232
- * Check basic auth credentials
233
- *
234
- * @param route The route to check auth for
235
- * @param username The provided username
236
- * @param password The provided password
237
- * @returns True if credentials are valid, false otherwise
238
- */
239
- public checkBasicAuth(route: IRouteConfig, username: string, password: string): boolean {
240
- if (!route.security?.basicAuth?.enabled) {
241
- return true;
242
- }
243
-
244
- const basicAuth = route.security.basicAuth;
245
-
246
- // Check credentials against configured users
247
- for (const user of basicAuth.users) {
248
- if (user.username === username && user.password === password) {
249
- return true;
250
- }
251
- }
252
-
253
- return false;
254
- }
255
-
256
- /**
257
- * Verify a JWT token
258
- *
259
- * @param route The route to verify the token for
260
- * @param token The JWT token to verify
261
- * @returns True if the token is valid, false otherwise
262
- */
263
- public verifyJwtToken(route: IRouteConfig, token: string): boolean {
264
- if (!route.security?.jwtAuth?.enabled) {
265
- return true;
266
- }
267
-
268
- try {
269
- // This is a simplified version - in production you'd use a proper JWT library
270
- const jwtAuth = route.security.jwtAuth;
271
-
272
- // Verify structure
273
- const parts = token.split('.');
274
- if (parts.length !== 3) {
275
- return false;
276
- }
277
-
278
- // Decode payload
279
- const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString());
280
-
281
- // Check expiration
282
- if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) {
283
- return false;
284
- }
285
-
286
- // Check issuer
287
- if (jwtAuth.issuer && payload.iss !== jwtAuth.issuer) {
288
- return false;
289
- }
290
-
291
- // Check audience
292
- if (jwtAuth.audience && payload.aud !== jwtAuth.audience) {
293
- return false;
294
- }
295
-
296
- // In a real implementation, you'd also verify the signature
297
- // using the secret and algorithm specified in jwtAuth
298
-
299
- return true;
300
- } catch (err) {
301
- this.logger.error(`Error verifying JWT: ${err}`);
302
- return false;
303
- }
304
- }
305
-
306
- /**
307
- * Get connections count by IP
308
- */
309
- public getConnectionCountByIP(ip: string): number {
310
- return this.connectionsByIP.get(ip)?.size || 0;
311
- }
312
-
313
- /**
314
- * Check and update connection rate for an IP
315
- * @returns true if within rate limit, false if exceeding limit
316
- */
317
- public checkConnectionRate(ip: string): boolean {
318
- const now = Date.now();
319
- const minute = 60 * 1000;
320
-
321
- if (!this.connectionRateByIP.has(ip)) {
322
- this.connectionRateByIP.set(ip, [now]);
323
- return true;
324
- }
325
-
326
- // Get timestamps and filter out entries older than 1 minute
327
- const timestamps = this.connectionRateByIP.get(ip)!.filter((time) => now - time < minute);
328
- timestamps.push(now);
329
- this.connectionRateByIP.set(ip, timestamps);
330
-
331
- // Check if rate exceeds limit
332
- return timestamps.length <= this.connectionRateLimitPerMinute;
333
- }
334
-
335
- /**
336
- * Track connection by IP
337
- */
338
- public trackConnectionByIP(ip: string, connectionId: string): void {
339
- if (!this.connectionsByIP.has(ip)) {
340
- this.connectionsByIP.set(ip, new Set());
341
- }
342
- this.connectionsByIP.get(ip)!.add(connectionId);
343
- }
344
-
345
- /**
346
- * Remove connection tracking for an IP
347
- */
348
- public removeConnectionByIP(ip: string, connectionId: string): void {
349
- if (this.connectionsByIP.has(ip)) {
350
- const connections = this.connectionsByIP.get(ip)!;
351
- connections.delete(connectionId);
352
- if (connections.size === 0) {
353
- this.connectionsByIP.delete(ip);
354
- }
355
- }
356
- }
357
-
358
- /**
359
- * Check if IP should be allowed considering connection rate and max connections
360
- * @returns Object with result and reason
361
- */
362
- public validateIP(ip: string): { allowed: boolean; reason?: string } {
363
- // Check connection count limit
364
- if (this.getConnectionCountByIP(ip) >= this.maxConnectionsPerIP) {
365
- return {
366
- allowed: false,
367
- reason: `Maximum connections per IP (${this.maxConnectionsPerIP}) exceeded`
368
- };
369
- }
370
-
371
- // Check connection rate limit
372
- if (!this.checkConnectionRate(ip)) {
373
- return {
374
- allowed: false,
375
- reason: `Connection rate limit (${this.connectionRateLimitPerMinute}/min) exceeded`
376
- };
377
- }
378
-
379
- return { allowed: true };
380
- }
381
-
382
- /**
383
- * Clears all IP tracking data (for shutdown)
384
- */
385
- public clearIPTracking(): void {
386
- this.connectionsByIP.clear();
387
- this.connectionRateByIP.clear();
388
- }
389
-
390
- /**
391
- * Start periodic cleanup of IP tracking data
392
- */
393
- private startPeriodicIpCleanup(): void {
394
- // Clean up IP tracking data every minute
395
- setInterval(() => {
396
- this.performIpCleanup();
397
- }, 60000).unref();
398
- }
399
-
400
- /**
401
- * Perform cleanup of expired IP data
402
- */
403
- private performIpCleanup(): void {
404
- const now = Date.now();
405
- const minute = 60 * 1000;
406
- let cleanedRateLimits = 0;
407
- let cleanedIPs = 0;
408
-
409
- // Clean up expired rate limit timestamps
410
- for (const [ip, timestamps] of this.connectionRateByIP.entries()) {
411
- const validTimestamps = timestamps.filter(time => now - time < minute);
412
-
413
- if (validTimestamps.length === 0) {
414
- this.connectionRateByIP.delete(ip);
415
- cleanedRateLimits++;
416
- } else if (validTimestamps.length < timestamps.length) {
417
- this.connectionRateByIP.set(ip, validTimestamps);
418
- }
419
- }
420
-
421
- // Clean up IPs with no active connections
422
- for (const [ip, connections] of this.connectionsByIP.entries()) {
423
- if (connections.size === 0) {
424
- this.connectionsByIP.delete(ip);
425
- cleanedIPs++;
426
- }
427
- }
428
-
429
- if (cleanedRateLimits > 0 || cleanedIPs > 0) {
430
- this.logger.debug(`IP cleanup: removed ${cleanedIPs} IPs and ${cleanedRateLimits} rate limits`);
431
- }
432
- }
433
- }