@push.rocks/smartproxy 22.4.2 → 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 (72) hide show
  1. package/changelog.md +28 -0
  2. package/dist_rust/rustproxy +0 -0
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/index.d.ts +1 -5
  5. package/dist_ts/index.js +3 -9
  6. package/dist_ts/protocols/common/fragment-handler.js +5 -1
  7. package/dist_ts/proxies/index.d.ts +1 -5
  8. package/dist_ts/proxies/index.js +2 -6
  9. package/dist_ts/proxies/smart-proxy/index.d.ts +5 -10
  10. package/dist_ts/proxies/smart-proxy/index.js +7 -13
  11. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -2
  12. package/dist_ts/proxies/smart-proxy/route-preprocessor.d.ts +37 -0
  13. package/dist_ts/proxies/smart-proxy/route-preprocessor.js +103 -0
  14. package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +23 -0
  15. package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +104 -0
  16. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +74 -0
  17. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +146 -0
  18. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +49 -0
  19. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +259 -0
  20. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +39 -157
  21. package/dist_ts/proxies/smart-proxy/smart-proxy.js +224 -621
  22. package/dist_ts/proxies/smart-proxy/socket-handler-server.d.ts +45 -0
  23. package/dist_ts/proxies/smart-proxy/socket-handler-server.js +253 -0
  24. package/dist_ts/routing/index.d.ts +1 -1
  25. package/dist_ts/routing/index.js +3 -3
  26. package/dist_ts/routing/models/http-types.d.ts +119 -4
  27. package/dist_ts/routing/models/http-types.js +93 -5
  28. package/package.json +1 -1
  29. package/readme.md +470 -219
  30. package/ts/00_commitinfo_data.ts +1 -1
  31. package/ts/index.ts +4 -12
  32. package/ts/protocols/common/fragment-handler.ts +4 -0
  33. package/ts/proxies/index.ts +1 -9
  34. package/ts/proxies/smart-proxy/index.ts +6 -13
  35. package/ts/proxies/smart-proxy/models/interfaces.ts +6 -4
  36. package/ts/proxies/smart-proxy/route-preprocessor.ts +122 -0
  37. package/ts/proxies/smart-proxy/rust-binary-locator.ts +112 -0
  38. package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +161 -0
  39. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +310 -0
  40. package/ts/proxies/smart-proxy/smart-proxy.ts +282 -798
  41. package/ts/proxies/smart-proxy/socket-handler-server.ts +279 -0
  42. package/ts/routing/index.ts +2 -2
  43. package/ts/routing/models/http-types.ts +147 -4
  44. package/ts/proxies/http-proxy/connection-pool.ts +0 -228
  45. package/ts/proxies/http-proxy/context-creator.ts +0 -145
  46. package/ts/proxies/http-proxy/default-certificates.ts +0 -150
  47. package/ts/proxies/http-proxy/function-cache.ts +0 -279
  48. package/ts/proxies/http-proxy/handlers/index.ts +0 -5
  49. package/ts/proxies/http-proxy/http-proxy.ts +0 -669
  50. package/ts/proxies/http-proxy/http-request-handler.ts +0 -331
  51. package/ts/proxies/http-proxy/http2-request-handler.ts +0 -255
  52. package/ts/proxies/http-proxy/index.ts +0 -18
  53. package/ts/proxies/http-proxy/models/http-types.ts +0 -148
  54. package/ts/proxies/http-proxy/models/index.ts +0 -5
  55. package/ts/proxies/http-proxy/models/types.ts +0 -125
  56. package/ts/proxies/http-proxy/request-handler.ts +0 -878
  57. package/ts/proxies/http-proxy/security-manager.ts +0 -413
  58. package/ts/proxies/http-proxy/websocket-handler.ts +0 -581
  59. package/ts/proxies/smart-proxy/acme-state-manager.ts +0 -112
  60. package/ts/proxies/smart-proxy/cert-store.ts +0 -92
  61. package/ts/proxies/smart-proxy/certificate-manager.ts +0 -895
  62. package/ts/proxies/smart-proxy/connection-manager.ts +0 -809
  63. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +0 -213
  64. package/ts/proxies/smart-proxy/metrics-collector.ts +0 -453
  65. package/ts/proxies/smart-proxy/nftables-manager.ts +0 -271
  66. package/ts/proxies/smart-proxy/port-manager.ts +0 -358
  67. package/ts/proxies/smart-proxy/route-connection-handler.ts +0 -1712
  68. package/ts/proxies/smart-proxy/route-orchestrator.ts +0 -297
  69. package/ts/proxies/smart-proxy/security-manager.ts +0 -269
  70. package/ts/proxies/smart-proxy/throughput-tracker.ts +0 -138
  71. package/ts/proxies/smart-proxy/timeout-manager.ts +0 -196
  72. package/ts/proxies/smart-proxy/tls-manager.ts +0 -171
@@ -1,413 +0,0 @@
1
- import type { ILogger } from './models/types.js';
2
- import type { IRouteConfig } from '../smart-proxy/models/route-types.js';
3
- import type { IRouteContext } from '../../core/models/route-context.js';
4
- import {
5
- isIPAuthorized,
6
- normalizeIP,
7
- parseBasicAuthHeader,
8
- cleanupExpiredRateLimits,
9
- type IRateLimitInfo
10
- } from '../../core/utils/security-utils.js';
11
-
12
- /**
13
- * Manages security features for the HttpProxy
14
- * Implements IP filtering, rate limiting, and authentication.
15
- * Uses shared utilities from security-utils.ts.
16
- */
17
- export class SecurityManager {
18
- // Cache IP filtering results to avoid constant regex matching
19
- private ipFilterCache: Map<string, Map<string, boolean>> = new Map();
20
-
21
- // Store rate limits per route and key
22
- private rateLimits: Map<string, Map<string, IRateLimitInfo>> = new Map();
23
-
24
- // Connection tracking by IP
25
- private connectionsByIP: Map<string, Set<string>> = new Map();
26
- private connectionRateByIP: Map<string, number[]> = new Map();
27
-
28
- constructor(
29
- private logger: ILogger,
30
- private routes: IRouteConfig[] = [],
31
- private maxConnectionsPerIP: number = 100,
32
- private connectionRateLimitPerMinute: number = 300
33
- ) {
34
- // Start periodic cleanup for connection tracking
35
- this.startPeriodicIpCleanup();
36
- }
37
-
38
- /**
39
- * Update the routes configuration
40
- */
41
- public setRoutes(routes: IRouteConfig[]): void {
42
- this.routes = routes;
43
- // Reset caches when routes change
44
- this.ipFilterCache.clear();
45
- }
46
-
47
- /**
48
- * Check if a client is allowed to access a specific route
49
- *
50
- * @param route The route to check access for
51
- * @param context The route context with client information
52
- * @returns True if access is allowed, false otherwise
53
- */
54
- public isAllowed(route: IRouteConfig, context: IRouteContext): boolean {
55
- if (!route.security) {
56
- return true; // No security restrictions
57
- }
58
-
59
- // --- IP filtering ---
60
- if (!this.isIpAllowed(route, context.clientIp)) {
61
- this.logger.debug(`IP ${context.clientIp} is blocked for route ${route.name || 'unnamed'}`);
62
- return false;
63
- }
64
-
65
- // --- Rate limiting ---
66
- if (route.security.rateLimit?.enabled && !this.isWithinRateLimit(route, context)) {
67
- this.logger.debug(`Rate limit exceeded for route ${route.name || 'unnamed'}`);
68
- return false;
69
- }
70
-
71
- // --- Basic Auth (handled at HTTP level) ---
72
- // Basic auth is not checked here as it requires HTTP headers
73
- // and is handled in the RequestHandler
74
-
75
- return true;
76
- }
77
-
78
- /**
79
- * Check if an IP is allowed based on route security settings
80
- */
81
- private isIpAllowed(route: IRouteConfig, clientIp: string): boolean {
82
- if (!route.security) {
83
- return true; // No security restrictions
84
- }
85
-
86
- const routeId = route.name || 'unnamed';
87
-
88
- // Check cache first
89
- if (!this.ipFilterCache.has(routeId)) {
90
- this.ipFilterCache.set(routeId, new Map());
91
- }
92
-
93
- const routeCache = this.ipFilterCache.get(routeId)!;
94
- if (routeCache.has(clientIp)) {
95
- return routeCache.get(clientIp)!;
96
- }
97
-
98
- // Use shared utility for IP authorization
99
- const allowed = isIPAuthorized(
100
- clientIp,
101
- route.security.ipAllowList,
102
- route.security.ipBlockList
103
- );
104
-
105
- // Cache the result
106
- routeCache.set(clientIp, allowed);
107
-
108
- return allowed;
109
- }
110
-
111
- /**
112
- * Check if request is within rate limit
113
- */
114
- private isWithinRateLimit(route: IRouteConfig, context: IRouteContext): boolean {
115
- if (!route.security?.rateLimit?.enabled) {
116
- return true;
117
- }
118
-
119
- const rateLimit = route.security.rateLimit;
120
- const routeId = route.name || 'unnamed';
121
-
122
- // Determine rate limit key (by IP, path, or header)
123
- let key = context.clientIp; // Default to IP
124
-
125
- if (rateLimit.keyBy === 'path' && context.path) {
126
- key = `${context.clientIp}:${context.path}`;
127
- } else if (rateLimit.keyBy === 'header' && rateLimit.headerName && context.headers) {
128
- const headerValue = context.headers[rateLimit.headerName.toLowerCase()];
129
- if (headerValue) {
130
- key = `${context.clientIp}:${headerValue}`;
131
- }
132
- }
133
-
134
- // Get or create rate limit tracking for this route
135
- if (!this.rateLimits.has(routeId)) {
136
- this.rateLimits.set(routeId, new Map());
137
- }
138
-
139
- const routeLimits = this.rateLimits.get(routeId)!;
140
- const now = Date.now();
141
-
142
- // Get or create rate limit tracking for this key
143
- let limit = routeLimits.get(key);
144
- if (!limit || limit.expiry < now) {
145
- // Create new rate limit or reset expired one
146
- limit = {
147
- count: 1,
148
- expiry: now + (rateLimit.window * 1000)
149
- };
150
- routeLimits.set(key, limit);
151
- return true;
152
- }
153
-
154
- // Increment the counter
155
- limit.count++;
156
-
157
- // Check if rate limit is exceeded
158
- return limit.count <= rateLimit.maxRequests;
159
- }
160
-
161
- /**
162
- * Clean up expired rate limits
163
- * Should be called periodically to prevent memory leaks
164
- */
165
- public cleanupExpiredRateLimits(): void {
166
- cleanupExpiredRateLimits(this.rateLimits, {
167
- info: this.logger.info.bind(this.logger),
168
- warn: this.logger.warn.bind(this.logger),
169
- error: this.logger.error.bind(this.logger),
170
- debug: this.logger.debug?.bind(this.logger)
171
- });
172
- }
173
-
174
- /**
175
- * Check basic auth credentials
176
- *
177
- * @param route The route to check auth for
178
- * @param username The provided username
179
- * @param password The provided password
180
- * @returns True if credentials are valid, false otherwise
181
- */
182
- public checkBasicAuth(route: IRouteConfig, username: string, password: string): boolean {
183
- if (!route.security?.basicAuth?.enabled) {
184
- return true;
185
- }
186
-
187
- const basicAuth = route.security.basicAuth;
188
-
189
- // Check credentials against configured users
190
- for (const user of basicAuth.users) {
191
- if (user.username === username && user.password === password) {
192
- return true;
193
- }
194
- }
195
-
196
- return false;
197
- }
198
-
199
- /**
200
- * Verify a JWT token
201
- *
202
- * @param route The route to verify the token for
203
- * @param token The JWT token to verify
204
- * @returns True if the token is valid, false otherwise
205
- */
206
- public verifyJwtToken(route: IRouteConfig, token: string): boolean {
207
- if (!route.security?.jwtAuth?.enabled) {
208
- return true;
209
- }
210
-
211
- try {
212
- const jwtAuth = route.security.jwtAuth;
213
-
214
- // Verify structure
215
- const parts = token.split('.');
216
- if (parts.length !== 3) {
217
- return false;
218
- }
219
-
220
- // Decode payload
221
- const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString());
222
-
223
- // Check expiration
224
- if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) {
225
- return false;
226
- }
227
-
228
- // Check issuer
229
- if (jwtAuth.issuer && payload.iss !== jwtAuth.issuer) {
230
- return false;
231
- }
232
-
233
- // Check audience
234
- if (jwtAuth.audience && payload.aud !== jwtAuth.audience) {
235
- return false;
236
- }
237
-
238
- // Note: In a real implementation, you'd also verify the signature
239
- // using the secret and algorithm specified in jwtAuth
240
-
241
- return true;
242
- } catch (err) {
243
- this.logger.error(`Error verifying JWT: ${err}`);
244
- return false;
245
- }
246
- }
247
-
248
- /**
249
- * Get connections count by IP (checks normalized variants)
250
- */
251
- public getConnectionCountByIP(ip: string): number {
252
- // Check all normalized variants of the IP
253
- const variants = normalizeIP(ip);
254
- for (const variant of variants) {
255
- const connections = this.connectionsByIP.get(variant);
256
- if (connections) {
257
- return connections.size;
258
- }
259
- }
260
- return 0;
261
- }
262
-
263
- /**
264
- * Check and update connection rate for an IP
265
- * @returns true if within rate limit, false if exceeding limit
266
- */
267
- public checkConnectionRate(ip: string): boolean {
268
- const now = Date.now();
269
- const minute = 60 * 1000;
270
-
271
- // Find existing rate tracking (check normalized variants)
272
- const variants = normalizeIP(ip);
273
- let existingKey: string | null = null;
274
- for (const variant of variants) {
275
- if (this.connectionRateByIP.has(variant)) {
276
- existingKey = variant;
277
- break;
278
- }
279
- }
280
-
281
- const key = existingKey || ip;
282
-
283
- if (!this.connectionRateByIP.has(key)) {
284
- this.connectionRateByIP.set(key, [now]);
285
- return true;
286
- }
287
-
288
- // Get timestamps and filter out entries older than 1 minute
289
- const timestamps = this.connectionRateByIP.get(key)!.filter((time) => now - time < minute);
290
- timestamps.push(now);
291
- this.connectionRateByIP.set(key, timestamps);
292
-
293
- // Check if rate exceeds limit
294
- return timestamps.length <= this.connectionRateLimitPerMinute;
295
- }
296
-
297
- /**
298
- * Track connection by IP
299
- */
300
- public trackConnectionByIP(ip: string, connectionId: string): void {
301
- // Check if any variant already exists
302
- const variants = normalizeIP(ip);
303
- let existingKey: string | null = null;
304
-
305
- for (const variant of variants) {
306
- if (this.connectionsByIP.has(variant)) {
307
- existingKey = variant;
308
- break;
309
- }
310
- }
311
-
312
- const key = existingKey || ip;
313
- if (!this.connectionsByIP.has(key)) {
314
- this.connectionsByIP.set(key, new Set());
315
- }
316
- this.connectionsByIP.get(key)!.add(connectionId);
317
- }
318
-
319
- /**
320
- * Remove connection tracking for an IP
321
- */
322
- public removeConnectionByIP(ip: string, connectionId: string): void {
323
- // Check all variants to find where the connection is tracked
324
- const variants = normalizeIP(ip);
325
-
326
- for (const variant of variants) {
327
- if (this.connectionsByIP.has(variant)) {
328
- const connections = this.connectionsByIP.get(variant)!;
329
- connections.delete(connectionId);
330
- if (connections.size === 0) {
331
- this.connectionsByIP.delete(variant);
332
- }
333
- break;
334
- }
335
- }
336
- }
337
-
338
- /**
339
- * Check if IP should be allowed considering connection rate and max connections
340
- * @returns Object with result and reason
341
- */
342
- public validateIP(ip: string): { allowed: boolean; reason?: string } {
343
- // Check connection count limit
344
- if (this.getConnectionCountByIP(ip) >= this.maxConnectionsPerIP) {
345
- return {
346
- allowed: false,
347
- reason: `Maximum connections per IP (${this.maxConnectionsPerIP}) exceeded`
348
- };
349
- }
350
-
351
- // Check connection rate limit
352
- if (!this.checkConnectionRate(ip)) {
353
- return {
354
- allowed: false,
355
- reason: `Connection rate limit (${this.connectionRateLimitPerMinute}/min) exceeded`
356
- };
357
- }
358
-
359
- return { allowed: true };
360
- }
361
-
362
- /**
363
- * Clears all IP tracking data (for shutdown)
364
- */
365
- public clearIPTracking(): void {
366
- this.connectionsByIP.clear();
367
- this.connectionRateByIP.clear();
368
- }
369
-
370
- /**
371
- * Start periodic cleanup of IP tracking data
372
- */
373
- private startPeriodicIpCleanup(): void {
374
- // Clean up IP tracking data every minute
375
- setInterval(() => {
376
- this.performIpCleanup();
377
- }, 60000).unref();
378
- }
379
-
380
- /**
381
- * Perform cleanup of expired IP data
382
- */
383
- private performIpCleanup(): void {
384
- const now = Date.now();
385
- const minute = 60 * 1000;
386
- let cleanedRateLimits = 0;
387
- let cleanedIPs = 0;
388
-
389
- // Clean up expired rate limit timestamps
390
- for (const [ip, timestamps] of this.connectionRateByIP.entries()) {
391
- const validTimestamps = timestamps.filter((time) => now - time < minute);
392
-
393
- if (validTimestamps.length === 0) {
394
- this.connectionRateByIP.delete(ip);
395
- cleanedRateLimits++;
396
- } else if (validTimestamps.length < timestamps.length) {
397
- this.connectionRateByIP.set(ip, validTimestamps);
398
- }
399
- }
400
-
401
- // Clean up IPs with no active connections
402
- for (const [ip, connections] of this.connectionsByIP.entries()) {
403
- if (connections.size === 0) {
404
- this.connectionsByIP.delete(ip);
405
- cleanedIPs++;
406
- }
407
- }
408
-
409
- if (cleanedRateLimits > 0 || cleanedIPs > 0) {
410
- this.logger.debug(`IP cleanup: removed ${cleanedIPs} IPs and ${cleanedRateLimits} rate limits`);
411
- }
412
- }
413
- }