@push.rocks/smartproxy 25.17.10 → 26.1.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.
- package/changelog.md +15 -0
- package/dist_rust/rustproxy_linux_amd64 +0 -0
- package/dist_rust/rustproxy_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/core/index.d.ts +0 -1
- package/dist_ts/core/index.js +1 -2
- package/dist_ts/core/models/index.d.ts +0 -1
- package/dist_ts/core/models/index.js +1 -2
- package/dist_ts/core/utils/index.d.ts +0 -12
- package/dist_ts/core/utils/index.js +1 -13
- package/dist_ts/index.d.ts +0 -3
- package/dist_ts/index.js +2 -7
- package/dist_ts/protocols/http/index.d.ts +0 -1
- package/dist_ts/protocols/http/index.js +1 -2
- package/dist_ts/protocols/index.d.ts +0 -7
- package/dist_ts/protocols/index.js +1 -8
- package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +20 -0
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +2 -1
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +4 -1
- package/dist_ts/proxies/smart-proxy/socket-handler-server.js +6 -1
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.d.ts +0 -7
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.js +50 -51
- package/dist_ts/routing/index.d.ts +0 -1
- package/dist_ts/routing/index.js +1 -3
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/index.ts +0 -1
- package/ts/core/models/index.ts +0 -1
- package/ts/core/utils/index.ts +0 -12
- package/ts/index.ts +1 -7
- package/ts/protocols/http/index.ts +1 -2
- package/ts/protocols/index.ts +0 -7
- package/ts/proxies/smart-proxy/models/metrics-types.ts +21 -0
- package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +4 -1
- package/ts/proxies/smart-proxy/socket-handler-server.ts +6 -0
- package/ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.ts +60 -59
- package/ts/routing/index.ts +0 -3
- package/dist_ts/core/events/index.d.ts +0 -4
- package/dist_ts/core/events/index.js +0 -5
- package/dist_ts/core/models/socket-augmentation.d.ts +0 -15
- package/dist_ts/core/models/socket-augmentation.js +0 -18
- package/dist_ts/core/utils/async-utils.d.ts +0 -81
- package/dist_ts/core/utils/async-utils.js +0 -216
- package/dist_ts/core/utils/binary-heap.d.ts +0 -73
- package/dist_ts/core/utils/binary-heap.js +0 -193
- package/dist_ts/core/utils/enhanced-connection-pool.d.ts +0 -110
- package/dist_ts/core/utils/enhanced-connection-pool.js +0 -325
- package/dist_ts/core/utils/fs-utils.d.ts +0 -144
- package/dist_ts/core/utils/fs-utils.js +0 -252
- package/dist_ts/core/utils/ip-utils.d.ts +0 -69
- package/dist_ts/core/utils/ip-utils.js +0 -270
- package/dist_ts/core/utils/lifecycle-component.d.ts +0 -59
- package/dist_ts/core/utils/lifecycle-component.js +0 -211
- package/dist_ts/core/utils/log-deduplicator.d.ts +0 -39
- package/dist_ts/core/utils/log-deduplicator.js +0 -305
- package/dist_ts/core/utils/security-utils.d.ts +0 -111
- package/dist_ts/core/utils/security-utils.js +0 -212
- package/dist_ts/core/utils/shared-security-manager.d.ts +0 -128
- package/dist_ts/core/utils/shared-security-manager.js +0 -362
- package/dist_ts/core/utils/socket-utils.d.ts +0 -63
- package/dist_ts/core/utils/socket-utils.js +0 -249
- package/dist_ts/core/utils/template-utils.d.ts +0 -37
- package/dist_ts/core/utils/template-utils.js +0 -104
- package/dist_ts/core/utils/validation-utils.d.ts +0 -61
- package/dist_ts/core/utils/validation-utils.js +0 -149
- package/dist_ts/core/utils/websocket-utils.d.ts +0 -22
- package/dist_ts/core/utils/websocket-utils.js +0 -30
- package/dist_ts/detection/detectors/http-detector.d.ts +0 -33
- package/dist_ts/detection/detectors/http-detector.js +0 -101
- package/dist_ts/detection/detectors/quick-detector.d.ts +0 -28
- package/dist_ts/detection/detectors/quick-detector.js +0 -131
- package/dist_ts/detection/detectors/routing-extractor.d.ts +0 -28
- package/dist_ts/detection/detectors/routing-extractor.js +0 -122
- package/dist_ts/detection/detectors/tls-detector.d.ts +0 -47
- package/dist_ts/detection/detectors/tls-detector.js +0 -183
- package/dist_ts/detection/index.d.ts +0 -17
- package/dist_ts/detection/index.js +0 -22
- package/dist_ts/detection/models/detection-types.d.ts +0 -87
- package/dist_ts/detection/models/detection-types.js +0 -5
- package/dist_ts/detection/models/interfaces.d.ts +0 -97
- package/dist_ts/detection/models/interfaces.js +0 -5
- package/dist_ts/detection/protocol-detector.d.ts +0 -79
- package/dist_ts/detection/protocol-detector.js +0 -253
- package/dist_ts/detection/utils/buffer-utils.d.ts +0 -61
- package/dist_ts/detection/utils/buffer-utils.js +0 -127
- package/dist_ts/detection/utils/fragment-manager.d.ts +0 -31
- package/dist_ts/detection/utils/fragment-manager.js +0 -53
- package/dist_ts/detection/utils/parser-utils.d.ts +0 -42
- package/dist_ts/detection/utils/parser-utils.js +0 -63
- package/dist_ts/protocols/common/fragment-handler.d.ts +0 -73
- package/dist_ts/protocols/common/fragment-handler.js +0 -121
- package/dist_ts/protocols/common/index.d.ts +0 -7
- package/dist_ts/protocols/common/index.js +0 -8
- package/dist_ts/protocols/common/types.d.ts +0 -68
- package/dist_ts/protocols/common/types.js +0 -7
- package/dist_ts/protocols/http/parser.d.ts +0 -58
- package/dist_ts/protocols/http/parser.js +0 -184
- package/dist_ts/protocols/proxy/index.d.ts +0 -5
- package/dist_ts/protocols/proxy/index.js +0 -6
- package/dist_ts/protocols/proxy/types.d.ts +0 -47
- package/dist_ts/protocols/proxy/types.js +0 -6
- package/dist_ts/protocols/tls/alerts/index.d.ts +0 -4
- package/dist_ts/protocols/tls/alerts/index.js +0 -5
- package/dist_ts/protocols/tls/alerts/tls-alert.d.ts +0 -150
- package/dist_ts/protocols/tls/alerts/tls-alert.js +0 -226
- package/dist_ts/protocols/tls/index.d.ts +0 -12
- package/dist_ts/protocols/tls/index.js +0 -27
- package/dist_ts/protocols/tls/sni/client-hello-parser.d.ts +0 -100
- package/dist_ts/protocols/tls/sni/client-hello-parser.js +0 -463
- package/dist_ts/protocols/tls/sni/index.d.ts +0 -5
- package/dist_ts/protocols/tls/sni/index.js +0 -6
- package/dist_ts/protocols/tls/sni/sni-extraction.d.ts +0 -58
- package/dist_ts/protocols/tls/sni/sni-extraction.js +0 -275
- package/dist_ts/protocols/tls/utils/index.d.ts +0 -4
- package/dist_ts/protocols/tls/utils/index.js +0 -5
- package/dist_ts/protocols/tls/utils/tls-utils.d.ts +0 -158
- package/dist_ts/protocols/tls/utils/tls-utils.js +0 -187
- package/dist_ts/protocols/websocket/constants.d.ts +0 -55
- package/dist_ts/protocols/websocket/constants.js +0 -58
- package/dist_ts/protocols/websocket/index.d.ts +0 -7
- package/dist_ts/protocols/websocket/index.js +0 -8
- package/dist_ts/protocols/websocket/types.d.ts +0 -47
- package/dist_ts/protocols/websocket/types.js +0 -5
- package/dist_ts/protocols/websocket/utils.d.ts +0 -25
- package/dist_ts/protocols/websocket/utils.js +0 -103
- package/dist_ts/routing/router/http-router.d.ts +0 -89
- package/dist_ts/routing/router/http-router.js +0 -205
- package/dist_ts/routing/router/index.d.ts +0 -5
- package/dist_ts/routing/router/index.js +0 -6
- package/dist_ts/tls/index.d.ts +0 -16
- package/dist_ts/tls/index.js +0 -24
- package/dist_ts/tls/sni/index.d.ts +0 -4
- package/dist_ts/tls/sni/index.js +0 -5
- package/dist_ts/tls/sni/sni-handler.d.ts +0 -154
- package/dist_ts/tls/sni/sni-handler.js +0 -191
- package/ts/core/events/index.ts +0 -3
- package/ts/core/models/socket-augmentation.ts +0 -38
- package/ts/core/utils/async-utils.ts +0 -275
- package/ts/core/utils/binary-heap.ts +0 -225
- package/ts/core/utils/enhanced-connection-pool.ts +0 -425
- package/ts/core/utils/fs-utils.ts +0 -270
- package/ts/core/utils/ip-utils.ts +0 -303
- package/ts/core/utils/lifecycle-component.ts +0 -251
- package/ts/core/utils/log-deduplicator.ts +0 -370
- package/ts/core/utils/security-utils.ts +0 -305
- package/ts/core/utils/shared-security-manager.ts +0 -470
- package/ts/core/utils/socket-utils.ts +0 -322
- package/ts/core/utils/template-utils.ts +0 -124
- package/ts/core/utils/validation-utils.ts +0 -177
- package/ts/core/utils/websocket-utils.ts +0 -33
- package/ts/detection/detectors/http-detector.ts +0 -127
- package/ts/detection/detectors/quick-detector.ts +0 -148
- package/ts/detection/detectors/routing-extractor.ts +0 -147
- package/ts/detection/detectors/tls-detector.ts +0 -223
- package/ts/detection/index.ts +0 -25
- package/ts/detection/models/detection-types.ts +0 -102
- package/ts/detection/models/interfaces.ts +0 -115
- package/ts/detection/protocol-detector.ts +0 -319
- package/ts/detection/utils/buffer-utils.ts +0 -141
- package/ts/detection/utils/fragment-manager.ts +0 -64
- package/ts/detection/utils/parser-utils.ts +0 -77
- package/ts/protocols/common/fragment-handler.ts +0 -167
- package/ts/protocols/common/index.ts +0 -8
- package/ts/protocols/common/types.ts +0 -76
- package/ts/protocols/http/parser.ts +0 -219
- package/ts/protocols/proxy/index.ts +0 -6
- package/ts/protocols/proxy/types.ts +0 -53
- package/ts/protocols/tls/alerts/index.ts +0 -3
- package/ts/protocols/tls/alerts/tls-alert.ts +0 -259
- package/ts/protocols/tls/index.ts +0 -37
- package/ts/protocols/tls/sni/client-hello-parser.ts +0 -629
- package/ts/protocols/tls/sni/index.ts +0 -6
- package/ts/protocols/tls/sni/sni-extraction.ts +0 -353
- package/ts/protocols/tls/utils/index.ts +0 -3
- package/ts/protocols/tls/utils/tls-utils.ts +0 -201
- package/ts/protocols/websocket/constants.ts +0 -60
- package/ts/protocols/websocket/index.ts +0 -8
- package/ts/protocols/websocket/types.ts +0 -53
- package/ts/protocols/websocket/utils.ts +0 -98
- package/ts/routing/router/http-router.ts +0 -266
- package/ts/routing/router/index.ts +0 -7
- package/ts/tls/index.ts +0 -29
- package/ts/tls/sni/index.ts +0 -3
- package/ts/tls/sni/sni-handler.ts +0 -264
|
@@ -1,470 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../../plugins.js';
|
|
2
|
-
import type { IRouteConfig, IRouteContext } from '../../proxies/smart-proxy/models/route-types.js';
|
|
3
|
-
import type {
|
|
4
|
-
IIpValidationResult,
|
|
5
|
-
IIpConnectionInfo,
|
|
6
|
-
ISecurityLogger,
|
|
7
|
-
IRateLimitInfo
|
|
8
|
-
} from './security-utils.js';
|
|
9
|
-
import {
|
|
10
|
-
isIPAuthorized,
|
|
11
|
-
checkMaxConnections,
|
|
12
|
-
checkConnectionRate,
|
|
13
|
-
trackConnection,
|
|
14
|
-
removeConnection,
|
|
15
|
-
cleanupExpiredRateLimits,
|
|
16
|
-
parseBasicAuthHeader,
|
|
17
|
-
normalizeIP
|
|
18
|
-
} from './security-utils.js';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Shared SecurityManager for use across proxy components
|
|
22
|
-
* Handles IP tracking, rate limiting, and authentication
|
|
23
|
-
*/
|
|
24
|
-
export class SharedSecurityManager {
|
|
25
|
-
// IP connection tracking
|
|
26
|
-
private connectionsByIP: Map<string, IIpConnectionInfo> = new Map();
|
|
27
|
-
|
|
28
|
-
// Route-specific rate limiting
|
|
29
|
-
private rateLimits: Map<string, Map<string, IRateLimitInfo>> = new Map();
|
|
30
|
-
|
|
31
|
-
// Cache IP filtering results to avoid constant regex matching
|
|
32
|
-
private ipFilterCache: Map<string, Map<string, boolean>> = new Map();
|
|
33
|
-
|
|
34
|
-
// Default limits
|
|
35
|
-
private maxConnectionsPerIP: number;
|
|
36
|
-
private connectionRateLimitPerMinute: number;
|
|
37
|
-
|
|
38
|
-
// Cache cleanup interval
|
|
39
|
-
private cleanupInterval: NodeJS.Timeout | null = null;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Create a new SharedSecurityManager
|
|
43
|
-
*
|
|
44
|
-
* @param options - Configuration options
|
|
45
|
-
* @param logger - Logger instance
|
|
46
|
-
*/
|
|
47
|
-
constructor(options: {
|
|
48
|
-
maxConnectionsPerIP?: number;
|
|
49
|
-
connectionRateLimitPerMinute?: number;
|
|
50
|
-
cleanupIntervalMs?: number;
|
|
51
|
-
routes?: IRouteConfig[];
|
|
52
|
-
}, private logger?: ISecurityLogger) {
|
|
53
|
-
this.maxConnectionsPerIP = options.maxConnectionsPerIP || 100;
|
|
54
|
-
this.connectionRateLimitPerMinute = options.connectionRateLimitPerMinute || 300;
|
|
55
|
-
|
|
56
|
-
// Set up logger with defaults if not provided
|
|
57
|
-
this.logger = logger || {
|
|
58
|
-
info: console.log,
|
|
59
|
-
warn: console.warn,
|
|
60
|
-
error: console.error
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// Set up cache cleanup interval
|
|
64
|
-
const cleanupInterval = options.cleanupIntervalMs || 60000; // Default: 1 minute
|
|
65
|
-
this.cleanupInterval = setInterval(() => {
|
|
66
|
-
this.cleanupCaches();
|
|
67
|
-
}, cleanupInterval);
|
|
68
|
-
|
|
69
|
-
// Don't keep the process alive just for cleanup
|
|
70
|
-
if (this.cleanupInterval.unref) {
|
|
71
|
-
this.cleanupInterval.unref();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Get connections count by IP
|
|
77
|
-
*
|
|
78
|
-
* @param ip - The IP address to check
|
|
79
|
-
* @returns Number of connections from this IP
|
|
80
|
-
*/
|
|
81
|
-
public getConnectionCountByIP(ip: string): number {
|
|
82
|
-
// Check all normalized variants of the IP
|
|
83
|
-
const variants = normalizeIP(ip);
|
|
84
|
-
for (const variant of variants) {
|
|
85
|
-
const info = this.connectionsByIP.get(variant);
|
|
86
|
-
if (info) {
|
|
87
|
-
return info.connections.size;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return 0;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Track connection by IP
|
|
95
|
-
*
|
|
96
|
-
* @param ip - The IP address to track
|
|
97
|
-
* @param connectionId - The connection ID to associate
|
|
98
|
-
*/
|
|
99
|
-
public trackConnectionByIP(ip: string, connectionId: string): void {
|
|
100
|
-
// Check if any variant already exists
|
|
101
|
-
const variants = normalizeIP(ip);
|
|
102
|
-
let existingKey: string | null = null;
|
|
103
|
-
|
|
104
|
-
for (const variant of variants) {
|
|
105
|
-
if (this.connectionsByIP.has(variant)) {
|
|
106
|
-
existingKey = variant;
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Use existing key or the original IP
|
|
112
|
-
trackConnection(existingKey || ip, connectionId, this.connectionsByIP);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Remove connection tracking for an IP
|
|
117
|
-
*
|
|
118
|
-
* @param ip - The IP address to update
|
|
119
|
-
* @param connectionId - The connection ID to remove
|
|
120
|
-
*/
|
|
121
|
-
public removeConnectionByIP(ip: string, connectionId: string): void {
|
|
122
|
-
// Check all variants to find where the connection is tracked
|
|
123
|
-
const variants = normalizeIP(ip);
|
|
124
|
-
|
|
125
|
-
for (const variant of variants) {
|
|
126
|
-
if (this.connectionsByIP.has(variant)) {
|
|
127
|
-
removeConnection(variant, connectionId, this.connectionsByIP);
|
|
128
|
-
break;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Check if IP is authorized based on route security settings
|
|
135
|
-
*
|
|
136
|
-
* @param ip - The IP address to check
|
|
137
|
-
* @param allowedIPs - List of allowed IP patterns
|
|
138
|
-
* @param blockedIPs - List of blocked IP patterns
|
|
139
|
-
* @returns Whether the IP is authorized
|
|
140
|
-
*/
|
|
141
|
-
public isIPAuthorized(
|
|
142
|
-
ip: string,
|
|
143
|
-
allowedIPs: string[] = ['*'],
|
|
144
|
-
blockedIPs: string[] = []
|
|
145
|
-
): boolean {
|
|
146
|
-
return isIPAuthorized(ip, allowedIPs, blockedIPs);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Validate IP against rate limits and connection limits
|
|
151
|
-
*
|
|
152
|
-
* @param ip - The IP address to validate
|
|
153
|
-
* @returns Result with allowed status and reason if blocked
|
|
154
|
-
*/
|
|
155
|
-
public validateIP(ip: string): IIpValidationResult {
|
|
156
|
-
// Check connection count limit
|
|
157
|
-
const connectionResult = checkMaxConnections(
|
|
158
|
-
ip,
|
|
159
|
-
this.connectionsByIP,
|
|
160
|
-
this.maxConnectionsPerIP
|
|
161
|
-
);
|
|
162
|
-
if (!connectionResult.allowed) {
|
|
163
|
-
return connectionResult;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Check connection rate limit
|
|
167
|
-
const rateResult = checkConnectionRate(
|
|
168
|
-
ip,
|
|
169
|
-
this.connectionsByIP,
|
|
170
|
-
this.connectionRateLimitPerMinute
|
|
171
|
-
);
|
|
172
|
-
if (!rateResult.allowed) {
|
|
173
|
-
return rateResult;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return { allowed: true };
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Atomically validate an IP and track the connection if allowed.
|
|
181
|
-
* This prevents race conditions where concurrent connections could bypass per-IP limits.
|
|
182
|
-
*
|
|
183
|
-
* @param ip - The IP address to validate
|
|
184
|
-
* @param connectionId - The connection ID to track if validation passes
|
|
185
|
-
* @returns Object with validation result and reason
|
|
186
|
-
*/
|
|
187
|
-
public validateAndTrackIP(ip: string, connectionId: string): IIpValidationResult {
|
|
188
|
-
// Check connection count limit BEFORE tracking
|
|
189
|
-
const connectionResult = checkMaxConnections(
|
|
190
|
-
ip,
|
|
191
|
-
this.connectionsByIP,
|
|
192
|
-
this.maxConnectionsPerIP
|
|
193
|
-
);
|
|
194
|
-
if (!connectionResult.allowed) {
|
|
195
|
-
return connectionResult;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Check connection rate limit
|
|
199
|
-
const rateResult = checkConnectionRate(
|
|
200
|
-
ip,
|
|
201
|
-
this.connectionsByIP,
|
|
202
|
-
this.connectionRateLimitPerMinute
|
|
203
|
-
);
|
|
204
|
-
if (!rateResult.allowed) {
|
|
205
|
-
return rateResult;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Validation passed - immediately track to prevent race conditions
|
|
209
|
-
this.trackConnectionByIP(ip, connectionId);
|
|
210
|
-
|
|
211
|
-
return { allowed: true };
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Check if a client is allowed to access a specific route
|
|
216
|
-
*
|
|
217
|
-
* @param route - The route to check
|
|
218
|
-
* @param context - The request context
|
|
219
|
-
* @param routeConnectionCount - Current connection count for this route (optional)
|
|
220
|
-
* @returns Whether access is allowed
|
|
221
|
-
*/
|
|
222
|
-
public isAllowed(route: IRouteConfig, context: IRouteContext, routeConnectionCount?: number): boolean {
|
|
223
|
-
if (!route.security) {
|
|
224
|
-
return true; // No security restrictions
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// --- IP filtering ---
|
|
228
|
-
if (!this.isClientIpAllowed(route, context.clientIp)) {
|
|
229
|
-
this.logger?.debug?.(`IP ${context.clientIp} is blocked for route ${route.name || 'unnamed'}`);
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// --- Route-level connection limit ---
|
|
234
|
-
if (route.security.maxConnections !== undefined && routeConnectionCount !== undefined) {
|
|
235
|
-
if (routeConnectionCount >= route.security.maxConnections) {
|
|
236
|
-
this.logger?.debug?.(`Route connection limit (${route.security.maxConnections}) exceeded for route ${route.name || 'unnamed'}`);
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// --- Rate limiting ---
|
|
242
|
-
if (route.security.rateLimit?.enabled && !this.isWithinRateLimit(route, context)) {
|
|
243
|
-
this.logger?.debug?.(`Rate limit exceeded for route ${route.name || 'unnamed'}`);
|
|
244
|
-
return false;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return true;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Check if a client IP is allowed for a route
|
|
252
|
-
*
|
|
253
|
-
* @param route - The route to check
|
|
254
|
-
* @param clientIp - The client IP
|
|
255
|
-
* @returns Whether the IP is allowed
|
|
256
|
-
*/
|
|
257
|
-
private isClientIpAllowed(route: IRouteConfig, clientIp: string): boolean {
|
|
258
|
-
if (!route.security) {
|
|
259
|
-
return true; // No security restrictions
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const routeId = route.id || route.name || 'unnamed';
|
|
263
|
-
|
|
264
|
-
// Check cache first
|
|
265
|
-
if (!this.ipFilterCache.has(routeId)) {
|
|
266
|
-
this.ipFilterCache.set(routeId, new Map());
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const routeCache = this.ipFilterCache.get(routeId)!;
|
|
270
|
-
if (routeCache.has(clientIp)) {
|
|
271
|
-
return routeCache.get(clientIp)!;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Check IP against route security settings
|
|
275
|
-
const ipAllowList = route.security.ipAllowList;
|
|
276
|
-
const ipBlockList = route.security.ipBlockList;
|
|
277
|
-
|
|
278
|
-
const allowed = this.isIPAuthorized(clientIp, ipAllowList, ipBlockList);
|
|
279
|
-
|
|
280
|
-
// Cache the result
|
|
281
|
-
routeCache.set(clientIp, allowed);
|
|
282
|
-
|
|
283
|
-
return allowed;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Check if request is within rate limit
|
|
288
|
-
*
|
|
289
|
-
* @param route - The route to check
|
|
290
|
-
* @param context - The request context
|
|
291
|
-
* @returns Whether the request is within rate limit
|
|
292
|
-
*/
|
|
293
|
-
private isWithinRateLimit(route: IRouteConfig, context: IRouteContext): boolean {
|
|
294
|
-
if (!route.security?.rateLimit?.enabled) {
|
|
295
|
-
return true;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const rateLimit = route.security.rateLimit;
|
|
299
|
-
const routeId = route.id || route.name || 'unnamed';
|
|
300
|
-
|
|
301
|
-
// Determine rate limit key (by IP, path, or header)
|
|
302
|
-
let key = context.clientIp; // Default to IP
|
|
303
|
-
|
|
304
|
-
if (rateLimit.keyBy === 'path' && context.path) {
|
|
305
|
-
key = `${context.clientIp}:${context.path}`;
|
|
306
|
-
} else if (rateLimit.keyBy === 'header' && rateLimit.headerName && context.headers) {
|
|
307
|
-
const headerValue = context.headers[rateLimit.headerName.toLowerCase()];
|
|
308
|
-
if (headerValue) {
|
|
309
|
-
key = `${context.clientIp}:${headerValue}`;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Get or create rate limit tracking for this route
|
|
314
|
-
if (!this.rateLimits.has(routeId)) {
|
|
315
|
-
this.rateLimits.set(routeId, new Map());
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const routeLimits = this.rateLimits.get(routeId)!;
|
|
319
|
-
const now = Date.now();
|
|
320
|
-
|
|
321
|
-
// Get or create rate limit tracking for this key
|
|
322
|
-
let limit = routeLimits.get(key);
|
|
323
|
-
if (!limit || limit.expiry < now) {
|
|
324
|
-
// Create new rate limit or reset expired one
|
|
325
|
-
limit = {
|
|
326
|
-
count: 1,
|
|
327
|
-
expiry: now + (rateLimit.window * 1000)
|
|
328
|
-
};
|
|
329
|
-
routeLimits.set(key, limit);
|
|
330
|
-
return true;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Increment the counter
|
|
334
|
-
limit.count++;
|
|
335
|
-
|
|
336
|
-
// Check if rate limit is exceeded
|
|
337
|
-
return limit.count <= rateLimit.maxRequests;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Validate HTTP Basic Authentication
|
|
342
|
-
*
|
|
343
|
-
* @param route - The route to check
|
|
344
|
-
* @param authHeader - The Authorization header
|
|
345
|
-
* @returns Whether authentication is valid
|
|
346
|
-
*/
|
|
347
|
-
public validateBasicAuth(route: IRouteConfig, authHeader?: string): boolean {
|
|
348
|
-
// Skip if basic auth not enabled for route
|
|
349
|
-
if (!route.security?.basicAuth?.enabled) {
|
|
350
|
-
return true;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// No auth header means auth failed
|
|
354
|
-
if (!authHeader) {
|
|
355
|
-
return false;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Parse auth header
|
|
359
|
-
const credentials = parseBasicAuthHeader(authHeader);
|
|
360
|
-
if (!credentials) {
|
|
361
|
-
return false;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Check credentials against configured users
|
|
365
|
-
const { username, password } = credentials;
|
|
366
|
-
const users = route.security.basicAuth.users;
|
|
367
|
-
|
|
368
|
-
return users.some(user =>
|
|
369
|
-
user.username === username && user.password === password
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Verify a JWT token against route configuration
|
|
375
|
-
*
|
|
376
|
-
* @param route - The route to verify the token for
|
|
377
|
-
* @param token - The JWT token to verify
|
|
378
|
-
* @returns True if the token is valid, false otherwise
|
|
379
|
-
*/
|
|
380
|
-
public verifyJwtToken(route: IRouteConfig, token: string): boolean {
|
|
381
|
-
if (!route.security?.jwtAuth?.enabled) {
|
|
382
|
-
return true;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
try {
|
|
386
|
-
const jwtAuth = route.security.jwtAuth;
|
|
387
|
-
|
|
388
|
-
// Verify structure (header.payload.signature)
|
|
389
|
-
const parts = token.split('.');
|
|
390
|
-
if (parts.length !== 3) {
|
|
391
|
-
return false;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Decode payload
|
|
395
|
-
const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString());
|
|
396
|
-
|
|
397
|
-
// Check expiration
|
|
398
|
-
if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) {
|
|
399
|
-
return false;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Check issuer
|
|
403
|
-
if (jwtAuth.issuer && payload.iss !== jwtAuth.issuer) {
|
|
404
|
-
return false;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Check audience
|
|
408
|
-
if (jwtAuth.audience && payload.aud !== jwtAuth.audience) {
|
|
409
|
-
return false;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Note: In a real implementation, you'd also verify the signature
|
|
413
|
-
// using the secret and algorithm specified in jwtAuth.
|
|
414
|
-
// This requires a proper JWT library for cryptographic verification.
|
|
415
|
-
|
|
416
|
-
return true;
|
|
417
|
-
} catch (err) {
|
|
418
|
-
this.logger?.error?.(`Error verifying JWT: ${err}`);
|
|
419
|
-
return false;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* Clean up caches to prevent memory leaks
|
|
425
|
-
*/
|
|
426
|
-
private cleanupCaches(): void {
|
|
427
|
-
// Clean up rate limits
|
|
428
|
-
cleanupExpiredRateLimits(this.rateLimits, this.logger);
|
|
429
|
-
|
|
430
|
-
// Clean up IP connection tracking
|
|
431
|
-
let cleanedIPs = 0;
|
|
432
|
-
for (const [ip, info] of this.connectionsByIP.entries()) {
|
|
433
|
-
// Remove IPs with no active connections and no recent timestamps
|
|
434
|
-
if (info.connections.size === 0 && info.timestamps.length === 0) {
|
|
435
|
-
this.connectionsByIP.delete(ip);
|
|
436
|
-
cleanedIPs++;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
if (cleanedIPs > 0 && this.logger?.debug) {
|
|
441
|
-
this.logger.debug(`Cleaned up ${cleanedIPs} IPs with no active connections`);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// IP filter cache doesn't need cleanup (tied to routes)
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Clear all IP tracking data (for shutdown)
|
|
449
|
-
*/
|
|
450
|
-
public clearIPTracking(): void {
|
|
451
|
-
this.connectionsByIP.clear();
|
|
452
|
-
this.rateLimits.clear();
|
|
453
|
-
this.ipFilterCache.clear();
|
|
454
|
-
|
|
455
|
-
if (this.cleanupInterval) {
|
|
456
|
-
clearInterval(this.cleanupInterval);
|
|
457
|
-
this.cleanupInterval = null;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* Update routes for security checking
|
|
463
|
-
*
|
|
464
|
-
* @param routes - New routes to use
|
|
465
|
-
*/
|
|
466
|
-
public setRoutes(routes: IRouteConfig[]): void {
|
|
467
|
-
// Only clear the IP filter cache - route-specific
|
|
468
|
-
this.ipFilterCache.clear();
|
|
469
|
-
}
|
|
470
|
-
}
|