@push.rocks/smartproxy 3.12.0 → 3.14.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 +3 -3
- package/dist_ts/classes.iptablesproxy.d.ts +19 -2
- package/dist_ts/classes.iptablesproxy.js +101 -9
- package/dist_ts/classes.portproxy.d.ts +2 -1
- package/dist_ts/classes.portproxy.js +40 -4
- package/npmextra.json +6 -2
- package/package.json +7 -3
- package/readme.md +145 -28
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/classes.iptablesproxy.ts +103 -8
- package/ts/classes.portproxy.ts +44 -3
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '3.
|
|
7
|
-
description: '
|
|
6
|
+
version: '3.14.0',
|
|
7
|
+
description: 'A robust and versatile proxy package designed to handle high workloads, offering features like SSL redirection, port proxying, WebSocket support, and customizable routing and authentication.'
|
|
8
8
|
};
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLGdNQUFnTTtDQUM5TSxDQUFBIn0=
|
|
@@ -1,23 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings for IPTablesProxy.
|
|
3
|
+
*/
|
|
1
4
|
export interface IIpTableProxySettings {
|
|
2
5
|
fromPort: number;
|
|
3
6
|
toPort: number;
|
|
4
7
|
toHost?: string;
|
|
5
8
|
preserveSourceIP?: boolean;
|
|
9
|
+
deleteOnExit?: boolean;
|
|
6
10
|
}
|
|
7
11
|
/**
|
|
8
12
|
* IPTablesProxy sets up iptables NAT rules to forward TCP traffic.
|
|
9
|
-
* It only supports basic port forwarding.
|
|
13
|
+
* It only supports basic port forwarding and uses iptables comments to tag rules.
|
|
10
14
|
*/
|
|
11
15
|
export declare class IPTablesProxy {
|
|
12
16
|
settings: IIpTableProxySettings;
|
|
13
17
|
private rulesInstalled;
|
|
18
|
+
private ruleTag;
|
|
14
19
|
constructor(settings: IIpTableProxySettings);
|
|
15
20
|
/**
|
|
16
21
|
* Sets up iptables rules for port forwarding.
|
|
22
|
+
* The rules are tagged with a unique comment so that they can be identified later.
|
|
17
23
|
*/
|
|
18
24
|
start(): Promise<void>;
|
|
19
25
|
/**
|
|
20
|
-
* Removes the iptables rules that were added in start().
|
|
26
|
+
* Removes the iptables rules that were added in start(), by matching the unique comment.
|
|
21
27
|
*/
|
|
22
28
|
stop(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Asynchronously cleans up any iptables rules in the nat table that were added by this module.
|
|
31
|
+
* It looks for rules with comments containing "IPTablesProxy:".
|
|
32
|
+
*/
|
|
33
|
+
static cleanSlate(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Synchronously cleans up any iptables rules in the nat table that were added by this module.
|
|
36
|
+
* It looks for rules with comments containing "IPTablesProxy:".
|
|
37
|
+
* This method is intended for use in process exit handlers.
|
|
38
|
+
*/
|
|
39
|
+
static cleanSlateSync(): void;
|
|
23
40
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { exec } from 'child_process';
|
|
1
|
+
import { exec, execSync } from 'child_process';
|
|
2
2
|
import { promisify } from 'util';
|
|
3
3
|
const execAsync = promisify(exec);
|
|
4
4
|
/**
|
|
5
5
|
* IPTablesProxy sets up iptables NAT rules to forward TCP traffic.
|
|
6
|
-
* It only supports basic port forwarding.
|
|
6
|
+
* It only supports basic port forwarding and uses iptables comments to tag rules.
|
|
7
7
|
*/
|
|
8
8
|
export class IPTablesProxy {
|
|
9
9
|
constructor(settings) {
|
|
@@ -12,12 +12,37 @@ export class IPTablesProxy {
|
|
|
12
12
|
...settings,
|
|
13
13
|
toHost: settings.toHost || 'localhost',
|
|
14
14
|
};
|
|
15
|
+
// Generate a unique identifier for the rules added by this instance.
|
|
16
|
+
this.ruleTag = `IPTablesProxy:${Date.now()}:${Math.random().toString(36).substr(2, 5)}`;
|
|
17
|
+
// If deleteOnExit is true, register cleanup handlers.
|
|
18
|
+
if (this.settings.deleteOnExit) {
|
|
19
|
+
const cleanup = () => {
|
|
20
|
+
try {
|
|
21
|
+
IPTablesProxy.cleanSlateSync();
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
console.error('Error cleaning iptables rules on exit:', err);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
process.on('exit', cleanup);
|
|
28
|
+
process.on('SIGINT', () => {
|
|
29
|
+
cleanup();
|
|
30
|
+
process.exit();
|
|
31
|
+
});
|
|
32
|
+
process.on('SIGTERM', () => {
|
|
33
|
+
cleanup();
|
|
34
|
+
process.exit();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
15
37
|
}
|
|
16
38
|
/**
|
|
17
39
|
* Sets up iptables rules for port forwarding.
|
|
40
|
+
* The rules are tagged with a unique comment so that they can be identified later.
|
|
18
41
|
*/
|
|
19
42
|
async start() {
|
|
20
|
-
const dnatCmd = `iptables -t nat -A PREROUTING -p tcp --dport ${this.settings.fromPort}
|
|
43
|
+
const dnatCmd = `iptables -t nat -A PREROUTING -p tcp --dport ${this.settings.fromPort} ` +
|
|
44
|
+
`-j DNAT --to-destination ${this.settings.toHost}:${this.settings.toPort} ` +
|
|
45
|
+
`-m comment --comment "${this.ruleTag}:DNAT"`;
|
|
21
46
|
try {
|
|
22
47
|
await execAsync(dnatCmd);
|
|
23
48
|
console.log(`Added iptables rule: ${dnatCmd}`);
|
|
@@ -27,8 +52,11 @@ export class IPTablesProxy {
|
|
|
27
52
|
console.error(`Failed to add iptables DNAT rule: ${err}`);
|
|
28
53
|
throw err;
|
|
29
54
|
}
|
|
55
|
+
// If preserveSourceIP is false, add a MASQUERADE rule.
|
|
30
56
|
if (!this.settings.preserveSourceIP) {
|
|
31
|
-
const masqueradeCmd = `iptables -t nat -A POSTROUTING -p tcp -d ${this.settings.toHost}
|
|
57
|
+
const masqueradeCmd = `iptables -t nat -A POSTROUTING -p tcp -d ${this.settings.toHost} ` +
|
|
58
|
+
`--dport ${this.settings.toPort} -j MASQUERADE ` +
|
|
59
|
+
`-m comment --comment "${this.ruleTag}:MASQ"`;
|
|
32
60
|
try {
|
|
33
61
|
await execAsync(masqueradeCmd);
|
|
34
62
|
console.log(`Added iptables rule: ${masqueradeCmd}`);
|
|
@@ -37,7 +65,9 @@ export class IPTablesProxy {
|
|
|
37
65
|
console.error(`Failed to add iptables MASQUERADE rule: ${err}`);
|
|
38
66
|
// Roll back the DNAT rule if MASQUERADE fails.
|
|
39
67
|
try {
|
|
40
|
-
const rollbackCmd = `iptables -t nat -D PREROUTING -p tcp --dport ${this.settings.fromPort}
|
|
68
|
+
const rollbackCmd = `iptables -t nat -D PREROUTING -p tcp --dport ${this.settings.fromPort} ` +
|
|
69
|
+
`-j DNAT --to-destination ${this.settings.toHost}:${this.settings.toPort} ` +
|
|
70
|
+
`-m comment --comment "${this.ruleTag}:DNAT"`;
|
|
41
71
|
await execAsync(rollbackCmd);
|
|
42
72
|
this.rulesInstalled = false;
|
|
43
73
|
}
|
|
@@ -49,12 +79,14 @@ export class IPTablesProxy {
|
|
|
49
79
|
}
|
|
50
80
|
}
|
|
51
81
|
/**
|
|
52
|
-
* Removes the iptables rules that were added in start().
|
|
82
|
+
* Removes the iptables rules that were added in start(), by matching the unique comment.
|
|
53
83
|
*/
|
|
54
84
|
async stop() {
|
|
55
85
|
if (!this.rulesInstalled)
|
|
56
86
|
return;
|
|
57
|
-
const dnatDelCmd = `iptables -t nat -D PREROUTING -p tcp --dport ${this.settings.fromPort}
|
|
87
|
+
const dnatDelCmd = `iptables -t nat -D PREROUTING -p tcp --dport ${this.settings.fromPort} ` +
|
|
88
|
+
`-j DNAT --to-destination ${this.settings.toHost}:${this.settings.toPort} ` +
|
|
89
|
+
`-m comment --comment "${this.ruleTag}:DNAT"`;
|
|
58
90
|
try {
|
|
59
91
|
await execAsync(dnatDelCmd);
|
|
60
92
|
console.log(`Removed iptables rule: ${dnatDelCmd}`);
|
|
@@ -63,7 +95,9 @@ export class IPTablesProxy {
|
|
|
63
95
|
console.error(`Failed to remove iptables DNAT rule: ${err}`);
|
|
64
96
|
}
|
|
65
97
|
if (!this.settings.preserveSourceIP) {
|
|
66
|
-
const masqueradeDelCmd = `iptables -t nat -D POSTROUTING -p tcp -d ${this.settings.toHost}
|
|
98
|
+
const masqueradeDelCmd = `iptables -t nat -D POSTROUTING -p tcp -d ${this.settings.toHost} ` +
|
|
99
|
+
`--dport ${this.settings.toPort} -j MASQUERADE ` +
|
|
100
|
+
`-m comment --comment "${this.ruleTag}:MASQ"`;
|
|
67
101
|
try {
|
|
68
102
|
await execAsync(masqueradeDelCmd);
|
|
69
103
|
console.log(`Removed iptables rule: ${masqueradeDelCmd}`);
|
|
@@ -74,5 +108,63 @@ export class IPTablesProxy {
|
|
|
74
108
|
}
|
|
75
109
|
this.rulesInstalled = false;
|
|
76
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Asynchronously cleans up any iptables rules in the nat table that were added by this module.
|
|
113
|
+
* It looks for rules with comments containing "IPTablesProxy:".
|
|
114
|
+
*/
|
|
115
|
+
static async cleanSlate() {
|
|
116
|
+
try {
|
|
117
|
+
const { stdout } = await execAsync('iptables-save -t nat');
|
|
118
|
+
const lines = stdout.split('\n');
|
|
119
|
+
const proxyLines = lines.filter(line => line.includes('IPTablesProxy:'));
|
|
120
|
+
for (const line of proxyLines) {
|
|
121
|
+
const trimmedLine = line.trim();
|
|
122
|
+
if (trimmedLine.startsWith('-A')) {
|
|
123
|
+
// Replace the "-A" with "-D" to form a deletion command.
|
|
124
|
+
const deleteRule = trimmedLine.replace('-A', '-D');
|
|
125
|
+
const cmd = `iptables -t nat ${deleteRule}`;
|
|
126
|
+
try {
|
|
127
|
+
await execAsync(cmd);
|
|
128
|
+
console.log(`Cleaned up iptables rule: ${cmd}`);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
console.error(`Failed to remove iptables rule: ${cmd}`, err);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
console.error(`Failed to run iptables-save: ${err}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Synchronously cleans up any iptables rules in the nat table that were added by this module.
|
|
142
|
+
* It looks for rules with comments containing "IPTablesProxy:".
|
|
143
|
+
* This method is intended for use in process exit handlers.
|
|
144
|
+
*/
|
|
145
|
+
static cleanSlateSync() {
|
|
146
|
+
try {
|
|
147
|
+
const stdout = execSync('iptables-save -t nat').toString();
|
|
148
|
+
const lines = stdout.split('\n');
|
|
149
|
+
const proxyLines = lines.filter(line => line.includes('IPTablesProxy:'));
|
|
150
|
+
for (const line of proxyLines) {
|
|
151
|
+
const trimmedLine = line.trim();
|
|
152
|
+
if (trimmedLine.startsWith('-A')) {
|
|
153
|
+
const deleteRule = trimmedLine.replace('-A', '-D');
|
|
154
|
+
const cmd = `iptables -t nat ${deleteRule}`;
|
|
155
|
+
try {
|
|
156
|
+
execSync(cmd);
|
|
157
|
+
console.log(`Cleaned up iptables rule: ${cmd}`);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
console.error(`Failed to remove iptables rule: ${cmd}`, err);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
console.error(`Failed to run iptables-save: ${err}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
77
169
|
}
|
|
78
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
170
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5pcHRhYmxlc3Byb3h5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5pcHRhYmxlc3Byb3h5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQy9DLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFakMsTUFBTSxTQUFTLEdBQUcsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0FBYWxDOzs7R0FHRztBQUNILE1BQU0sT0FBTyxhQUFhO0lBS3hCLFlBQVksUUFBK0I7UUFIbkMsbUJBQWMsR0FBWSxLQUFLLENBQUM7UUFJdEMsSUFBSSxDQUFDLFFBQVEsR0FBRztZQUNkLEdBQUcsUUFBUTtZQUNYLE1BQU0sRUFBRSxRQUFRLENBQUMsTUFBTSxJQUFJLFdBQVc7U0FDdkMsQ0FBQztRQUNGLHFFQUFxRTtRQUNyRSxJQUFJLENBQUMsT0FBTyxHQUFHLGlCQUFpQixJQUFJLENBQUMsR0FBRyxFQUFFLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFFeEYsc0RBQXNEO1FBQ3RELElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxZQUFZLEVBQUUsQ0FBQztZQUMvQixNQUFNLE9BQU8sR0FBRyxHQUFHLEVBQUU7Z0JBQ25CLElBQUksQ0FBQztvQkFDSCxhQUFhLENBQUMsY0FBYyxFQUFFLENBQUM7Z0JBQ2pDLENBQUM7Z0JBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztvQkFDYixPQUFPLENBQUMsS0FBSyxDQUFDLHdDQUF3QyxFQUFFLEdBQUcsQ0FBQyxDQUFDO2dCQUMvRCxDQUFDO1lBQ0gsQ0FBQyxDQUFDO1lBQ0YsT0FBTyxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLENB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
|
|
@@ -12,6 +12,7 @@ export interface IPortProxySettings extends plugins.tls.TlsOptions {
|
|
|
12
12
|
sniEnabled?: boolean;
|
|
13
13
|
defaultAllowedIPs?: string[];
|
|
14
14
|
preserveSourceIP?: boolean;
|
|
15
|
+
maxConnectionLifetime?: number;
|
|
15
16
|
}
|
|
16
17
|
export declare class PortProxy {
|
|
17
18
|
netServer: plugins.net.Server;
|
|
@@ -19,7 +20,7 @@ export declare class PortProxy {
|
|
|
19
20
|
private connectionRecords;
|
|
20
21
|
private connectionLogger;
|
|
21
22
|
private terminationStats;
|
|
22
|
-
constructor(
|
|
23
|
+
constructor(settingsArg: IPortProxySettings);
|
|
23
24
|
private incrementTerminationStat;
|
|
24
25
|
start(): Promise<void>;
|
|
25
26
|
stop(): Promise<void>;
|
|
@@ -61,7 +61,7 @@ function extractSNI(buffer) {
|
|
|
61
61
|
return undefined;
|
|
62
62
|
}
|
|
63
63
|
export class PortProxy {
|
|
64
|
-
constructor(
|
|
64
|
+
constructor(settingsArg) {
|
|
65
65
|
// Unified record tracking each connection pair.
|
|
66
66
|
this.connectionRecords = new Set();
|
|
67
67
|
this.connectionLogger = null;
|
|
@@ -70,8 +70,9 @@ export class PortProxy {
|
|
|
70
70
|
outgoing: {},
|
|
71
71
|
};
|
|
72
72
|
this.settings = {
|
|
73
|
-
...
|
|
74
|
-
toHost:
|
|
73
|
+
...settingsArg,
|
|
74
|
+
toHost: settingsArg.toHost || 'localhost',
|
|
75
|
+
maxConnectionLifetime: settingsArg.maxConnectionLifetime || 10000,
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
78
|
incrementTerminationStat(side, reason) {
|
|
@@ -121,6 +122,9 @@ export class PortProxy {
|
|
|
121
122
|
const cleanupOnce = () => {
|
|
122
123
|
if (!connectionRecord.connectionClosed) {
|
|
123
124
|
connectionRecord.connectionClosed = true;
|
|
125
|
+
if (connectionRecord.cleanupTimer) {
|
|
126
|
+
clearTimeout(connectionRecord.cleanupTimer);
|
|
127
|
+
}
|
|
124
128
|
cleanUpSockets(connectionRecord.incoming, connectionRecord.outgoing || undefined);
|
|
125
129
|
this.connectionRecords.delete(connectionRecord);
|
|
126
130
|
console.log(`Connection from ${remoteIP} terminated. Active connections: ${this.connectionRecords.size}`);
|
|
@@ -211,6 +215,7 @@ export class PortProxy {
|
|
|
211
215
|
socket.setTimeout(120000);
|
|
212
216
|
socket.pipe(targetSocket);
|
|
213
217
|
targetSocket.pipe(socket);
|
|
218
|
+
// Attach error and close handlers.
|
|
214
219
|
socket.on('error', handleError('incoming'));
|
|
215
220
|
targetSocket.on('error', handleError('outgoing'));
|
|
216
221
|
socket.on('close', handleClose('incoming'));
|
|
@@ -233,6 +238,37 @@ export class PortProxy {
|
|
|
233
238
|
});
|
|
234
239
|
socket.on('end', handleClose('incoming'));
|
|
235
240
|
targetSocket.on('end', handleClose('outgoing'));
|
|
241
|
+
// If maxConnectionLifetime is set, initialize a cleanup timer that will be reset on data flow.
|
|
242
|
+
if (this.settings.maxConnectionLifetime) {
|
|
243
|
+
let incomingActive = false;
|
|
244
|
+
let outgoingActive = false;
|
|
245
|
+
const resetCleanupTimer = () => {
|
|
246
|
+
if (this.settings.maxConnectionLifetime) {
|
|
247
|
+
if (connectionRecord.cleanupTimer) {
|
|
248
|
+
clearTimeout(connectionRecord.cleanupTimer);
|
|
249
|
+
}
|
|
250
|
+
connectionRecord.cleanupTimer = setTimeout(() => {
|
|
251
|
+
console.log(`Connection from ${remoteIP} exceeded max lifetime with inactivity (${this.settings.maxConnectionLifetime}ms), forcing cleanup.`);
|
|
252
|
+
cleanupOnce();
|
|
253
|
+
}, this.settings.maxConnectionLifetime);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
// Start the cleanup timer.
|
|
257
|
+
resetCleanupTimer();
|
|
258
|
+
// Listen for data events on both sides and reset the timer when both are active.
|
|
259
|
+
socket.on('data', () => {
|
|
260
|
+
incomingActive = true;
|
|
261
|
+
if (incomingActive && outgoingActive) {
|
|
262
|
+
resetCleanupTimer();
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
targetSocket.on('data', () => {
|
|
266
|
+
outgoingActive = true;
|
|
267
|
+
if (incomingActive && outgoingActive) {
|
|
268
|
+
resetCleanupTimer();
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
236
272
|
};
|
|
237
273
|
if (this.settings.sniEnabled) {
|
|
238
274
|
socket.setTimeout(5000, () => {
|
|
@@ -292,4 +328,4 @@ export class PortProxy {
|
|
|
292
328
|
await done.promise;
|
|
293
329
|
}
|
|
294
330
|
}
|
|
295
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5wb3J0cHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLnBvcnRwcm94eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQWtCeEM7Ozs7R0FJRztBQUNILFNBQVMsVUFBVSxDQUFDLE1BQWM7SUFDaEMsSUFBSSxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQ2YsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUM7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUV4QyxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3ZDLElBQUksVUFBVSxLQUFLLEVBQUU7UUFBRSxPQUFPLFNBQVMsQ0FBQyxDQUFDLGlCQUFpQjtJQUUxRCxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzVDLElBQUksTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLEdBQUcsWUFBWTtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBRXZELE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDWCxNQUFNLGFBQWEsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQy9DLElBQUksYUFBYSxLQUFLLENBQUM7UUFBRSxPQUFPLFNBQVMsQ0FBQyxDQUFDLGtCQUFrQjtJQUU3RCxNQUFNLElBQUksQ0FBQyxDQUFDLENBQUMsd0NBQXdDO0lBQ3JELE1BQU0sSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsaUNBQWlDO0lBRW5ELE1BQU0sZUFBZSxHQUFHLE1BQU0sQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDakQsTUFBTSxJQUFJLENBQUMsR0FBRyxlQUFlLENBQUMsQ0FBQyxrQkFBa0I7SUFFakQsTUFBTSxrQkFBa0IsR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FB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
|
|
331
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5wb3J0cHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLnBvcnRwcm94eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQW1CeEM7Ozs7R0FJRztBQUNILFNBQVMsVUFBVSxDQUFDLE1BQWM7SUFDaEMsSUFBSSxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQ2YsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUM7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUV4QyxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3ZDLElBQUksVUFBVSxLQUFLLEVBQUU7UUFBRSxPQUFPLFNBQVMsQ0FBQyxDQUFDLGlCQUFpQjtJQUUxRCxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzVDLElBQUksTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLEdBQUcsWUFBWTtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBRXZELE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDWCxNQUFNLGFBQWEsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQy9DLElBQUksYUFBYSxLQUFLLENBQUM7UUFBRSxPQUFPLFNBQVMsQ0FBQyxDQUFDLGtCQUFrQjtJQUU3RCxNQUFNLElBQUksQ0FBQyxDQUFDLENBQUMsd0NBQXdDO0lBQ3JELE1BQU0sSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsaUNBQWlDO0lBRW5ELE1BQU0sZUFBZSxHQUFHLE1BQU0sQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDakQsTUFBTSxJQUFJLENBQUMsR0FBRyxlQUFlLENBQUMsQ0FBQyxrQkFBa0I7SUFFakQsTUFBTSxrQkFBa0IsR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FB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
|
package/npmextra.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"githost": "code.foss.global",
|
|
6
6
|
"gitscope": "push.rocks",
|
|
7
7
|
"gitrepo": "smartproxy",
|
|
8
|
-
"description": "
|
|
8
|
+
"description": "A robust and versatile proxy package designed to handle high workloads, offering features like SSL redirection, port proxying, WebSocket support, and customizable routing and authentication.",
|
|
9
9
|
"npmPackagename": "@push.rocks/smartproxy",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"projectDomain": "push.rocks",
|
|
@@ -20,7 +20,11 @@
|
|
|
20
20
|
"ssl redirect",
|
|
21
21
|
"port mapping",
|
|
22
22
|
"reverse proxy",
|
|
23
|
-
"authentication"
|
|
23
|
+
"authentication",
|
|
24
|
+
"dynamic routing",
|
|
25
|
+
"sni",
|
|
26
|
+
"port forwarding",
|
|
27
|
+
"real-time applications"
|
|
24
28
|
]
|
|
25
29
|
}
|
|
26
30
|
},
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartproxy",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "A robust and versatile proxy package designed to handle high workloads, offering features like SSL redirection, port proxying, WebSocket support, and customizable routing and authentication.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
7
7
|
"typings": "dist_ts/index.d.ts",
|
|
8
8
|
"type": "module",
|
|
@@ -56,7 +56,11 @@
|
|
|
56
56
|
"ssl redirect",
|
|
57
57
|
"port mapping",
|
|
58
58
|
"reverse proxy",
|
|
59
|
-
"authentication"
|
|
59
|
+
"authentication",
|
|
60
|
+
"dynamic routing",
|
|
61
|
+
"sni",
|
|
62
|
+
"port forwarding",
|
|
63
|
+
"real-time applications"
|
|
60
64
|
],
|
|
61
65
|
"homepage": "https://code.foss.global/push.rocks/smartproxy#readme",
|
|
62
66
|
"repository": {
|
package/readme.md
CHANGED
|
@@ -14,11 +14,11 @@ This will add `@push.rocks/smartproxy` to your project's dependencies.
|
|
|
14
14
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
17
|
-
`@push.rocks/smartproxy` is a versatile package
|
|
17
|
+
`@push.rocks/smartproxy` is a comprehensive and versatile package designed to handle complex and high-volume proxying tasks efficiently. It includes features such as SSL redirection, port proxying, WebSocket support, and customizable routing and authentication mechanisms. This guide will provide a detailed walkthrough of how to harness these capabilities effectively.
|
|
18
18
|
|
|
19
|
-
###
|
|
19
|
+
### Initial Setup
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Before diving into specific features, let's start by configuring and setting up our basic proxy server:
|
|
22
22
|
|
|
23
23
|
```typescript
|
|
24
24
|
import { NetworkProxy } from '@push.rocks/smartproxy';
|
|
@@ -26,7 +26,7 @@ import { NetworkProxy } from '@push.rocks/smartproxy';
|
|
|
26
26
|
// Instantiate the NetworkProxy with desired options
|
|
27
27
|
const myNetworkProxy = new NetworkProxy({ port: 443 });
|
|
28
28
|
|
|
29
|
-
// Define
|
|
29
|
+
// Define reverse proxy configurations
|
|
30
30
|
const proxyConfigs = [
|
|
31
31
|
{
|
|
32
32
|
destinationIp: '127.0.0.1',
|
|
@@ -39,70 +39,187 @@ PRIVATE_KEY_CONTENT
|
|
|
39
39
|
CERTIFICATE_CONTENT
|
|
40
40
|
-----END CERTIFICATE-----`,
|
|
41
41
|
},
|
|
42
|
-
//
|
|
42
|
+
// More configurations can be added here
|
|
43
43
|
];
|
|
44
44
|
|
|
45
45
|
// Start the network proxy
|
|
46
46
|
await myNetworkProxy.start();
|
|
47
47
|
|
|
48
|
-
//
|
|
48
|
+
// Apply proxy configurations
|
|
49
49
|
await myNetworkProxy.updateProxyConfigs(proxyConfigs);
|
|
50
50
|
|
|
51
|
-
// Optionally
|
|
51
|
+
// Optionally add default headers to all responses
|
|
52
52
|
await myNetworkProxy.addDefaultHeaders({
|
|
53
53
|
'X-Powered-By': 'smartproxy',
|
|
54
54
|
});
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
###
|
|
57
|
+
### Configuring SSL Redirection
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
One essential capability of a robust proxy server is ensuring that all HTTP traffic is redirected to secure HTTPS endpoints. This can be effortlessly accomplished using the `SslRedirect` class within `smartproxy`. This class listens on port 80 (HTTP) and redirects all incoming requests to HTTPS:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { SslRedirect } from '@push.rocks/smartproxy';
|
|
63
|
+
|
|
64
|
+
// Instantiate the SslRedirect for listening on port 80
|
|
65
|
+
const mySslRedirect = new SslRedirect(80);
|
|
66
|
+
|
|
67
|
+
// Start listening and redirect HTTP traffic to HTTPS
|
|
68
|
+
await mySslRedirect.start();
|
|
69
|
+
|
|
70
|
+
// To stop redirection, you can use the following command:
|
|
71
|
+
await mySslRedirect.stop();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Handling Complex Networking with Port Proxy
|
|
75
|
+
|
|
76
|
+
Port proxying allows redirection of traffic from one port to another. This capability is crucial when dealing with services that need dynamic port forwarding, or when adapting to infrastructure changes without downtime. Smartproxy's `PortProxy` class handles this efficiently:
|
|
60
77
|
|
|
61
78
|
```typescript
|
|
62
79
|
import { PortProxy } from '@push.rocks/smartproxy';
|
|
63
80
|
|
|
64
|
-
// Create a PortProxy to forward traffic from port 5000 to
|
|
81
|
+
// Create a PortProxy to directly forward traffic from port 5000 to 3000
|
|
65
82
|
const myPortProxy = new PortProxy(5000, 3000);
|
|
66
83
|
|
|
67
|
-
//
|
|
84
|
+
// Initiate the port proxy
|
|
68
85
|
await myPortProxy.start();
|
|
69
86
|
|
|
70
|
-
// To stop the port proxy
|
|
87
|
+
// To stop the port proxy mechanism:
|
|
71
88
|
await myPortProxy.stop();
|
|
72
89
|
```
|
|
73
90
|
|
|
74
|
-
|
|
91
|
+
Additionally, smartproxy's port proxying can support intricate scenarios where different forwarding rules are configured based on domain names or allowed IPs:
|
|
75
92
|
|
|
76
|
-
|
|
93
|
+
```typescript
|
|
94
|
+
import { PortProxy } from '@push.rocks/smartproxy';
|
|
95
|
+
|
|
96
|
+
const myComplexPortProxy = new PortProxy({
|
|
97
|
+
fromPort: 6000,
|
|
98
|
+
toPort: 3000,
|
|
99
|
+
domains: [
|
|
100
|
+
{
|
|
101
|
+
domain: 'api.example.com',
|
|
102
|
+
allowedIPs: ['192.168.0.*', '127.0.0.1'],
|
|
103
|
+
targetIP: '192.168.1.100'
|
|
104
|
+
}
|
|
105
|
+
// Define more domain-specific rules if needed
|
|
106
|
+
],
|
|
107
|
+
sniEnabled: true, // if SNI (Server Name Indication) is desired
|
|
108
|
+
defaultAllowedIPs: ['*']);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Start listening for complex routing requests
|
|
112
|
+
await myComplexPortProxy.start();
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### WebSocket Support and Load Handling
|
|
116
|
+
|
|
117
|
+
With the advent of real-time applications, efficient WebSocket handling in proxies is crucial. Smartproxy integrates WebSocket support seamlessly, enabling it to proxy WebSocket traffic while maintaining security and performance:
|
|
77
118
|
|
|
78
119
|
```typescript
|
|
79
|
-
import {
|
|
120
|
+
import { NetworkProxy } from '@push.rocks/smartproxy';
|
|
80
121
|
|
|
81
|
-
|
|
82
|
-
const mySslRedirect = new SslRedirect(80);
|
|
122
|
+
const wsProxy = new NetworkProxy({ port: 443 });
|
|
83
123
|
|
|
84
|
-
//
|
|
85
|
-
|
|
124
|
+
// Assume reverse proxy configurations with WebSocket intentions
|
|
125
|
+
const wsProxyConfigs = [
|
|
126
|
+
{
|
|
127
|
+
destinationIp: '127.0.0.1',
|
|
128
|
+
destinationPort: '8080',
|
|
129
|
+
hostName: 'socket.example.com',
|
|
130
|
+
// Add further options such as keys for SSL if needed
|
|
131
|
+
}
|
|
132
|
+
];
|
|
86
133
|
|
|
87
|
-
//
|
|
88
|
-
await
|
|
134
|
+
// Start the network proxy with WebSocket capabilities
|
|
135
|
+
await wsProxy.start();
|
|
136
|
+
await wsProxy.updateProxyConfigs(wsProxyConfigs);
|
|
137
|
+
|
|
138
|
+
// Ensure WebSocket connections remain alive
|
|
139
|
+
wsProxy.heartbeatInterval = setInterval(() => {
|
|
140
|
+
// logic for keeping connections alive and healthy
|
|
141
|
+
}, 60000); // Every 60 seconds
|
|
142
|
+
|
|
143
|
+
// Gracefully handle server or connection errors to maintain uptime
|
|
144
|
+
wsProxy.httpsServer.on('error', (error) => console.log('Server Error:', error));
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Comprehensive Routing and Advanced Features
|
|
148
|
+
|
|
149
|
+
Smartproxy supports dynamic and customizable request routing based on the incoming request's destination. This feature enables extensive use-case scenarios, from simple API endpoint redirection to elaborate B2B service integrations:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { NetworkProxy } from '@push.rocks/smartproxy';
|
|
153
|
+
|
|
154
|
+
const dynamicRoutingProxy = new NetworkProxy({ port: 8443 });
|
|
155
|
+
dynamicRoutingProxy.router.setNewProxyConfigs([
|
|
156
|
+
{
|
|
157
|
+
destinationIp: '192.168.1.150',
|
|
158
|
+
destinationPort: '80',
|
|
159
|
+
hostName: 'dynamic.example.com',
|
|
160
|
+
authentication: {
|
|
161
|
+
type: 'Basic',
|
|
162
|
+
user: 'admin',
|
|
163
|
+
pass: 'password123'
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
await dynamicRoutingProxy.start();
|
|
89
169
|
```
|
|
90
170
|
|
|
91
|
-
|
|
171
|
+
For those dealing with high volume or regulatory needs, the integration of tools like `iptables` allows broad control over network traffic:
|
|
92
172
|
|
|
93
|
-
|
|
173
|
+
```typescript
|
|
174
|
+
import { IPTablesProxy } from '@push.rocks/smartproxy';
|
|
94
175
|
|
|
95
|
-
|
|
176
|
+
// Setting up iptables for advanced network management
|
|
177
|
+
const ipTablesProxy = new IPTablesProxy({
|
|
178
|
+
fromPort: 8081,
|
|
179
|
+
toPort: 8080,
|
|
180
|
+
deleteOnExit: true // clean rules upon server shutdown
|
|
181
|
+
});
|
|
96
182
|
|
|
97
|
-
|
|
183
|
+
// Begin routing with IPTables
|
|
184
|
+
await ipTablesProxy.start();
|
|
185
|
+
```
|
|
98
186
|
|
|
99
|
-
|
|
187
|
+
### Combining with HTTP and HTTPS Credentials
|
|
188
|
+
|
|
189
|
+
When undertaking proxy configurations, handling sensitive data like SSL certificates and keys securely is imperative:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { loadDefaultCertificates } from '@push.rocks/smartproxy';
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const { privateKey, publicKey } = loadDefaultCertificates(); // adjust path as needed
|
|
196
|
+
console.log('Certificates loaded.');
|
|
197
|
+
// Use these certificates in your SSL-based configurations
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error('Cannot load certificates:', error);
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Testing and Validation
|
|
204
|
+
|
|
205
|
+
Given these powerful capabilities, rigorous testing of configurations and functionality using frameworks like `tap` can ensure high-quality and reliable proxy configurations. Smartproxy integrates with Typescript test setups:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { expect, tap } from '@push.rocks/tapbundle';
|
|
209
|
+
import { NetworkProxy } from '@push.rocks/smartproxy';
|
|
210
|
+
|
|
211
|
+
tap.test('proxied request should return status 200', async () => {
|
|
212
|
+
// Your test logic here
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
tap.start();
|
|
216
|
+
```
|
|
100
217
|
|
|
101
|
-
|
|
218
|
+
In summary, `@push.rocks/smartproxy` offers a plethora of solutions tailored to both common and sophisticated proxying needs. Whether you're seeking straightforward port forwarding, secure SSL redirection, WebSocket management, or robust network routing controls, smartproxy provides the right tools for efficient and effective proxy operations. Through its integration simplicity and versatile configurations, developers can ensure high performance and secure proxying across various environments and applications.
|
|
102
219
|
|
|
103
220
|
## License and Legal Information
|
|
104
221
|
|
|
105
|
-
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
222
|
+
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
106
223
|
|
|
107
224
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
108
225
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '3.
|
|
7
|
-
description: '
|
|
6
|
+
version: '3.14.0',
|
|
7
|
+
description: 'A robust and versatile proxy package designed to handle high workloads, offering features like SSL redirection, port proxying, WebSocket support, and customizable routing and authentication.'
|
|
8
8
|
}
|
|
@@ -1,35 +1,65 @@
|
|
|
1
|
-
import { exec } from 'child_process';
|
|
1
|
+
import { exec, execSync } from 'child_process';
|
|
2
2
|
import { promisify } from 'util';
|
|
3
3
|
|
|
4
4
|
const execAsync = promisify(exec);
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Settings for IPTablesProxy.
|
|
8
|
+
*/
|
|
6
9
|
export interface IIpTableProxySettings {
|
|
7
10
|
fromPort: number;
|
|
8
11
|
toPort: number;
|
|
9
12
|
toHost?: string; // Target host for proxying; defaults to 'localhost'
|
|
10
13
|
preserveSourceIP?: boolean; // If true, the original source IP is preserved.
|
|
14
|
+
deleteOnExit?: boolean; // If true, clean up marked iptables rules before process exit.
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
/**
|
|
14
18
|
* IPTablesProxy sets up iptables NAT rules to forward TCP traffic.
|
|
15
|
-
* It only supports basic port forwarding.
|
|
19
|
+
* It only supports basic port forwarding and uses iptables comments to tag rules.
|
|
16
20
|
*/
|
|
17
21
|
export class IPTablesProxy {
|
|
18
22
|
public settings: IIpTableProxySettings;
|
|
19
23
|
private rulesInstalled: boolean = false;
|
|
24
|
+
private ruleTag: string;
|
|
20
25
|
|
|
21
26
|
constructor(settings: IIpTableProxySettings) {
|
|
22
27
|
this.settings = {
|
|
23
28
|
...settings,
|
|
24
29
|
toHost: settings.toHost || 'localhost',
|
|
25
30
|
};
|
|
31
|
+
// Generate a unique identifier for the rules added by this instance.
|
|
32
|
+
this.ruleTag = `IPTablesProxy:${Date.now()}:${Math.random().toString(36).substr(2, 5)}`;
|
|
33
|
+
|
|
34
|
+
// If deleteOnExit is true, register cleanup handlers.
|
|
35
|
+
if (this.settings.deleteOnExit) {
|
|
36
|
+
const cleanup = () => {
|
|
37
|
+
try {
|
|
38
|
+
IPTablesProxy.cleanSlateSync();
|
|
39
|
+
} catch (err) {
|
|
40
|
+
console.error('Error cleaning iptables rules on exit:', err);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
process.on('exit', cleanup);
|
|
44
|
+
process.on('SIGINT', () => {
|
|
45
|
+
cleanup();
|
|
46
|
+
process.exit();
|
|
47
|
+
});
|
|
48
|
+
process.on('SIGTERM', () => {
|
|
49
|
+
cleanup();
|
|
50
|
+
process.exit();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
26
53
|
}
|
|
27
54
|
|
|
28
55
|
/**
|
|
29
56
|
* Sets up iptables rules for port forwarding.
|
|
57
|
+
* The rules are tagged with a unique comment so that they can be identified later.
|
|
30
58
|
*/
|
|
31
59
|
public async start(): Promise<void> {
|
|
32
|
-
const dnatCmd = `iptables -t nat -A PREROUTING -p tcp --dport ${this.settings.fromPort}
|
|
60
|
+
const dnatCmd = `iptables -t nat -A PREROUTING -p tcp --dport ${this.settings.fromPort} ` +
|
|
61
|
+
`-j DNAT --to-destination ${this.settings.toHost}:${this.settings.toPort} ` +
|
|
62
|
+
`-m comment --comment "${this.ruleTag}:DNAT"`;
|
|
33
63
|
try {
|
|
34
64
|
await execAsync(dnatCmd);
|
|
35
65
|
console.log(`Added iptables rule: ${dnatCmd}`);
|
|
@@ -39,8 +69,11 @@ export class IPTablesProxy {
|
|
|
39
69
|
throw err;
|
|
40
70
|
}
|
|
41
71
|
|
|
72
|
+
// If preserveSourceIP is false, add a MASQUERADE rule.
|
|
42
73
|
if (!this.settings.preserveSourceIP) {
|
|
43
|
-
const masqueradeCmd = `iptables -t nat -A POSTROUTING -p tcp -d ${this.settings.toHost}
|
|
74
|
+
const masqueradeCmd = `iptables -t nat -A POSTROUTING -p tcp -d ${this.settings.toHost} ` +
|
|
75
|
+
`--dport ${this.settings.toPort} -j MASQUERADE ` +
|
|
76
|
+
`-m comment --comment "${this.ruleTag}:MASQ"`;
|
|
44
77
|
try {
|
|
45
78
|
await execAsync(masqueradeCmd);
|
|
46
79
|
console.log(`Added iptables rule: ${masqueradeCmd}`);
|
|
@@ -48,7 +81,9 @@ export class IPTablesProxy {
|
|
|
48
81
|
console.error(`Failed to add iptables MASQUERADE rule: ${err}`);
|
|
49
82
|
// Roll back the DNAT rule if MASQUERADE fails.
|
|
50
83
|
try {
|
|
51
|
-
const rollbackCmd = `iptables -t nat -D PREROUTING -p tcp --dport ${this.settings.fromPort}
|
|
84
|
+
const rollbackCmd = `iptables -t nat -D PREROUTING -p tcp --dport ${this.settings.fromPort} ` +
|
|
85
|
+
`-j DNAT --to-destination ${this.settings.toHost}:${this.settings.toPort} ` +
|
|
86
|
+
`-m comment --comment "${this.ruleTag}:DNAT"`;
|
|
52
87
|
await execAsync(rollbackCmd);
|
|
53
88
|
this.rulesInstalled = false;
|
|
54
89
|
} catch (rollbackErr) {
|
|
@@ -60,12 +95,14 @@ export class IPTablesProxy {
|
|
|
60
95
|
}
|
|
61
96
|
|
|
62
97
|
/**
|
|
63
|
-
* Removes the iptables rules that were added in start().
|
|
98
|
+
* Removes the iptables rules that were added in start(), by matching the unique comment.
|
|
64
99
|
*/
|
|
65
100
|
public async stop(): Promise<void> {
|
|
66
101
|
if (!this.rulesInstalled) return;
|
|
67
102
|
|
|
68
|
-
const dnatDelCmd = `iptables -t nat -D PREROUTING -p tcp --dport ${this.settings.fromPort}
|
|
103
|
+
const dnatDelCmd = `iptables -t nat -D PREROUTING -p tcp --dport ${this.settings.fromPort} ` +
|
|
104
|
+
`-j DNAT --to-destination ${this.settings.toHost}:${this.settings.toPort} ` +
|
|
105
|
+
`-m comment --comment "${this.ruleTag}:DNAT"`;
|
|
69
106
|
try {
|
|
70
107
|
await execAsync(dnatDelCmd);
|
|
71
108
|
console.log(`Removed iptables rule: ${dnatDelCmd}`);
|
|
@@ -74,7 +111,9 @@ export class IPTablesProxy {
|
|
|
74
111
|
}
|
|
75
112
|
|
|
76
113
|
if (!this.settings.preserveSourceIP) {
|
|
77
|
-
const masqueradeDelCmd = `iptables -t nat -D POSTROUTING -p tcp -d ${this.settings.toHost}
|
|
114
|
+
const masqueradeDelCmd = `iptables -t nat -D POSTROUTING -p tcp -d ${this.settings.toHost} ` +
|
|
115
|
+
`--dport ${this.settings.toPort} -j MASQUERADE ` +
|
|
116
|
+
`-m comment --comment "${this.ruleTag}:MASQ"`;
|
|
78
117
|
try {
|
|
79
118
|
await execAsync(masqueradeDelCmd);
|
|
80
119
|
console.log(`Removed iptables rule: ${masqueradeDelCmd}`);
|
|
@@ -85,4 +124,60 @@ export class IPTablesProxy {
|
|
|
85
124
|
|
|
86
125
|
this.rulesInstalled = false;
|
|
87
126
|
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Asynchronously cleans up any iptables rules in the nat table that were added by this module.
|
|
130
|
+
* It looks for rules with comments containing "IPTablesProxy:".
|
|
131
|
+
*/
|
|
132
|
+
public static async cleanSlate(): Promise<void> {
|
|
133
|
+
try {
|
|
134
|
+
const { stdout } = await execAsync('iptables-save -t nat');
|
|
135
|
+
const lines = stdout.split('\n');
|
|
136
|
+
const proxyLines = lines.filter(line => line.includes('IPTablesProxy:'));
|
|
137
|
+
for (const line of proxyLines) {
|
|
138
|
+
const trimmedLine = line.trim();
|
|
139
|
+
if (trimmedLine.startsWith('-A')) {
|
|
140
|
+
// Replace the "-A" with "-D" to form a deletion command.
|
|
141
|
+
const deleteRule = trimmedLine.replace('-A', '-D');
|
|
142
|
+
const cmd = `iptables -t nat ${deleteRule}`;
|
|
143
|
+
try {
|
|
144
|
+
await execAsync(cmd);
|
|
145
|
+
console.log(`Cleaned up iptables rule: ${cmd}`);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.error(`Failed to remove iptables rule: ${cmd}`, err);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} catch (err) {
|
|
152
|
+
console.error(`Failed to run iptables-save: ${err}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Synchronously cleans up any iptables rules in the nat table that were added by this module.
|
|
158
|
+
* It looks for rules with comments containing "IPTablesProxy:".
|
|
159
|
+
* This method is intended for use in process exit handlers.
|
|
160
|
+
*/
|
|
161
|
+
public static cleanSlateSync(): void {
|
|
162
|
+
try {
|
|
163
|
+
const stdout = execSync('iptables-save -t nat').toString();
|
|
164
|
+
const lines = stdout.split('\n');
|
|
165
|
+
const proxyLines = lines.filter(line => line.includes('IPTablesProxy:'));
|
|
166
|
+
for (const line of proxyLines) {
|
|
167
|
+
const trimmedLine = line.trim();
|
|
168
|
+
if (trimmedLine.startsWith('-A')) {
|
|
169
|
+
const deleteRule = trimmedLine.replace('-A', '-D');
|
|
170
|
+
const cmd = `iptables -t nat ${deleteRule}`;
|
|
171
|
+
try {
|
|
172
|
+
execSync(cmd);
|
|
173
|
+
console.log(`Cleaned up iptables rule: ${cmd}`);
|
|
174
|
+
} catch (err) {
|
|
175
|
+
console.error(`Failed to remove iptables rule: ${cmd}`, err);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch (err) {
|
|
180
|
+
console.error(`Failed to run iptables-save: ${err}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
88
183
|
}
|
package/ts/classes.portproxy.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface IPortProxySettings extends plugins.tls.TlsOptions {
|
|
|
14
14
|
sniEnabled?: boolean;
|
|
15
15
|
defaultAllowedIPs?: string[];
|
|
16
16
|
preserveSourceIP?: boolean;
|
|
17
|
+
maxConnectionLifetime?: number; // New option (in milliseconds) to force cleanup of long-lived connections
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -85,6 +86,7 @@ interface IConnectionRecord {
|
|
|
85
86
|
incomingStartTime: number;
|
|
86
87
|
outgoingStartTime?: number;
|
|
87
88
|
connectionClosed: boolean;
|
|
89
|
+
cleanupTimer?: NodeJS.Timeout; // Timer to force cleanup after max lifetime/inactivity
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
export class PortProxy {
|
|
@@ -102,10 +104,11 @@ export class PortProxy {
|
|
|
102
104
|
outgoing: {},
|
|
103
105
|
};
|
|
104
106
|
|
|
105
|
-
constructor(
|
|
107
|
+
constructor(settingsArg: IPortProxySettings) {
|
|
106
108
|
this.settings = {
|
|
107
|
-
...
|
|
108
|
-
toHost:
|
|
109
|
+
...settingsArg,
|
|
110
|
+
toHost: settingsArg.toHost || 'localhost',
|
|
111
|
+
maxConnectionLifetime: settingsArg.maxConnectionLifetime || 10000,
|
|
109
112
|
};
|
|
110
113
|
}
|
|
111
114
|
|
|
@@ -164,6 +167,9 @@ export class PortProxy {
|
|
|
164
167
|
const cleanupOnce = () => {
|
|
165
168
|
if (!connectionRecord.connectionClosed) {
|
|
166
169
|
connectionRecord.connectionClosed = true;
|
|
170
|
+
if (connectionRecord.cleanupTimer) {
|
|
171
|
+
clearTimeout(connectionRecord.cleanupTimer);
|
|
172
|
+
}
|
|
167
173
|
cleanUpSockets(connectionRecord.incoming, connectionRecord.outgoing || undefined);
|
|
168
174
|
this.connectionRecords.delete(connectionRecord);
|
|
169
175
|
console.log(`Connection from ${remoteIP} terminated. Active connections: ${this.connectionRecords.size}`);
|
|
@@ -262,6 +268,7 @@ export class PortProxy {
|
|
|
262
268
|
socket.pipe(targetSocket);
|
|
263
269
|
targetSocket.pipe(socket);
|
|
264
270
|
|
|
271
|
+
// Attach error and close handlers.
|
|
265
272
|
socket.on('error', handleError('incoming'));
|
|
266
273
|
targetSocket.on('error', handleError('outgoing'));
|
|
267
274
|
socket.on('close', handleClose('incoming'));
|
|
@@ -284,6 +291,40 @@ export class PortProxy {
|
|
|
284
291
|
});
|
|
285
292
|
socket.on('end', handleClose('incoming'));
|
|
286
293
|
targetSocket.on('end', handleClose('outgoing'));
|
|
294
|
+
|
|
295
|
+
// If maxConnectionLifetime is set, initialize a cleanup timer that will be reset on data flow.
|
|
296
|
+
if (this.settings.maxConnectionLifetime) {
|
|
297
|
+
let incomingActive = false;
|
|
298
|
+
let outgoingActive = false;
|
|
299
|
+
const resetCleanupTimer = () => {
|
|
300
|
+
if (this.settings.maxConnectionLifetime) {
|
|
301
|
+
if (connectionRecord.cleanupTimer) {
|
|
302
|
+
clearTimeout(connectionRecord.cleanupTimer);
|
|
303
|
+
}
|
|
304
|
+
connectionRecord.cleanupTimer = setTimeout(() => {
|
|
305
|
+
console.log(`Connection from ${remoteIP} exceeded max lifetime with inactivity (${this.settings.maxConnectionLifetime}ms), forcing cleanup.`);
|
|
306
|
+
cleanupOnce();
|
|
307
|
+
}, this.settings.maxConnectionLifetime);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
// Start the cleanup timer.
|
|
312
|
+
resetCleanupTimer();
|
|
313
|
+
|
|
314
|
+
// Listen for data events on both sides and reset the timer when both are active.
|
|
315
|
+
socket.on('data', () => {
|
|
316
|
+
incomingActive = true;
|
|
317
|
+
if (incomingActive && outgoingActive) {
|
|
318
|
+
resetCleanupTimer();
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
targetSocket.on('data', () => {
|
|
322
|
+
outgoingActive = true;
|
|
323
|
+
if (incomingActive && outgoingActive) {
|
|
324
|
+
resetCleanupTimer();
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
287
328
|
};
|
|
288
329
|
|
|
289
330
|
if (this.settings.sniEnabled) {
|