@push.rocks/smartproxy 10.2.0 → 11.0.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/common/port80-adapter.d.ts +11 -0
- package/dist_ts/common/port80-adapter.js +61 -0
- package/dist_ts/examples/forwarding-example.d.ts +1 -0
- package/dist_ts/examples/forwarding-example.js +96 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts/smartproxy/classes.pp.connectionhandler.js +179 -30
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.d.ts +39 -0
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.js +172 -20
- package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +3 -11
- package/dist_ts/smartproxy/classes.pp.portrangemanager.js +17 -10
- package/dist_ts/smartproxy/classes.pp.securitymanager.d.ts +19 -2
- package/dist_ts/smartproxy/classes.pp.securitymanager.js +27 -4
- package/dist_ts/smartproxy/classes.pp.timeoutmanager.js +3 -3
- package/dist_ts/smartproxy/classes.smartproxy.js +45 -13
- package/dist_ts/smartproxy/forwarding/domain-config.d.ts +12 -0
- package/dist_ts/smartproxy/forwarding/domain-config.js +12 -0
- package/dist_ts/smartproxy/forwarding/domain-manager.d.ts +86 -0
- package/dist_ts/smartproxy/forwarding/domain-manager.js +241 -0
- package/dist_ts/smartproxy/forwarding/forwarding.factory.d.ts +24 -0
- package/dist_ts/smartproxy/forwarding/forwarding.factory.js +137 -0
- package/dist_ts/smartproxy/forwarding/forwarding.handler.d.ts +55 -0
- package/dist_ts/smartproxy/forwarding/forwarding.handler.js +94 -0
- package/dist_ts/smartproxy/forwarding/http.handler.d.ts +25 -0
- package/dist_ts/smartproxy/forwarding/http.handler.js +123 -0
- package/dist_ts/smartproxy/forwarding/https-passthrough.handler.d.ts +24 -0
- package/dist_ts/smartproxy/forwarding/https-passthrough.handler.js +154 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.d.ts +36 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.js +229 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.d.ts +35 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.js +254 -0
- package/dist_ts/smartproxy/forwarding/index.d.ts +16 -0
- package/dist_ts/smartproxy/forwarding/index.js +23 -0
- package/dist_ts/smartproxy/types/forwarding.types.d.ts +104 -0
- package/dist_ts/smartproxy/types/forwarding.types.js +50 -0
- package/package.json +2 -2
- package/readme.md +158 -8
- package/readme.plan.md +471 -42
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/common/port80-adapter.ts +87 -0
- package/ts/examples/forwarding-example.ts +128 -0
- package/ts/index.ts +3 -0
- package/ts/smartproxy/classes.pp.connectionhandler.ts +231 -44
- package/ts/smartproxy/classes.pp.domainconfigmanager.ts +198 -24
- package/ts/smartproxy/classes.pp.interfaces.ts +3 -11
- package/ts/smartproxy/classes.pp.portrangemanager.ts +17 -10
- package/ts/smartproxy/classes.pp.securitymanager.ts +29 -5
- package/ts/smartproxy/classes.pp.timeoutmanager.ts +3 -3
- package/ts/smartproxy/classes.smartproxy.ts +68 -15
- package/ts/smartproxy/forwarding/domain-config.ts +28 -0
- package/ts/smartproxy/forwarding/domain-manager.ts +283 -0
- package/ts/smartproxy/forwarding/forwarding.factory.ts +155 -0
- package/ts/smartproxy/forwarding/forwarding.handler.ts +127 -0
- package/ts/smartproxy/forwarding/http.handler.ts +140 -0
- package/ts/smartproxy/forwarding/https-passthrough.handler.ts +182 -0
- package/ts/smartproxy/forwarding/https-terminate-to-http.handler.ts +264 -0
- package/ts/smartproxy/forwarding/https-terminate-to-https.handler.ts +292 -0
- package/ts/smartproxy/forwarding/index.ts +52 -0
- package/ts/smartproxy/types/forwarding.types.ts +162 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
2
|
import type { IDomainConfig, ISmartProxyOptions } from './classes.pp.interfaces.js';
|
|
3
|
+
import type { ForwardingType, IForwardConfig, IForwardingHandler } from './types/forwarding.types.js';
|
|
4
|
+
import { ForwardingHandlerFactory } from './forwarding/forwarding.factory.js';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Manages domain configurations and target selection
|
|
@@ -7,7 +9,10 @@ import type { IDomainConfig, ISmartProxyOptions } from './classes.pp.interfaces.
|
|
|
7
9
|
export class DomainConfigManager {
|
|
8
10
|
// Track round-robin indices for domain configs
|
|
9
11
|
private domainTargetIndices: Map<IDomainConfig, number> = new Map();
|
|
10
|
-
|
|
12
|
+
|
|
13
|
+
// Cache forwarding handlers for each domain config
|
|
14
|
+
private forwardingHandlers: Map<IDomainConfig, IForwardingHandler> = new Map();
|
|
15
|
+
|
|
11
16
|
constructor(private settings: ISmartProxyOptions) {}
|
|
12
17
|
|
|
13
18
|
/**
|
|
@@ -15,7 +20,7 @@ export class DomainConfigManager {
|
|
|
15
20
|
*/
|
|
16
21
|
public updateDomainConfigs(newDomainConfigs: IDomainConfig[]): void {
|
|
17
22
|
this.settings.domainConfigs = newDomainConfigs;
|
|
18
|
-
|
|
23
|
+
|
|
19
24
|
// Reset target indices for removed configs
|
|
20
25
|
const currentConfigSet = new Set(newDomainConfigs);
|
|
21
26
|
for (const [config] of this.domainTargetIndices) {
|
|
@@ -23,6 +28,31 @@ export class DomainConfigManager {
|
|
|
23
28
|
this.domainTargetIndices.delete(config);
|
|
24
29
|
}
|
|
25
30
|
}
|
|
31
|
+
|
|
32
|
+
// Clear handlers for removed configs and create handlers for new configs
|
|
33
|
+
const handlersToRemove: IDomainConfig[] = [];
|
|
34
|
+
for (const [config] of this.forwardingHandlers) {
|
|
35
|
+
if (!currentConfigSet.has(config)) {
|
|
36
|
+
handlersToRemove.push(config);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Remove handlers that are no longer needed
|
|
41
|
+
for (const config of handlersToRemove) {
|
|
42
|
+
this.forwardingHandlers.delete(config);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Create handlers for new configs
|
|
46
|
+
for (const config of newDomainConfigs) {
|
|
47
|
+
if (!this.forwardingHandlers.has(config)) {
|
|
48
|
+
try {
|
|
49
|
+
const handler = this.createForwardingHandler(config);
|
|
50
|
+
this.forwardingHandlers.set(config, handler);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.log(`Error creating forwarding handler for domain ${config.domains.join(', ')}: ${err}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
26
56
|
}
|
|
27
57
|
|
|
28
58
|
/**
|
|
@@ -48,10 +78,12 @@ export class DomainConfigManager {
|
|
|
48
78
|
*/
|
|
49
79
|
public findDomainConfigForPort(port: number): IDomainConfig | undefined {
|
|
50
80
|
return this.settings.domainConfigs.find(
|
|
51
|
-
(domain) =>
|
|
52
|
-
domain.portRanges
|
|
53
|
-
|
|
54
|
-
|
|
81
|
+
(domain) => {
|
|
82
|
+
const portRanges = domain.forwarding?.advanced?.portRanges;
|
|
83
|
+
return portRanges &&
|
|
84
|
+
portRanges.length > 0 &&
|
|
85
|
+
this.isPortInRanges(port, portRanges);
|
|
86
|
+
}
|
|
55
87
|
);
|
|
56
88
|
}
|
|
57
89
|
|
|
@@ -66,48 +98,102 @@ export class DomainConfigManager {
|
|
|
66
98
|
* Get target IP with round-robin support
|
|
67
99
|
*/
|
|
68
100
|
public getTargetIP(domainConfig: IDomainConfig): string {
|
|
69
|
-
|
|
101
|
+
const targetHosts = Array.isArray(domainConfig.forwarding.target.host)
|
|
102
|
+
? domainConfig.forwarding.target.host
|
|
103
|
+
: [domainConfig.forwarding.target.host];
|
|
104
|
+
|
|
105
|
+
if (targetHosts.length > 0) {
|
|
70
106
|
const currentIndex = this.domainTargetIndices.get(domainConfig) || 0;
|
|
71
|
-
const ip =
|
|
107
|
+
const ip = targetHosts[currentIndex % targetHosts.length];
|
|
72
108
|
this.domainTargetIndices.set(domainConfig, currentIndex + 1);
|
|
73
109
|
return ip;
|
|
74
110
|
}
|
|
75
|
-
|
|
111
|
+
|
|
76
112
|
return this.settings.targetIP || 'localhost';
|
|
77
113
|
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get target host with round-robin support (for tests)
|
|
117
|
+
* This is just an alias for getTargetIP for easier test compatibility
|
|
118
|
+
*/
|
|
119
|
+
public getTargetHost(domainConfig: IDomainConfig): string {
|
|
120
|
+
return this.getTargetIP(domainConfig);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get target port from domain config
|
|
125
|
+
*/
|
|
126
|
+
public getTargetPort(domainConfig: IDomainConfig, defaultPort: number): number {
|
|
127
|
+
return domainConfig.forwarding.target.port || defaultPort;
|
|
128
|
+
}
|
|
78
129
|
|
|
79
130
|
/**
|
|
80
131
|
* Checks if a domain should use NetworkProxy
|
|
81
132
|
*/
|
|
82
133
|
public shouldUseNetworkProxy(domainConfig: IDomainConfig): boolean {
|
|
83
|
-
|
|
134
|
+
const forwardingType = this.getForwardingType(domainConfig);
|
|
135
|
+
return forwardingType === 'https-terminate-to-http' ||
|
|
136
|
+
forwardingType === 'https-terminate-to-https';
|
|
84
137
|
}
|
|
85
|
-
|
|
138
|
+
|
|
86
139
|
/**
|
|
87
140
|
* Gets the NetworkProxy port for a domain
|
|
88
141
|
*/
|
|
89
142
|
public getNetworkProxyPort(domainConfig: IDomainConfig): number | undefined {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
143
|
+
// First check if we should use NetworkProxy at all
|
|
144
|
+
if (!this.shouldUseNetworkProxy(domainConfig)) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return domainConfig.forwarding.advanced?.networkProxyPort || this.settings.networkProxyPort;
|
|
93
149
|
}
|
|
94
150
|
|
|
95
151
|
/**
|
|
96
152
|
* Get effective allowed and blocked IPs for a domain
|
|
153
|
+
*
|
|
154
|
+
* This method combines domain-specific security rules from the forwarding configuration
|
|
155
|
+
* with global security defaults when necessary.
|
|
97
156
|
*/
|
|
98
157
|
public getEffectiveIPRules(domainConfig: IDomainConfig): {
|
|
99
158
|
allowedIPs: string[],
|
|
100
159
|
blockedIPs: string[]
|
|
101
160
|
} {
|
|
161
|
+
// Start with empty arrays
|
|
162
|
+
const allowedIPs: string[] = [];
|
|
163
|
+
const blockedIPs: string[] = [];
|
|
164
|
+
|
|
165
|
+
// Add IPs from forwarding security settings if available
|
|
166
|
+
if (domainConfig.forwarding?.security?.allowedIps) {
|
|
167
|
+
allowedIPs.push(...domainConfig.forwarding.security.allowedIps);
|
|
168
|
+
} else {
|
|
169
|
+
// If no allowed IPs are specified in forwarding config and global defaults exist, use them
|
|
170
|
+
if (this.settings.defaultAllowedIPs && this.settings.defaultAllowedIPs.length > 0) {
|
|
171
|
+
allowedIPs.push(...this.settings.defaultAllowedIPs);
|
|
172
|
+
} else {
|
|
173
|
+
// Default to allow all if no specific rules
|
|
174
|
+
allowedIPs.push('*');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Add blocked IPs from forwarding security settings if available
|
|
179
|
+
if (domainConfig.forwarding?.security?.blockedIps) {
|
|
180
|
+
blockedIPs.push(...domainConfig.forwarding.security.blockedIps);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Always add global blocked IPs, even if domain has its own rules
|
|
184
|
+
// This ensures that global blocks take precedence
|
|
185
|
+
if (this.settings.defaultBlockedIPs && this.settings.defaultBlockedIPs.length > 0) {
|
|
186
|
+
// Add only unique IPs that aren't already in the list
|
|
187
|
+
for (const ip of this.settings.defaultBlockedIPs) {
|
|
188
|
+
if (!blockedIPs.includes(ip)) {
|
|
189
|
+
blockedIPs.push(ip);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
102
194
|
return {
|
|
103
|
-
allowedIPs
|
|
104
|
-
|
|
105
|
-
...(this.settings.defaultAllowedIPs || [])
|
|
106
|
-
],
|
|
107
|
-
blockedIPs: [
|
|
108
|
-
...(domainConfig.blockedIPs || []),
|
|
109
|
-
...(this.settings.defaultBlockedIPs || [])
|
|
110
|
-
]
|
|
195
|
+
allowedIPs,
|
|
196
|
+
blockedIPs
|
|
111
197
|
};
|
|
112
198
|
}
|
|
113
199
|
|
|
@@ -115,9 +201,97 @@ export class DomainConfigManager {
|
|
|
115
201
|
* Get connection timeout for a domain
|
|
116
202
|
*/
|
|
117
203
|
public getConnectionTimeout(domainConfig?: IDomainConfig): number {
|
|
118
|
-
if (domainConfig?.
|
|
119
|
-
return domainConfig.
|
|
204
|
+
if (domainConfig?.forwarding.advanced?.timeout) {
|
|
205
|
+
return domainConfig.forwarding.advanced.timeout;
|
|
120
206
|
}
|
|
207
|
+
|
|
121
208
|
return this.settings.maxConnectionLifetime || 86400000; // 24 hours default
|
|
122
209
|
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Creates a forwarding handler for a domain configuration
|
|
213
|
+
*/
|
|
214
|
+
private createForwardingHandler(domainConfig: IDomainConfig): IForwardingHandler {
|
|
215
|
+
// Create a new handler using the factory
|
|
216
|
+
const handler = ForwardingHandlerFactory.createHandler(domainConfig.forwarding);
|
|
217
|
+
|
|
218
|
+
// Initialize the handler
|
|
219
|
+
handler.initialize().catch(err => {
|
|
220
|
+
console.log(`Error initializing forwarding handler for ${domainConfig.domains.join(', ')}: ${err}`);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return handler;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Gets a forwarding handler for a domain config
|
|
228
|
+
* If no handler exists, creates one
|
|
229
|
+
*/
|
|
230
|
+
public getForwardingHandler(domainConfig: IDomainConfig): IForwardingHandler {
|
|
231
|
+
// If we already have a handler, return it
|
|
232
|
+
if (this.forwardingHandlers.has(domainConfig)) {
|
|
233
|
+
return this.forwardingHandlers.get(domainConfig)!;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Otherwise create a new handler
|
|
237
|
+
const handler = this.createForwardingHandler(domainConfig);
|
|
238
|
+
this.forwardingHandlers.set(domainConfig, handler);
|
|
239
|
+
|
|
240
|
+
return handler;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Gets the forwarding type for a domain config
|
|
245
|
+
*/
|
|
246
|
+
public getForwardingType(domainConfig?: IDomainConfig): ForwardingType | undefined {
|
|
247
|
+
if (!domainConfig?.forwarding) return undefined;
|
|
248
|
+
return domainConfig.forwarding.type;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Checks if the forwarding type requires TLS termination
|
|
253
|
+
*/
|
|
254
|
+
public requiresTlsTermination(domainConfig?: IDomainConfig): boolean {
|
|
255
|
+
if (!domainConfig) return false;
|
|
256
|
+
|
|
257
|
+
const forwardingType = this.getForwardingType(domainConfig);
|
|
258
|
+
return forwardingType === 'https-terminate-to-http' ||
|
|
259
|
+
forwardingType === 'https-terminate-to-https';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Checks if the forwarding type supports HTTP
|
|
264
|
+
*/
|
|
265
|
+
public supportsHttp(domainConfig?: IDomainConfig): boolean {
|
|
266
|
+
if (!domainConfig) return false;
|
|
267
|
+
|
|
268
|
+
const forwardingType = this.getForwardingType(domainConfig);
|
|
269
|
+
|
|
270
|
+
// HTTP-only always supports HTTP
|
|
271
|
+
if (forwardingType === 'http-only') return true;
|
|
272
|
+
|
|
273
|
+
// For termination types, check the HTTP settings
|
|
274
|
+
if (forwardingType === 'https-terminate-to-http' ||
|
|
275
|
+
forwardingType === 'https-terminate-to-https') {
|
|
276
|
+
// HTTP is supported by default for termination types
|
|
277
|
+
return domainConfig.forwarding?.http?.enabled !== false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// HTTPS-passthrough doesn't support HTTP
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Checks if HTTP requests should be redirected to HTTPS
|
|
286
|
+
*/
|
|
287
|
+
public shouldRedirectToHttps(domainConfig?: IDomainConfig): boolean {
|
|
288
|
+
if (!domainConfig?.forwarding) return false;
|
|
289
|
+
|
|
290
|
+
// Only check for redirect if HTTP is enabled
|
|
291
|
+
if (this.supportsHttp(domainConfig)) {
|
|
292
|
+
return !!domainConfig.forwarding.http?.redirectToHttps;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
123
297
|
}
|
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
|
+
import type { IForwardConfig } from './forwarding/index.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Provision object for static or HTTP-01 certificate
|
|
5
6
|
*/
|
|
6
7
|
export type ISmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
|
|
7
8
|
|
|
8
|
-
/** Domain configuration with
|
|
9
|
+
/** Domain configuration with forwarding configuration */
|
|
9
10
|
export interface IDomainConfig {
|
|
10
11
|
domains: string[]; // Glob patterns for domain(s)
|
|
11
|
-
|
|
12
|
-
blockedIPs?: string[]; // Glob patterns for blocked IPs
|
|
13
|
-
targetIPs?: string[]; // If multiple targetIPs are given, use round robin.
|
|
14
|
-
portRanges?: Array<{ from: number; to: number }>; // Optional port ranges
|
|
15
|
-
// Allow domain-specific timeout override
|
|
16
|
-
connectionTimeout?: number; // Connection timeout override (ms)
|
|
17
|
-
|
|
18
|
-
// NetworkProxy integration options for this specific domain
|
|
19
|
-
useNetworkProxy?: boolean; // Whether to use NetworkProxy for this domain
|
|
20
|
-
networkProxyPort?: number; // Override default NetworkProxy port for this domain
|
|
12
|
+
forwarding: IForwardConfig; // Unified forwarding configuration
|
|
21
13
|
}
|
|
22
14
|
|
|
23
15
|
/** Port proxy settings including global allowed port ranges */
|
|
@@ -84,8 +84,10 @@ export class PortRangeManager {
|
|
|
84
84
|
} | undefined {
|
|
85
85
|
for (let i = 0; i < this.settings.domainConfigs.length; i++) {
|
|
86
86
|
const domain = this.settings.domainConfigs[i];
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
// Get port ranges from forwarding.advanced if available
|
|
88
|
+
const portRanges = domain.forwarding?.advanced?.portRanges;
|
|
89
|
+
if (portRanges && portRanges.length > 0) {
|
|
90
|
+
for (const range of portRanges) {
|
|
89
91
|
if (port >= range.from && port <= range.to) {
|
|
90
92
|
return { domainIndex: i, range };
|
|
91
93
|
}
|
|
@@ -129,17 +131,20 @@ export class PortRangeManager {
|
|
|
129
131
|
|
|
130
132
|
// Add domain-specific port ranges
|
|
131
133
|
for (const domain of this.settings.domainConfigs) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
// Get port ranges from forwarding.advanced
|
|
135
|
+
const portRanges = domain.forwarding?.advanced?.portRanges;
|
|
136
|
+
if (portRanges && portRanges.length > 0) {
|
|
137
|
+
for (const range of portRanges) {
|
|
134
138
|
for (let port = range.from; port <= range.to; port++) {
|
|
135
139
|
ports.add(port);
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
142
|
}
|
|
139
|
-
|
|
140
|
-
// Add domain-specific NetworkProxy port if configured
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
|
|
144
|
+
// Add domain-specific NetworkProxy port if configured in forwarding.advanced
|
|
145
|
+
const networkProxyPort = domain.forwarding?.advanced?.networkProxyPort;
|
|
146
|
+
if (networkProxyPort) {
|
|
147
|
+
ports.add(networkProxyPort);
|
|
143
148
|
}
|
|
144
149
|
}
|
|
145
150
|
|
|
@@ -170,8 +175,10 @@ export class PortRangeManager {
|
|
|
170
175
|
|
|
171
176
|
// Track domain-specific port ranges
|
|
172
177
|
for (const domain of this.settings.domainConfigs) {
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
// Get port ranges from forwarding.advanced
|
|
179
|
+
const portRanges = domain.forwarding?.advanced?.portRanges;
|
|
180
|
+
if (portRanges && portRanges.length > 0) {
|
|
181
|
+
for (const range of portRanges) {
|
|
175
182
|
for (let port = range.from; port <= range.to; port++) {
|
|
176
183
|
if (!portMappings.has(port)) {
|
|
177
184
|
portMappings.set(port, []);
|
|
@@ -63,45 +63,69 @@ export class SecurityManager {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
* Check if an IP is
|
|
66
|
+
* Check if an IP is authorized using forwarding security rules
|
|
67
|
+
*
|
|
68
|
+
* This method is used to determine if an IP is allowed to connect, based on security
|
|
69
|
+
* rules configured in the forwarding configuration. The allowed and blocked IPs are
|
|
70
|
+
* typically derived from domain.forwarding.security.allowedIps and blockedIps through
|
|
71
|
+
* DomainConfigManager.getEffectiveIPRules().
|
|
72
|
+
*
|
|
73
|
+
* @param ip - The IP address to check
|
|
74
|
+
* @param allowedIPs - Array of allowed IP patterns from forwarding.security.allowedIps
|
|
75
|
+
* @param blockedIPs - Array of blocked IP patterns from forwarding.security.blockedIps
|
|
76
|
+
* @returns true if IP is authorized, false if blocked
|
|
67
77
|
*/
|
|
68
78
|
public isIPAuthorized(ip: string, allowedIPs: string[], blockedIPs: string[] = []): boolean {
|
|
69
79
|
// Skip IP validation if allowedIPs is empty
|
|
70
80
|
if (!ip || (allowedIPs.length === 0 && blockedIPs.length === 0)) {
|
|
71
81
|
return true;
|
|
72
82
|
}
|
|
73
|
-
|
|
74
|
-
// First check if IP is blocked
|
|
83
|
+
|
|
84
|
+
// First check if IP is blocked - blocked IPs take precedence
|
|
75
85
|
if (blockedIPs.length > 0 && this.isGlobIPMatch(ip, blockedIPs)) {
|
|
76
86
|
return false;
|
|
77
87
|
}
|
|
78
|
-
|
|
88
|
+
|
|
79
89
|
// Then check if IP is allowed
|
|
80
90
|
return this.isGlobIPMatch(ip, allowedIPs);
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
/**
|
|
84
|
-
* Check if the IP matches any of the glob patterns
|
|
94
|
+
* Check if the IP matches any of the glob patterns from security configuration
|
|
95
|
+
*
|
|
96
|
+
* This method checks IP addresses against glob patterns and handles IPv4/IPv6 normalization.
|
|
97
|
+
* It's used to implement IP filtering based on the forwarding.security configuration.
|
|
98
|
+
*
|
|
99
|
+
* @param ip - The IP address to check
|
|
100
|
+
* @param patterns - Array of glob patterns from forwarding.security.allowedIps or blockedIps
|
|
101
|
+
* @returns true if IP matches any pattern, false otherwise
|
|
85
102
|
*/
|
|
86
103
|
private isGlobIPMatch(ip: string, patterns: string[]): boolean {
|
|
87
104
|
if (!ip || !patterns || patterns.length === 0) return false;
|
|
88
105
|
|
|
106
|
+
// Handle IPv4/IPv6 normalization for proper matching
|
|
89
107
|
const normalizeIP = (ip: string): string[] => {
|
|
90
108
|
if (!ip) return [];
|
|
109
|
+
// Handle IPv4-mapped IPv6 addresses (::ffff:127.0.0.1)
|
|
91
110
|
if (ip.startsWith('::ffff:')) {
|
|
92
111
|
const ipv4 = ip.slice(7);
|
|
93
112
|
return [ip, ipv4];
|
|
94
113
|
}
|
|
114
|
+
// Handle IPv4 addresses by also checking IPv4-mapped form
|
|
95
115
|
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(ip)) {
|
|
96
116
|
return [ip, `::ffff:${ip}`];
|
|
97
117
|
}
|
|
98
118
|
return [ip];
|
|
99
119
|
};
|
|
100
120
|
|
|
121
|
+
// Normalize the IP being checked
|
|
101
122
|
const normalizedIPVariants = normalizeIP(ip);
|
|
102
123
|
if (normalizedIPVariants.length === 0) return false;
|
|
103
124
|
|
|
125
|
+
// Normalize the pattern IPs for consistent comparison
|
|
104
126
|
const expandedPatterns = patterns.flatMap(normalizeIP);
|
|
127
|
+
|
|
128
|
+
// Check for any match between normalized IP variants and patterns
|
|
105
129
|
return normalizedIPVariants.some((ipVariant) =>
|
|
106
130
|
expandedPatterns.some((pattern) => plugins.minimatch(ipVariant, pattern))
|
|
107
131
|
);
|
|
@@ -61,9 +61,9 @@ export class TimeoutManager {
|
|
|
61
61
|
* Calculate effective max lifetime based on connection type
|
|
62
62
|
*/
|
|
63
63
|
public getEffectiveMaxLifetime(record: IConnectionRecord): number {
|
|
64
|
-
// Use domain-specific timeout if available
|
|
65
|
-
const baseTimeout = record.domainConfig?.
|
|
66
|
-
this.settings.maxConnectionLifetime ||
|
|
64
|
+
// Use domain-specific timeout from forwarding.advanced if available
|
|
65
|
+
const baseTimeout = record.domainConfig?.forwarding?.advanced?.timeout ||
|
|
66
|
+
this.settings.maxConnectionLifetime ||
|
|
67
67
|
86400000; // 24 hours default
|
|
68
68
|
|
|
69
69
|
// For immortal keep-alive connections, use an extremely long lifetime
|
|
@@ -12,6 +12,8 @@ import { Port80Handler } from '../port80handler/classes.port80handler.js';
|
|
|
12
12
|
import { CertProvisioner } from './classes.pp.certprovisioner.js';
|
|
13
13
|
import type { ICertificateData } from '../common/types.js';
|
|
14
14
|
import { buildPort80Handler } from '../common/acmeFactory.js';
|
|
15
|
+
import type { ForwardingType } from './types/forwarding.types.js';
|
|
16
|
+
import { createPort80HandlerOptions } from '../common/port80-adapter.js';
|
|
15
17
|
|
|
16
18
|
import type { ISmartProxyOptions, IDomainConfig } from './classes.pp.interfaces.js';
|
|
17
19
|
export type { ISmartProxyOptions as IPortProxySettings, IDomainConfig };
|
|
@@ -156,11 +158,44 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
156
158
|
return;
|
|
157
159
|
}
|
|
158
160
|
|
|
161
|
+
// Process domain configs
|
|
162
|
+
// Note: ensureForwardingConfig is no longer needed since forwarding is now required
|
|
163
|
+
|
|
164
|
+
// Initialize domain config manager with the processed configs
|
|
165
|
+
this.domainConfigManager.updateDomainConfigs(this.settings.domainConfigs);
|
|
166
|
+
|
|
159
167
|
// Initialize Port80Handler if enabled
|
|
160
168
|
await this.initializePort80Handler();
|
|
169
|
+
|
|
161
170
|
// Initialize CertProvisioner for unified certificate workflows
|
|
162
171
|
if (this.port80Handler) {
|
|
163
172
|
const acme = this.settings.acme!;
|
|
173
|
+
|
|
174
|
+
// Convert domain forwards to use the new forwarding system if possible
|
|
175
|
+
const domainForwards = acme.domainForwards?.map(f => {
|
|
176
|
+
// If the domain has a forwarding config in domainConfigs, use that
|
|
177
|
+
const domainConfig = this.settings.domainConfigs.find(
|
|
178
|
+
dc => dc.domains.some(d => d === f.domain)
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
if (domainConfig?.forwarding) {
|
|
182
|
+
return {
|
|
183
|
+
domain: f.domain,
|
|
184
|
+
forwardConfig: f.forwardConfig,
|
|
185
|
+
acmeForwardConfig: f.acmeForwardConfig,
|
|
186
|
+
sslRedirect: f.sslRedirect || domainConfig.forwarding.http?.redirectToHttps || false
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Otherwise use the existing configuration
|
|
191
|
+
return {
|
|
192
|
+
domain: f.domain,
|
|
193
|
+
forwardConfig: f.forwardConfig,
|
|
194
|
+
acmeForwardConfig: f.acmeForwardConfig,
|
|
195
|
+
sslRedirect: f.sslRedirect || false
|
|
196
|
+
};
|
|
197
|
+
}) || [];
|
|
198
|
+
|
|
164
199
|
this.certProvisioner = new CertProvisioner(
|
|
165
200
|
this.settings.domainConfigs,
|
|
166
201
|
this.port80Handler,
|
|
@@ -169,13 +204,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
169
204
|
acme.renewThresholdDays!,
|
|
170
205
|
acme.renewCheckIntervalHours!,
|
|
171
206
|
acme.autoRenew!,
|
|
172
|
-
|
|
173
|
-
domain: f.domain,
|
|
174
|
-
forwardConfig: f.forwardConfig,
|
|
175
|
-
acmeForwardConfig: f.acmeForwardConfig,
|
|
176
|
-
sslRedirect: f.sslRedirect || false
|
|
177
|
-
})) || []
|
|
207
|
+
domainForwards
|
|
178
208
|
);
|
|
209
|
+
|
|
179
210
|
this.certProvisioner.on('certificate', (certData) => {
|
|
180
211
|
this.emit('certificate', {
|
|
181
212
|
domain: certData.domain,
|
|
@@ -186,6 +217,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
186
217
|
isRenewal: certData.isRenewal
|
|
187
218
|
});
|
|
188
219
|
});
|
|
220
|
+
|
|
189
221
|
await this.certProvisioner.start();
|
|
190
222
|
console.log('CertProvisioner started');
|
|
191
223
|
}
|
|
@@ -378,21 +410,41 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
378
410
|
*/
|
|
379
411
|
public async updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void> {
|
|
380
412
|
console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`);
|
|
381
|
-
|
|
413
|
+
|
|
382
414
|
// Update domain configs in DomainConfigManager
|
|
383
415
|
this.domainConfigManager.updateDomainConfigs(newDomainConfigs);
|
|
384
|
-
|
|
416
|
+
|
|
385
417
|
// If NetworkProxy is initialized, resync the configurations
|
|
386
418
|
if (this.networkProxyBridge.getNetworkProxy()) {
|
|
387
419
|
await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
|
|
388
420
|
}
|
|
389
|
-
|
|
390
|
-
// If Port80Handler is running, provision certificates
|
|
421
|
+
|
|
422
|
+
// If Port80Handler is running, provision certificates based on forwarding type
|
|
391
423
|
if (this.port80Handler && this.settings.acme?.enabled) {
|
|
392
424
|
for (const domainConfig of newDomainConfigs) {
|
|
425
|
+
// Skip certificate provisioning for http-only or passthrough configs that don't need certs
|
|
426
|
+
const forwardingType = domainConfig.forwarding.type;
|
|
427
|
+
const needsCertificate =
|
|
428
|
+
forwardingType === 'https-terminate-to-http' ||
|
|
429
|
+
forwardingType === 'https-terminate-to-https';
|
|
430
|
+
|
|
431
|
+
// Skip certificate provisioning if ACME is explicitly disabled for this domain
|
|
432
|
+
const acmeDisabled = domainConfig.forwarding.acme?.enabled === false;
|
|
433
|
+
|
|
434
|
+
if (!needsCertificate || acmeDisabled) {
|
|
435
|
+
if (this.settings.enableDetailedLogging) {
|
|
436
|
+
console.log(`Skipping certificate provisioning for ${domainConfig.domains.join(', ')} (${forwardingType})`);
|
|
437
|
+
}
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
|
|
393
441
|
for (const domain of domainConfig.domains) {
|
|
394
442
|
const isWildcard = domain.includes('*');
|
|
395
443
|
let provision: string | plugins.tsclass.network.ICert = 'http01';
|
|
444
|
+
|
|
445
|
+
// Check for ACME forwarding configuration in the domain
|
|
446
|
+
const forwardAcmeChallenges = domainConfig.forwarding.acme?.forwardChallenges;
|
|
447
|
+
|
|
396
448
|
if (this.settings.certProvisionFunction) {
|
|
397
449
|
try {
|
|
398
450
|
provision = await this.settings.certProvisionFunction(domain);
|
|
@@ -403,16 +455,17 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
403
455
|
console.warn(`Skipping wildcard domain without certProvisionFunction: ${domain}`);
|
|
404
456
|
continue;
|
|
405
457
|
}
|
|
458
|
+
|
|
406
459
|
if (provision === 'http01') {
|
|
407
460
|
if (isWildcard) {
|
|
408
461
|
console.warn(`Skipping HTTP-01 for wildcard domain: ${domain}`);
|
|
409
462
|
continue;
|
|
410
463
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
464
|
+
|
|
465
|
+
// Create Port80Handler options from the forwarding configuration
|
|
466
|
+
const port80Config = createPort80HandlerOptions(domain, domainConfig.forwarding);
|
|
467
|
+
|
|
468
|
+
this.port80Handler.addDomain(port80Config);
|
|
416
469
|
console.log(`Registered domain ${domain} with Port80Handler for HTTP-01`);
|
|
417
470
|
} else {
|
|
418
471
|
// Static certificate (e.g., DNS-01 provisioned) supports wildcards
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IForwardConfig } from '../types/forwarding.types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Domain configuration with unified forwarding configuration
|
|
5
|
+
*/
|
|
6
|
+
export interface IDomainConfig {
|
|
7
|
+
// Core properties - domain patterns
|
|
8
|
+
domains: string[];
|
|
9
|
+
|
|
10
|
+
// Unified forwarding configuration
|
|
11
|
+
forwarding: IForwardConfig;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Helper function to create a domain configuration
|
|
16
|
+
*/
|
|
17
|
+
export function createDomainConfig(
|
|
18
|
+
domains: string | string[],
|
|
19
|
+
forwarding: IForwardConfig
|
|
20
|
+
): IDomainConfig {
|
|
21
|
+
// Normalize domains to an array
|
|
22
|
+
const domainArray = Array.isArray(domains) ? domains : [domains];
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
domains: domainArray,
|
|
26
|
+
forwarding
|
|
27
|
+
};
|
|
28
|
+
}
|