@push.rocks/smartproxy 17.0.0 → 18.0.1
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/00_commitinfo_data.js +1 -1
- package/dist_ts/core/utils/shared-security-manager.js +3 -3
- package/dist_ts/forwarding/handlers/base-handler.d.ts +5 -2
- package/dist_ts/forwarding/handlers/base-handler.js +12 -9
- package/dist_ts/forwarding/handlers/http-handler.js +8 -4
- package/dist_ts/proxies/network-proxy/http-request-handler.js +3 -2
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +2 -2
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +22 -29
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +3 -3
- package/dist_ts/proxies/smart-proxy/route-manager.js +5 -5
- package/dist_ts/proxies/smart-proxy/security-manager.d.ts +7 -8
- package/dist_ts/proxies/smart-proxy/security-manager.js +8 -9
- package/package.json +4 -4
- package/readme.plan.md +157 -172
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/utils/shared-security-manager.ts +2 -2
- package/ts/forwarding/handlers/base-handler.ts +14 -8
- package/ts/forwarding/handlers/http-handler.ts +8 -3
- package/ts/proxies/network-proxy/http-request-handler.ts +3 -2
- package/ts/proxies/smart-proxy/models/interfaces.ts +2 -2
- package/ts/proxies/smart-proxy/models/route-types.ts +31 -27
- package/ts/proxies/smart-proxy/route-connection-handler.ts +3 -3
- package/ts/proxies/smart-proxy/route-manager.ts +4 -4
- package/ts/proxies/smart-proxy/security-manager.ts +7 -8
|
@@ -40,9 +40,10 @@ export abstract class ForwardingHandler extends plugins.EventEmitter implements
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* Get a target from the configuration, supporting round-robin selection
|
|
43
|
+
* @param incomingPort Optional incoming port for 'preserve' mode
|
|
43
44
|
* @returns A resolved target object with host and port
|
|
44
45
|
*/
|
|
45
|
-
protected getTargetFromConfig(): { host: string, port: number } {
|
|
46
|
+
protected getTargetFromConfig(incomingPort: number = 80): { host: string, port: number } {
|
|
46
47
|
const { target } = this.config;
|
|
47
48
|
|
|
48
49
|
// Handle round-robin host selection
|
|
@@ -55,32 +56,37 @@ export abstract class ForwardingHandler extends plugins.EventEmitter implements
|
|
|
55
56
|
const randomIndex = Math.floor(Math.random() * target.host.length);
|
|
56
57
|
return {
|
|
57
58
|
host: target.host[randomIndex],
|
|
58
|
-
port: this.resolvePort(target.port)
|
|
59
|
+
port: this.resolvePort(target.port, incomingPort)
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
// Single host
|
|
63
64
|
return {
|
|
64
65
|
host: target.host,
|
|
65
|
-
port: this.resolvePort(target.port)
|
|
66
|
+
port: this.resolvePort(target.port, incomingPort)
|
|
66
67
|
};
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
/**
|
|
70
71
|
* Resolves a port value, handling 'preserve' and function ports
|
|
72
|
+
* @param port The port value to resolve
|
|
73
|
+
* @param incomingPort Optional incoming port to use for 'preserve' mode
|
|
71
74
|
*/
|
|
72
|
-
protected resolvePort(
|
|
75
|
+
protected resolvePort(
|
|
76
|
+
port: number | 'preserve' | ((ctx: any) => number),
|
|
77
|
+
incomingPort: number = 80
|
|
78
|
+
): number {
|
|
73
79
|
if (typeof port === 'function') {
|
|
74
80
|
try {
|
|
75
|
-
// Create a minimal context for the function
|
|
76
|
-
const ctx = { port:
|
|
81
|
+
// Create a minimal context for the function that includes the incoming port
|
|
82
|
+
const ctx = { port: incomingPort };
|
|
77
83
|
return port(ctx);
|
|
78
84
|
} catch (err) {
|
|
79
85
|
console.error('Error resolving port function:', err);
|
|
80
|
-
return
|
|
86
|
+
return incomingPort; // Fall back to incoming port
|
|
81
87
|
}
|
|
82
88
|
} else if (port === 'preserve') {
|
|
83
|
-
return
|
|
89
|
+
return incomingPort; // Use the actual incoming port for 'preserve'
|
|
84
90
|
} else {
|
|
85
91
|
return port;
|
|
86
92
|
}
|
|
@@ -38,6 +38,7 @@ export class HttpForwardingHandler extends ForwardingHandler {
|
|
|
38
38
|
// For HTTP, we mainly handle parsed requests, but we can still set up
|
|
39
39
|
// some basic connection tracking
|
|
40
40
|
const remoteAddress = socket.remoteAddress || 'unknown';
|
|
41
|
+
const localPort = socket.localPort || 80;
|
|
41
42
|
|
|
42
43
|
socket.on('close', (hadError) => {
|
|
43
44
|
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
@@ -54,7 +55,8 @@ export class HttpForwardingHandler extends ForwardingHandler {
|
|
|
54
55
|
});
|
|
55
56
|
|
|
56
57
|
this.emit(ForwardingHandlerEvents.CONNECTED, {
|
|
57
|
-
remoteAddress
|
|
58
|
+
remoteAddress,
|
|
59
|
+
localPort
|
|
58
60
|
});
|
|
59
61
|
}
|
|
60
62
|
|
|
@@ -64,8 +66,11 @@ export class HttpForwardingHandler extends ForwardingHandler {
|
|
|
64
66
|
* @param res The HTTP response
|
|
65
67
|
*/
|
|
66
68
|
public handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void {
|
|
67
|
-
// Get the
|
|
68
|
-
const
|
|
69
|
+
// Get the local port from the request (for 'preserve' port handling)
|
|
70
|
+
const localPort = req.socket.localPort || 80;
|
|
71
|
+
|
|
72
|
+
// Get the target from configuration, passing the incoming port
|
|
73
|
+
const target = this.getTargetFromConfig(localPort);
|
|
69
74
|
|
|
70
75
|
// Create a custom headers object with variables for substitution
|
|
71
76
|
const variables = {
|
|
@@ -41,11 +41,12 @@ export class HttpRequestHandler {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
// Optionally rewrite host header to match target
|
|
44
|
-
if (options.headers && options.headers
|
|
44
|
+
if (options.headers && 'host' in options.headers) {
|
|
45
45
|
// Only apply if host header rewrite is enabled or not explicitly disabled
|
|
46
46
|
const shouldRewriteHost = route?.action.options?.rewriteHostHeader !== false;
|
|
47
47
|
if (shouldRewriteHost) {
|
|
48
|
-
|
|
48
|
+
// Safely cast to OutgoingHttpHeaders to access host property
|
|
49
|
+
(options.headers as plugins.http.OutgoingHttpHeaders).host = `${destination.host}:${destination.port}`;
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -27,8 +27,8 @@ export interface ISmartProxyOptions {
|
|
|
27
27
|
port: number; // Default port to use when not specified in routes
|
|
28
28
|
};
|
|
29
29
|
security?: {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
ipAllowList?: string[]; // Default allowed IPs
|
|
31
|
+
ipBlockList?: string[]; // Default blocked IPs
|
|
32
32
|
maxConnections?: number; // Default max connections
|
|
33
33
|
};
|
|
34
34
|
preserveSourceIP?: boolean; // Default source IP preservation
|
|
@@ -112,13 +112,39 @@ export interface IRouteAuthentication {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
-
* Security options for
|
|
115
|
+
* Security options for routes
|
|
116
116
|
*/
|
|
117
117
|
export interface IRouteSecurity {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
// Access control lists
|
|
119
|
+
ipAllowList?: string[]; // IP addresses that are allowed to connect
|
|
120
|
+
ipBlockList?: string[]; // IP addresses that are blocked from connecting
|
|
121
|
+
|
|
122
|
+
// Connection limits
|
|
123
|
+
maxConnections?: number; // Maximum concurrent connections
|
|
124
|
+
|
|
125
|
+
// Authentication
|
|
121
126
|
authentication?: IRouteAuthentication;
|
|
127
|
+
|
|
128
|
+
// Rate limiting
|
|
129
|
+
rateLimit?: IRouteRateLimit;
|
|
130
|
+
|
|
131
|
+
// Authentication methods
|
|
132
|
+
basicAuth?: {
|
|
133
|
+
enabled: boolean;
|
|
134
|
+
users: Array<{ username: string; password: string }>;
|
|
135
|
+
realm?: string;
|
|
136
|
+
excludePaths?: string[];
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
jwtAuth?: {
|
|
140
|
+
enabled: boolean;
|
|
141
|
+
secret: string;
|
|
142
|
+
algorithm?: string;
|
|
143
|
+
issuer?: string;
|
|
144
|
+
audience?: string;
|
|
145
|
+
expiresIn?: number;
|
|
146
|
+
excludePaths?: string[];
|
|
147
|
+
};
|
|
122
148
|
}
|
|
123
149
|
|
|
124
150
|
/**
|
|
@@ -247,29 +273,7 @@ export interface IRouteRateLimit {
|
|
|
247
273
|
errorMessage?: string;
|
|
248
274
|
}
|
|
249
275
|
|
|
250
|
-
|
|
251
|
-
* Security features for routes
|
|
252
|
-
*/
|
|
253
|
-
export interface IRouteSecurity {
|
|
254
|
-
rateLimit?: IRouteRateLimit;
|
|
255
|
-
basicAuth?: {
|
|
256
|
-
enabled: boolean;
|
|
257
|
-
users: Array<{ username: string; password: string }>;
|
|
258
|
-
realm?: string;
|
|
259
|
-
excludePaths?: string[];
|
|
260
|
-
};
|
|
261
|
-
jwtAuth?: {
|
|
262
|
-
enabled: boolean;
|
|
263
|
-
secret: string;
|
|
264
|
-
algorithm?: string;
|
|
265
|
-
issuer?: string;
|
|
266
|
-
audience?: string;
|
|
267
|
-
expiresIn?: number;
|
|
268
|
-
excludePaths?: string[];
|
|
269
|
-
};
|
|
270
|
-
ipAllowList?: string[];
|
|
271
|
-
ipBlockList?: string[];
|
|
272
|
-
}
|
|
276
|
+
// IRouteSecurity is defined above - unified definition is used for all routes
|
|
273
277
|
|
|
274
278
|
/**
|
|
275
279
|
* CORS configuration for a route
|
|
@@ -289,11 +289,11 @@ export class RouteConnectionHandler {
|
|
|
289
289
|
// Check default security settings
|
|
290
290
|
const defaultSecuritySettings = this.settings.defaults?.security;
|
|
291
291
|
if (defaultSecuritySettings) {
|
|
292
|
-
if (defaultSecuritySettings.
|
|
292
|
+
if (defaultSecuritySettings.ipAllowList && defaultSecuritySettings.ipAllowList.length > 0) {
|
|
293
293
|
const isAllowed = this.securityManager.isIPAuthorized(
|
|
294
294
|
remoteIP,
|
|
295
|
-
defaultSecuritySettings.
|
|
296
|
-
defaultSecuritySettings.
|
|
295
|
+
defaultSecuritySettings.ipAllowList,
|
|
296
|
+
defaultSecuritySettings.ipBlockList || []
|
|
297
297
|
);
|
|
298
298
|
|
|
299
299
|
if (!isAllowed) {
|
|
@@ -213,8 +213,8 @@ export class RouteManager extends plugins.EventEmitter {
|
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
// Check blocked IPs first
|
|
216
|
-
if (security.
|
|
217
|
-
for (const pattern of security.
|
|
216
|
+
if (security.ipBlockList && security.ipBlockList.length > 0) {
|
|
217
|
+
for (const pattern of security.ipBlockList) {
|
|
218
218
|
if (this.matchIpPattern(pattern, clientIp)) {
|
|
219
219
|
return false; // IP is blocked
|
|
220
220
|
}
|
|
@@ -222,8 +222,8 @@ export class RouteManager extends plugins.EventEmitter {
|
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
// If there are allowed IPs, check them
|
|
225
|
-
if (security.
|
|
226
|
-
for (const pattern of security.
|
|
225
|
+
if (security.ipAllowList && security.ipAllowList.length > 0) {
|
|
226
|
+
for (const pattern of security.ipAllowList) {
|
|
227
227
|
if (this.matchIpPattern(pattern, clientIp)) {
|
|
228
228
|
return true; // IP is allowed
|
|
229
229
|
}
|
|
@@ -63,16 +63,15 @@ export class SecurityManager {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
* Check if an IP is authorized using
|
|
66
|
+
* Check if an IP is authorized using security rules
|
|
67
67
|
*
|
|
68
68
|
* This method is used to determine if an IP is allowed to connect, based on security
|
|
69
|
-
* rules configured in the
|
|
70
|
-
* typically derived from
|
|
71
|
-
* DomainConfigManager.getEffectiveIPRules().
|
|
69
|
+
* rules configured in the route configuration. The allowed and blocked IPs are
|
|
70
|
+
* typically derived from route.security.ipAllowList and ipBlockList.
|
|
72
71
|
*
|
|
73
72
|
* @param ip - The IP address to check
|
|
74
|
-
* @param allowedIPs - Array of allowed IP patterns from
|
|
75
|
-
* @param blockedIPs - Array of blocked IP patterns from
|
|
73
|
+
* @param allowedIPs - Array of allowed IP patterns from security.ipAllowList
|
|
74
|
+
* @param blockedIPs - Array of blocked IP patterns from security.ipBlockList
|
|
76
75
|
* @returns true if IP is authorized, false if blocked
|
|
77
76
|
*/
|
|
78
77
|
public isIPAuthorized(ip: string, allowedIPs: string[], blockedIPs: string[] = []): boolean {
|
|
@@ -94,10 +93,10 @@ export class SecurityManager {
|
|
|
94
93
|
* Check if the IP matches any of the glob patterns from security configuration
|
|
95
94
|
*
|
|
96
95
|
* This method checks IP addresses against glob patterns and handles IPv4/IPv6 normalization.
|
|
97
|
-
* It's used to implement IP filtering based on the
|
|
96
|
+
* It's used to implement IP filtering based on the route.security configuration.
|
|
98
97
|
*
|
|
99
98
|
* @param ip - The IP address to check
|
|
100
|
-
* @param patterns - Array of glob patterns from
|
|
99
|
+
* @param patterns - Array of glob patterns from security.ipAllowList or ipBlockList
|
|
101
100
|
* @returns true if IP matches any pattern, false otherwise
|
|
102
101
|
*/
|
|
103
102
|
private isGlobIPMatch(ip: string, patterns: string[]): boolean {
|