@push.rocks/smartproxy 19.6.13 → 19.6.14
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.
- package/dist_ts/core/utils/log-deduplicator.d.ts +36 -0
- package/dist_ts/core/utils/log-deduplicator.js +224 -0
- package/dist_ts/core/utils/shared-security-manager.d.ts +2 -1
- package/dist_ts/core/utils/shared-security-manager.js +22 -2
- package/dist_ts/proxies/http-proxy/http-proxy.d.ts +1 -0
- package/dist_ts/proxies/http-proxy/http-proxy.js +94 -9
- package/dist_ts/proxies/http-proxy/models/types.d.ts +2 -0
- package/dist_ts/proxies/http-proxy/models/types.js +1 -1
- package/dist_ts/proxies/http-proxy/security-manager.d.ts +42 -1
- package/dist_ts/proxies/http-proxy/security-manager.js +121 -2
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +14 -0
- package/dist_ts/proxies/smart-proxy/connection-manager.js +74 -26
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +5 -1
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +1 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +24 -8
- package/dist_ts/proxies/smart-proxy/security-manager.d.ts +9 -0
- package/dist_ts/proxies/smart-proxy/security-manager.js +63 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +4 -1
- package/package.json +1 -1
- package/readme.hints.md +80 -1
- package/readme.plan.md +34 -353
- package/ts/core/utils/log-deduplicator.ts +280 -0
- package/ts/core/utils/shared-security-manager.ts +24 -1
- package/ts/proxies/http-proxy/http-proxy.ts +129 -9
- package/ts/proxies/http-proxy/models/types.ts +4 -0
- package/ts/proxies/http-proxy/security-manager.ts +136 -1
- package/ts/proxies/smart-proxy/connection-manager.ts +93 -27
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +5 -0
- package/ts/proxies/smart-proxy/models/interfaces.ts +1 -0
- package/ts/proxies/smart-proxy/route-connection-handler.ts +45 -14
- package/ts/proxies/smart-proxy/security-manager.ts +76 -1
- package/ts/proxies/smart-proxy/smart-proxy.ts +4 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
|
|
3
3
|
import { logger } from '../../core/utils/logger.js';
|
|
4
|
+
import { connectionLogDeduplicator } from '../../core/utils/log-deduplicator.js';
|
|
4
5
|
// Route checking functions have been removed
|
|
5
6
|
import type { IRouteConfig, IRouteAction } from './models/route-types.js';
|
|
6
7
|
import type { IRouteContext } from '../../core/models/route-context.js';
|
|
@@ -563,12 +564,20 @@ export class RouteConnectionHandler {
|
|
|
563
564
|
);
|
|
564
565
|
|
|
565
566
|
if (!isIPAllowed) {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
567
|
+
// Deduplicated logging for route IP blocks
|
|
568
|
+
connectionLogDeduplicator.log(
|
|
569
|
+
'ip-rejected',
|
|
570
|
+
'warn',
|
|
571
|
+
`IP blocked by route security`,
|
|
572
|
+
{
|
|
573
|
+
connectionId,
|
|
574
|
+
remoteIP,
|
|
575
|
+
routeName: route.name || 'unnamed',
|
|
576
|
+
reason: 'route-ip-blocked',
|
|
577
|
+
component: 'route-handler'
|
|
578
|
+
},
|
|
579
|
+
remoteIP
|
|
580
|
+
);
|
|
572
581
|
socket.end();
|
|
573
582
|
this.smartProxy.connectionManager.cleanupConnection(record, 'route_ip_blocked');
|
|
574
583
|
return;
|
|
@@ -577,14 +586,28 @@ export class RouteConnectionHandler {
|
|
|
577
586
|
|
|
578
587
|
// Check max connections per route
|
|
579
588
|
if (route.security.maxConnections !== undefined) {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
589
|
+
const routeId = route.id || route.name || 'unnamed';
|
|
590
|
+
const currentConnections = this.smartProxy.connectionManager.getConnectionCountByRoute(routeId);
|
|
591
|
+
|
|
592
|
+
if (currentConnections >= route.security.maxConnections) {
|
|
593
|
+
// Deduplicated logging for route connection limits
|
|
594
|
+
connectionLogDeduplicator.log(
|
|
595
|
+
'connection-rejected',
|
|
596
|
+
'warn',
|
|
597
|
+
`Route connection limit reached`,
|
|
598
|
+
{
|
|
599
|
+
connectionId,
|
|
600
|
+
routeName: route.name,
|
|
601
|
+
currentConnections,
|
|
602
|
+
maxConnections: route.security.maxConnections,
|
|
603
|
+
reason: 'route-limit',
|
|
604
|
+
component: 'route-handler'
|
|
605
|
+
},
|
|
606
|
+
`route-limit-${route.name}`
|
|
607
|
+
);
|
|
608
|
+
socket.end();
|
|
609
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'route_connection_limit');
|
|
610
|
+
return;
|
|
588
611
|
}
|
|
589
612
|
}
|
|
590
613
|
|
|
@@ -642,6 +665,10 @@ export class RouteConnectionHandler {
|
|
|
642
665
|
|
|
643
666
|
// Store the route config in the connection record for metrics and other uses
|
|
644
667
|
record.routeConfig = route;
|
|
668
|
+
record.routeId = route.id || route.name || 'unnamed';
|
|
669
|
+
|
|
670
|
+
// Track connection by route
|
|
671
|
+
this.smartProxy.connectionManager.trackConnectionByRoute(record.routeId, record.id);
|
|
645
672
|
|
|
646
673
|
// Check if this route uses NFTables for forwarding
|
|
647
674
|
if (action.forwardingEngine === 'nftables') {
|
|
@@ -960,6 +987,10 @@ export class RouteConnectionHandler {
|
|
|
960
987
|
|
|
961
988
|
// Store the route config in the connection record for metrics and other uses
|
|
962
989
|
record.routeConfig = route;
|
|
990
|
+
record.routeId = route.id || route.name || 'unnamed';
|
|
991
|
+
|
|
992
|
+
// Track connection by route
|
|
993
|
+
this.smartProxy.connectionManager.trackConnectionByRoute(record.routeId, record.id);
|
|
963
994
|
|
|
964
995
|
if (!route.action.socketHandler) {
|
|
965
996
|
logger.log('error', 'socket-handler action missing socketHandler function', {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
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';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Handles security aspects like IP tracking, rate limiting, and authorization
|
|
@@ -7,8 +9,12 @@ import type { SmartProxy } from './smart-proxy.js';
|
|
|
7
9
|
export class SecurityManager {
|
|
8
10
|
private connectionsByIP: Map<string, Set<string>> = new Map();
|
|
9
11
|
private connectionRateByIP: Map<string, number[]> = new Map();
|
|
12
|
+
private cleanupInterval: NodeJS.Timeout | null = null;
|
|
10
13
|
|
|
11
|
-
constructor(private smartProxy: SmartProxy) {
|
|
14
|
+
constructor(private smartProxy: SmartProxy) {
|
|
15
|
+
// Start periodic cleanup every 60 seconds
|
|
16
|
+
this.startPeriodicCleanup();
|
|
17
|
+
}
|
|
12
18
|
|
|
13
19
|
/**
|
|
14
20
|
* Get connections count by IP
|
|
@@ -164,7 +170,76 @@ export class SecurityManager {
|
|
|
164
170
|
* Clears all IP tracking data (for shutdown)
|
|
165
171
|
*/
|
|
166
172
|
public clearIPTracking(): void {
|
|
173
|
+
if (this.cleanupInterval) {
|
|
174
|
+
clearInterval(this.cleanupInterval);
|
|
175
|
+
this.cleanupInterval = null;
|
|
176
|
+
}
|
|
167
177
|
this.connectionsByIP.clear();
|
|
168
178
|
this.connectionRateByIP.clear();
|
|
169
179
|
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Start periodic cleanup of expired data
|
|
183
|
+
*/
|
|
184
|
+
private startPeriodicCleanup(): void {
|
|
185
|
+
this.cleanupInterval = setInterval(() => {
|
|
186
|
+
this.performCleanup();
|
|
187
|
+
}, 60000); // Run every minute
|
|
188
|
+
|
|
189
|
+
// Unref the timer so it doesn't keep the process alive
|
|
190
|
+
if (this.cleanupInterval.unref) {
|
|
191
|
+
this.cleanupInterval.unref();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Perform cleanup of expired rate limits and empty IP entries
|
|
197
|
+
*/
|
|
198
|
+
private performCleanup(): void {
|
|
199
|
+
const now = Date.now();
|
|
200
|
+
const minute = 60 * 1000;
|
|
201
|
+
let cleanedRateLimits = 0;
|
|
202
|
+
let cleanedIPs = 0;
|
|
203
|
+
|
|
204
|
+
// Clean up expired rate limit timestamps
|
|
205
|
+
for (const [ip, timestamps] of this.connectionRateByIP.entries()) {
|
|
206
|
+
const validTimestamps = timestamps.filter(time => now - time < minute);
|
|
207
|
+
|
|
208
|
+
if (validTimestamps.length === 0) {
|
|
209
|
+
// No valid timestamps, remove the IP entry
|
|
210
|
+
this.connectionRateByIP.delete(ip);
|
|
211
|
+
cleanedRateLimits++;
|
|
212
|
+
} else if (validTimestamps.length < timestamps.length) {
|
|
213
|
+
// Some timestamps expired, update with valid ones
|
|
214
|
+
this.connectionRateByIP.set(ip, validTimestamps);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Clean up IPs with no active connections
|
|
219
|
+
for (const [ip, connections] of this.connectionsByIP.entries()) {
|
|
220
|
+
if (connections.size === 0) {
|
|
221
|
+
this.connectionsByIP.delete(ip);
|
|
222
|
+
cleanedIPs++;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Log cleanup stats if anything was cleaned
|
|
227
|
+
if (cleanedRateLimits > 0 || cleanedIPs > 0) {
|
|
228
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
229
|
+
connectionLogDeduplicator.log(
|
|
230
|
+
'ip-cleanup',
|
|
231
|
+
'debug',
|
|
232
|
+
'IP tracking cleanup completed',
|
|
233
|
+
{
|
|
234
|
+
cleanedRateLimits,
|
|
235
|
+
cleanedIPs,
|
|
236
|
+
remainingIPs: this.connectionsByIP.size,
|
|
237
|
+
remainingRateLimits: this.connectionRateByIP.size,
|
|
238
|
+
component: 'security-manager'
|
|
239
|
+
},
|
|
240
|
+
'periodic-cleanup'
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
170
245
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import { logger } from '../../core/utils/logger.js';
|
|
3
|
+
import { connectionLogDeduplicator } from '../../core/utils/log-deduplicator.js';
|
|
3
4
|
|
|
4
5
|
// Importing required components
|
|
5
6
|
import { ConnectionManager } from './connection-manager.js';
|
|
@@ -515,6 +516,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
515
516
|
|
|
516
517
|
// Stop metrics collector
|
|
517
518
|
this.metricsCollector.stop();
|
|
519
|
+
|
|
520
|
+
// Flush any pending deduplicated logs
|
|
521
|
+
connectionLogDeduplicator.flushAll();
|
|
518
522
|
|
|
519
523
|
logger.log('info', 'SmartProxy shutdown complete.');
|
|
520
524
|
}
|