@push.rocks/smartproxy 22.6.0 → 23.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.
Files changed (40) hide show
  1. package/changelog.md +8 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/index.d.ts +0 -1
  4. package/dist_ts/index.js +1 -3
  5. package/dist_ts/proxies/index.d.ts +0 -1
  6. package/dist_ts/proxies/index.js +1 -3
  7. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  8. package/package.json +1 -1
  9. package/readme.md +0 -26
  10. package/ts/00_commitinfo_data.ts +1 -1
  11. package/ts/index.ts +0 -3
  12. package/ts/proxies/index.ts +0 -3
  13. package/ts/proxies/smart-proxy/models/route-types.ts +0 -2
  14. package/dist_ts/proxies/nftables-proxy/index.d.ts +0 -6
  15. package/dist_ts/proxies/nftables-proxy/index.js +0 -7
  16. package/dist_ts/proxies/nftables-proxy/models/errors.d.ts +0 -15
  17. package/dist_ts/proxies/nftables-proxy/models/errors.js +0 -28
  18. package/dist_ts/proxies/nftables-proxy/models/index.d.ts +0 -5
  19. package/dist_ts/proxies/nftables-proxy/models/index.js +0 -6
  20. package/dist_ts/proxies/nftables-proxy/models/interfaces.d.ts +0 -75
  21. package/dist_ts/proxies/nftables-proxy/models/interfaces.js +0 -5
  22. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +0 -124
  23. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +0 -1374
  24. package/dist_ts/proxies/nftables-proxy/utils/index.d.ts +0 -9
  25. package/dist_ts/proxies/nftables-proxy/utils/index.js +0 -12
  26. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.d.ts +0 -66
  27. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.js +0 -131
  28. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.d.ts +0 -39
  29. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.js +0 -112
  30. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.d.ts +0 -59
  31. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.js +0 -130
  32. package/ts/proxies/nftables-proxy/index.ts +0 -6
  33. package/ts/proxies/nftables-proxy/models/errors.ts +0 -30
  34. package/ts/proxies/nftables-proxy/models/index.ts +0 -5
  35. package/ts/proxies/nftables-proxy/models/interfaces.ts +0 -94
  36. package/ts/proxies/nftables-proxy/nftables-proxy.ts +0 -1754
  37. package/ts/proxies/nftables-proxy/utils/index.ts +0 -38
  38. package/ts/proxies/nftables-proxy/utils/nft-command-executor.ts +0 -162
  39. package/ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts +0 -125
  40. package/ts/proxies/nftables-proxy/utils/nft-rule-validator.ts +0 -156
@@ -1,1374 +0,0 @@
1
- import { exec, execSync } from 'child_process';
2
- import { promisify } from 'util';
3
- import * as fs from 'fs';
4
- import * as path from 'path';
5
- import * as os from 'os';
6
- import { AsyncFileSystem } from '../../core/utils/fs-utils.js';
7
- import { NftValidationError, NftExecutionError, NftResourceError } from './models/index.js';
8
- import { NftCommandExecutor, normalizePortSpec, validateSettings, filterIPsByFamily } from './utils/index.js';
9
- const execAsync = promisify(exec);
10
- /**
11
- * NfTablesProxy sets up nftables NAT rules to forward TCP traffic.
12
- * Enhanced with multi-port support, IPv6, connection tracking, metrics,
13
- * and more advanced features.
14
- */
15
- export class NfTablesProxy {
16
- static { this.NFT_CMD = 'nft'; }
17
- constructor(settings) {
18
- this.rules = [];
19
- this.ipSets = new Map(); // Store IP sets for tracking
20
- // Validate inputs to prevent command injection
21
- validateSettings(settings);
22
- // Set default settings
23
- this.settings = {
24
- ...settings,
25
- toHost: settings.toHost || 'localhost',
26
- protocol: settings.protocol || 'tcp',
27
- enableLogging: settings.enableLogging !== undefined ? settings.enableLogging : false,
28
- ipv6Support: settings.ipv6Support !== undefined ? settings.ipv6Support : false,
29
- tableName: settings.tableName || 'portproxy',
30
- logFormat: settings.logFormat || 'plain',
31
- useIPSets: settings.useIPSets !== undefined ? settings.useIPSets : true,
32
- maxRetries: settings.maxRetries || 3,
33
- retryDelayMs: settings.retryDelayMs || 1000,
34
- useAdvancedNAT: settings.useAdvancedNAT !== undefined ? settings.useAdvancedNAT : false,
35
- };
36
- // Generate a unique identifier for the rules added by this instance
37
- this.ruleTag = `NfTablesProxy:${Date.now()}:${Math.random().toString(36).substr(2, 5)}`;
38
- // Set table name
39
- this.tableName = this.settings.tableName || 'portproxy';
40
- // Create a temp file path for batch operations
41
- this.tempFilePath = path.join(os.tmpdir(), `nft-rules-${Date.now()}.nft`);
42
- // Create the command executor
43
- this.executor = new NftCommandExecutor((level, message, data) => this.log(level, message, data), {
44
- maxRetries: this.settings.maxRetries,
45
- retryDelayMs: this.settings.retryDelayMs,
46
- tempFilePath: this.tempFilePath
47
- });
48
- // Register cleanup handlers if deleteOnExit is true
49
- if (this.settings.deleteOnExit) {
50
- // Synchronous cleanup for 'exit' event (only sync code runs here)
51
- const syncCleanup = () => {
52
- try {
53
- this.stopSync();
54
- }
55
- catch (err) {
56
- this.log('error', 'Error cleaning nftables rules on exit:', { error: err.message });
57
- }
58
- };
59
- // Async cleanup for signal handlers (preferred, non-blocking)
60
- const asyncCleanup = async () => {
61
- try {
62
- await this.stop();
63
- }
64
- catch (err) {
65
- this.log('error', 'Error cleaning nftables rules on signal:', { error: err.message });
66
- }
67
- };
68
- process.on('exit', syncCleanup);
69
- process.on('SIGINT', () => {
70
- asyncCleanup().finally(() => process.exit());
71
- });
72
- process.on('SIGTERM', () => {
73
- asyncCleanup().finally(() => process.exit());
74
- });
75
- }
76
- }
77
- /**
78
- * Checks if nftables is available and the required modules are loaded
79
- */
80
- async checkNftablesAvailability() {
81
- const available = await this.executor.checkAvailability();
82
- if (available && this.settings.useAdvancedNAT) {
83
- await this.executor.checkConntrackModules();
84
- }
85
- return available;
86
- }
87
- /**
88
- * Creates the necessary tables and chains
89
- */
90
- async setupTablesAndChains(isIpv6 = false) {
91
- const family = isIpv6 ? 'ip6' : 'ip';
92
- try {
93
- // Check if the table already exists
94
- const stdout = await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} list tables ${family}`, this.settings.maxRetries, this.settings.retryDelayMs);
95
- const tableExists = stdout.includes(`table ${family} ${this.tableName}`);
96
- if (!tableExists) {
97
- // Create the table
98
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} add table ${family} ${this.tableName}`, this.settings.maxRetries, this.settings.retryDelayMs);
99
- this.log('info', `Created table ${family} ${this.tableName}`);
100
- // Create the nat chain for the prerouting hook
101
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} add chain ${family} ${this.tableName} nat_prerouting { type nat hook prerouting priority -100 ; }`, this.settings.maxRetries, this.settings.retryDelayMs);
102
- this.log('info', `Created nat_prerouting chain in ${family} ${this.tableName}`);
103
- // Create the nat chain for the postrouting hook if not preserving source IP
104
- if (!this.settings.preserveSourceIP) {
105
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} add chain ${family} ${this.tableName} nat_postrouting { type nat hook postrouting priority 100 ; }`, this.settings.maxRetries, this.settings.retryDelayMs);
106
- this.log('info', `Created nat_postrouting chain in ${family} ${this.tableName}`);
107
- }
108
- // Create the chain for NetworkProxy integration if needed
109
- if (this.settings.netProxyIntegration?.enabled && this.settings.netProxyIntegration.redirectLocalhost) {
110
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} add chain ${family} ${this.tableName} nat_output { type nat hook output priority 0 ; }`, this.settings.maxRetries, this.settings.retryDelayMs);
111
- this.log('info', `Created nat_output chain in ${family} ${this.tableName}`);
112
- }
113
- // Create the QoS chain if needed
114
- if (this.settings.qos?.enabled) {
115
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} add chain ${family} ${this.tableName} qos_forward { type filter hook forward priority 0 ; }`, this.settings.maxRetries, this.settings.retryDelayMs);
116
- this.log('info', `Created QoS forward chain in ${family} ${this.tableName}`);
117
- }
118
- }
119
- else {
120
- this.log('info', `Table ${family} ${this.tableName} already exists, using existing table`);
121
- }
122
- return true;
123
- }
124
- catch (err) {
125
- this.log('error', `Failed to set up tables and chains: ${err.message}`);
126
- return false;
127
- }
128
- }
129
- /**
130
- * Creates IP sets for efficient filtering of large IP lists
131
- */
132
- async createIPSet(family, setName, ips, setType = 'ipv4_addr') {
133
- try {
134
- // Filter IPs based on family
135
- const filteredIPs = filterIPsByFamily(ips, family);
136
- if (filteredIPs.length === 0) {
137
- this.log('info', `No IP addresses of type ${setType} to add to set ${setName}`);
138
- return true;
139
- }
140
- // Check if set already exists
141
- try {
142
- const sets = await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} list sets ${family} ${this.tableName}`, this.settings.maxRetries, this.settings.retryDelayMs);
143
- if (sets.includes(`set ${setName} {`)) {
144
- this.log('info', `IP set ${setName} already exists, will add elements`);
145
- }
146
- else {
147
- // Create the set
148
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} add set ${family} ${this.tableName} ${setName} { type ${setType}; }`, this.settings.maxRetries, this.settings.retryDelayMs);
149
- this.log('info', `Created IP set ${setName} for ${family} with type ${setType}`);
150
- }
151
- }
152
- catch (err) {
153
- // Set might not exist yet, create it
154
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} add set ${family} ${this.tableName} ${setName} { type ${setType}; }`, this.settings.maxRetries, this.settings.retryDelayMs);
155
- this.log('info', `Created IP set ${setName} for ${family} with type ${setType}`);
156
- }
157
- // Add IPs to the set in batches to avoid command line length limitations
158
- const batchSize = 100;
159
- for (let i = 0; i < filteredIPs.length; i += batchSize) {
160
- const batch = filteredIPs.slice(i, i + batchSize);
161
- const elements = batch.join(', ');
162
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} add element ${family} ${this.tableName} ${setName} { ${elements} }`, this.settings.maxRetries, this.settings.retryDelayMs);
163
- this.log('info', `Added batch of ${batch.length} IPs to set ${setName}`);
164
- }
165
- // Track the IP set
166
- this.ipSets.set(`${family}:${setName}`, filteredIPs);
167
- return true;
168
- }
169
- catch (err) {
170
- this.log('error', `Failed to create IP set ${setName}: ${err.message}`);
171
- return false;
172
- }
173
- }
174
- /**
175
- * Adds source IP filtering rules, potentially using IP sets for efficiency
176
- */
177
- async addSourceIPFilters(isIpv6 = false) {
178
- if (!this.settings.ipAllowList && !this.settings.ipBlockList) {
179
- return true; // Nothing to do
180
- }
181
- const family = isIpv6 ? 'ip6' : 'ip';
182
- const chain = 'nat_prerouting';
183
- const setType = isIpv6 ? 'ipv6_addr' : 'ipv4_addr';
184
- try {
185
- // Start building the ruleset file content
186
- let rulesetContent = '';
187
- // Using IP sets for more efficient rule processing with large IP lists
188
- if (this.settings.useIPSets) {
189
- // Create sets for banned and allowed IPs if needed
190
- if (this.settings.ipBlockList && this.settings.ipBlockList.length > 0) {
191
- const setName = 'banned_ips';
192
- await this.createIPSet(family, setName, this.settings.ipBlockList, setType);
193
- // Add rule to drop traffic from banned IPs
194
- const rule = `add rule ${family} ${this.tableName} ${chain} ip${isIpv6 ? '6' : ''} saddr @${setName} drop comment "${this.ruleTag}:BANNED_SET"`;
195
- rulesetContent += `${rule}\n`;
196
- this.rules.push({
197
- tableFamily: family,
198
- tableName: this.tableName,
199
- chainName: chain,
200
- ruleContents: rule,
201
- added: false
202
- });
203
- }
204
- if (this.settings.ipAllowList && this.settings.ipAllowList.length > 0) {
205
- const setName = 'allowed_ips';
206
- await this.createIPSet(family, setName, this.settings.ipAllowList, setType);
207
- // Add rule to allow traffic from allowed IPs
208
- const rule = `add rule ${family} ${this.tableName} ${chain} ip${isIpv6 ? '6' : ''} saddr @${setName} ${this.settings.protocol} dport {${this.getAllPorts(this.settings.fromPort)}} accept comment "${this.ruleTag}:ALLOWED_SET"`;
209
- rulesetContent += `${rule}\n`;
210
- this.rules.push({
211
- tableFamily: family,
212
- tableName: this.tableName,
213
- chainName: chain,
214
- ruleContents: rule,
215
- added: false
216
- });
217
- // Add default deny rule for unlisted IPs
218
- const denyRule = `add rule ${family} ${this.tableName} ${chain} ${this.settings.protocol} dport {${this.getAllPorts(this.settings.fromPort)}} drop comment "${this.ruleTag}:DENY_ALL"`;
219
- rulesetContent += `${denyRule}\n`;
220
- this.rules.push({
221
- tableFamily: family,
222
- tableName: this.tableName,
223
- chainName: chain,
224
- ruleContents: denyRule,
225
- added: false
226
- });
227
- }
228
- }
229
- else {
230
- // Traditional approach without IP sets - less efficient for large IP lists
231
- // Ban specific IPs first
232
- if (this.settings.ipBlockList && this.settings.ipBlockList.length > 0) {
233
- for (const ip of this.settings.ipBlockList) {
234
- // Skip IPv4 addresses for IPv6 rules and vice versa
235
- if (isIpv6 && ip.includes('.'))
236
- continue;
237
- if (!isIpv6 && ip.includes(':'))
238
- continue;
239
- const rule = `add rule ${family} ${this.tableName} ${chain} ip${isIpv6 ? '6' : ''} saddr ${ip} drop comment "${this.ruleTag}:BANNED"`;
240
- rulesetContent += `${rule}\n`;
241
- this.rules.push({
242
- tableFamily: family,
243
- tableName: this.tableName,
244
- chainName: chain,
245
- ruleContents: rule,
246
- added: false
247
- });
248
- }
249
- }
250
- // Allow specific IPs
251
- if (this.settings.ipAllowList && this.settings.ipAllowList.length > 0) {
252
- // Add rules to allow specific IPs
253
- for (const ip of this.settings.ipAllowList) {
254
- // Skip IPv4 addresses for IPv6 rules and vice versa
255
- if (isIpv6 && ip.includes('.'))
256
- continue;
257
- if (!isIpv6 && ip.includes(':'))
258
- continue;
259
- const rule = `add rule ${family} ${this.tableName} ${chain} ip${isIpv6 ? '6' : ''} saddr ${ip} ${this.settings.protocol} dport {${this.getAllPorts(this.settings.fromPort)}} accept comment "${this.ruleTag}:ALLOWED"`;
260
- rulesetContent += `${rule}\n`;
261
- this.rules.push({
262
- tableFamily: family,
263
- tableName: this.tableName,
264
- chainName: chain,
265
- ruleContents: rule,
266
- added: false
267
- });
268
- }
269
- // Add default deny rule for unlisted IPs
270
- const denyRule = `add rule ${family} ${this.tableName} ${chain} ${this.settings.protocol} dport {${this.getAllPorts(this.settings.fromPort)}} drop comment "${this.ruleTag}:DENY_ALL"`;
271
- rulesetContent += `${denyRule}\n`;
272
- this.rules.push({
273
- tableFamily: family,
274
- tableName: this.tableName,
275
- chainName: chain,
276
- ruleContents: denyRule,
277
- added: false
278
- });
279
- }
280
- }
281
- // Only write and apply if we have rules to add
282
- if (rulesetContent) {
283
- // Apply the ruleset using the helper
284
- await this.executor.executeWithTempFile(rulesetContent);
285
- this.log('info', `Added source IP filter rules for ${family}`);
286
- // Mark rules as added
287
- for (const rule of this.rules) {
288
- if (rule.tableFamily === family && !rule.added) {
289
- rule.added = true;
290
- // Verify the rule was applied
291
- await this.verifyRuleApplication(rule);
292
- }
293
- }
294
- }
295
- return true;
296
- }
297
- catch (err) {
298
- this.log('error', `Failed to add source IP filter rules: ${err.message}`);
299
- // Try to clean up any rules that might have been added
300
- this.rollbackRules();
301
- return false;
302
- }
303
- }
304
- /**
305
- * Gets a comma-separated list of all ports from a port specification
306
- */
307
- getAllPorts(portSpec) {
308
- const portRanges = normalizePortSpec(portSpec);
309
- const ports = [];
310
- for (const range of portRanges) {
311
- if (range.from === range.to) {
312
- ports.push(range.from.toString());
313
- }
314
- else {
315
- ports.push(`${range.from}-${range.to}`);
316
- }
317
- }
318
- return ports.join(', ');
319
- }
320
- /**
321
- * Configures advanced NAT with connection tracking
322
- */
323
- async setupAdvancedNAT(isIpv6 = false) {
324
- if (!this.settings.useAdvancedNAT) {
325
- return true; // Skip if not using advanced NAT
326
- }
327
- const family = isIpv6 ? 'ip6' : 'ip';
328
- const preroutingChain = 'nat_prerouting';
329
- try {
330
- // Get the port ranges
331
- const fromPortRanges = normalizePortSpec(this.settings.fromPort);
332
- const toPortRanges = normalizePortSpec(this.settings.toPort);
333
- let rulesetContent = '';
334
- // Simple case - one-to-one mapping with connection tracking
335
- if (fromPortRanges.length === 1 && toPortRanges.length === 1) {
336
- const fromRange = fromPortRanges[0];
337
- const toRange = toPortRanges[0];
338
- // Single port to single port with connection tracking
339
- if (fromRange.from === fromRange.to && toRange.from === toRange.to) {
340
- const rule = `add rule ${family} ${this.tableName} ${preroutingChain} ${this.settings.protocol} dport ${fromRange.from} ct state new dnat to ${this.settings.toHost}:${toRange.from} comment "${this.ruleTag}:DNAT_CT"`;
341
- rulesetContent += `${rule}\n`;
342
- this.rules.push({
343
- tableFamily: family,
344
- tableName: this.tableName,
345
- chainName: preroutingChain,
346
- ruleContents: rule,
347
- added: false
348
- });
349
- }
350
- // Port range with same size
351
- else if ((fromRange.to - fromRange.from) === (toRange.to - toRange.from)) {
352
- const rule = `add rule ${family} ${this.tableName} ${preroutingChain} ${this.settings.protocol} dport ${fromRange.from}-${fromRange.to} ct state new dnat to ${this.settings.toHost}:${toRange.from}-${toRange.to} comment "${this.ruleTag}:DNAT_RANGE_CT"`;
353
- rulesetContent += `${rule}\n`;
354
- this.rules.push({
355
- tableFamily: family,
356
- tableName: this.tableName,
357
- chainName: preroutingChain,
358
- ruleContents: rule,
359
- added: false
360
- });
361
- }
362
- // Add related and established connection rule for efficient connection handling
363
- const ctRule = `add rule ${family} ${this.tableName} ${preroutingChain} ct state established,related accept comment "${this.ruleTag}:CT_ESTABLISHED"`;
364
- rulesetContent += `${ctRule}\n`;
365
- this.rules.push({
366
- tableFamily: family,
367
- tableName: this.tableName,
368
- chainName: preroutingChain,
369
- ruleContents: ctRule,
370
- added: false
371
- });
372
- // Apply the rules if we have any
373
- if (rulesetContent) {
374
- await this.executor.executeWithTempFile(rulesetContent);
375
- this.log('info', `Added advanced NAT rules for ${family}`);
376
- // Mark rules as added
377
- for (const rule of this.rules) {
378
- if (rule.tableFamily === family && !rule.added) {
379
- rule.added = true;
380
- // Verify the rule was applied
381
- await this.verifyRuleApplication(rule);
382
- }
383
- }
384
- }
385
- }
386
- return true;
387
- }
388
- catch (err) {
389
- this.log('error', `Failed to set up advanced NAT: ${err.message}`);
390
- return false;
391
- }
392
- }
393
- /**
394
- * Adds port forwarding rules
395
- */
396
- async addPortForwardingRules(isIpv6 = false) {
397
- // Skip if using advanced NAT as that already handles the port forwarding
398
- if (this.settings.useAdvancedNAT) {
399
- return true;
400
- }
401
- const family = isIpv6 ? 'ip6' : 'ip';
402
- const preroutingChain = 'nat_prerouting';
403
- const postroutingChain = 'nat_postrouting';
404
- try {
405
- // Normalize port specifications
406
- const fromPortRanges = normalizePortSpec(this.settings.fromPort);
407
- const toPortRanges = normalizePortSpec(this.settings.toPort);
408
- // Handle the case where fromPort and toPort counts don't match
409
- if (fromPortRanges.length !== toPortRanges.length) {
410
- if (toPortRanges.length === 1) {
411
- // If there's only one toPort, use it for all fromPorts
412
- const singleToRange = toPortRanges[0];
413
- return await this.addPortMappings(family, preroutingChain, postroutingChain, fromPortRanges, singleToRange);
414
- }
415
- else {
416
- throw new NftValidationError('Mismatched port counts: fromPort and toPort arrays must have equal length or toPort must be a single value');
417
- }
418
- }
419
- else {
420
- // Add port mapping rules for each port pair
421
- return await this.addPortPairMappings(family, preroutingChain, postroutingChain, fromPortRanges, toPortRanges);
422
- }
423
- }
424
- catch (err) {
425
- this.log('error', `Failed to add port forwarding rules: ${err.message}`);
426
- return false;
427
- }
428
- }
429
- /**
430
- * Adds port forwarding rules for the case where one toPortRange maps to multiple fromPortRanges
431
- */
432
- async addPortMappings(family, preroutingChain, postroutingChain, fromPortRanges, toPortRange) {
433
- try {
434
- let rulesetContent = '';
435
- // For each from port range, create a mapping to the single to port range
436
- for (const fromRange of fromPortRanges) {
437
- // Simple case: single port to single port
438
- if (fromRange.from === fromRange.to && toPortRange.from === toPortRange.to) {
439
- const rule = `add rule ${family} ${this.tableName} ${preroutingChain} ${this.settings.protocol} dport ${fromRange.from} dnat to ${this.settings.toHost}:${toPortRange.from} comment "${this.ruleTag}:DNAT"`;
440
- rulesetContent += `${rule}\n`;
441
- this.rules.push({
442
- tableFamily: family,
443
- tableName: this.tableName,
444
- chainName: preroutingChain,
445
- ruleContents: rule,
446
- added: false
447
- });
448
- }
449
- // Multiple ports in from range, but only one port in to range
450
- else if (toPortRange.from === toPortRange.to) {
451
- // Map each port in from range to the single to port
452
- for (let p = fromRange.from; p <= fromRange.to; p++) {
453
- const rule = `add rule ${family} ${this.tableName} ${preroutingChain} ${this.settings.protocol} dport ${p} dnat to ${this.settings.toHost}:${toPortRange.from} comment "${this.ruleTag}:DNAT"`;
454
- rulesetContent += `${rule}\n`;
455
- this.rules.push({
456
- tableFamily: family,
457
- tableName: this.tableName,
458
- chainName: preroutingChain,
459
- ruleContents: rule,
460
- added: false
461
- });
462
- }
463
- }
464
- // Port range to port range mapping with modulo distribution
465
- else {
466
- const toRangeSize = toPortRange.to - toPortRange.from + 1;
467
- for (let p = fromRange.from; p <= fromRange.to; p++) {
468
- const offset = (p - fromRange.from) % toRangeSize;
469
- const targetPort = toPortRange.from + offset;
470
- const rule = `add rule ${family} ${this.tableName} ${preroutingChain} ${this.settings.protocol} dport ${p} dnat to ${this.settings.toHost}:${targetPort} comment "${this.ruleTag}:DNAT"`;
471
- rulesetContent += `${rule}\n`;
472
- this.rules.push({
473
- tableFamily: family,
474
- tableName: this.tableName,
475
- chainName: preroutingChain,
476
- ruleContents: rule,
477
- added: false
478
- });
479
- }
480
- }
481
- }
482
- // Add masquerade rule for source NAT if not preserving source IP
483
- if (!this.settings.preserveSourceIP) {
484
- const ports = this.getAllPorts(this.settings.toPort);
485
- const masqRule = `add rule ${family} ${this.tableName} ${postroutingChain} ${this.settings.protocol} daddr ${this.settings.toHost} dport {${ports}} masquerade comment "${this.ruleTag}:MASQ"`;
486
- rulesetContent += `${masqRule}\n`;
487
- this.rules.push({
488
- tableFamily: family,
489
- tableName: this.tableName,
490
- chainName: postroutingChain,
491
- ruleContents: masqRule,
492
- added: false
493
- });
494
- }
495
- // Apply the ruleset if we have any rules
496
- if (rulesetContent) {
497
- // Apply the ruleset using the helper
498
- await this.executor.executeWithTempFile(rulesetContent);
499
- this.log('info', `Added port forwarding rules for ${family}`);
500
- // Mark rules as added
501
- for (const rule of this.rules) {
502
- if (rule.tableFamily === family && !rule.added) {
503
- rule.added = true;
504
- // Verify the rule was applied
505
- await this.verifyRuleApplication(rule);
506
- }
507
- }
508
- }
509
- return true;
510
- }
511
- catch (err) {
512
- this.log('error', `Failed to add port mappings: ${err.message}`);
513
- return false;
514
- }
515
- }
516
- /**
517
- * Adds port forwarding rules for pairs of fromPortRanges and toPortRanges
518
- */
519
- async addPortPairMappings(family, preroutingChain, postroutingChain, fromPortRanges, toPortRanges) {
520
- try {
521
- let rulesetContent = '';
522
- // Process each fromPort and toPort pair
523
- for (let i = 0; i < fromPortRanges.length; i++) {
524
- const fromRange = fromPortRanges[i];
525
- const toRange = toPortRanges[i];
526
- // Simple case: single port to single port
527
- if (fromRange.from === fromRange.to && toRange.from === toRange.to) {
528
- const rule = `add rule ${family} ${this.tableName} ${preroutingChain} ${this.settings.protocol} dport ${fromRange.from} dnat to ${this.settings.toHost}:${toRange.from} comment "${this.ruleTag}:DNAT"`;
529
- rulesetContent += `${rule}\n`;
530
- this.rules.push({
531
- tableFamily: family,
532
- tableName: this.tableName,
533
- chainName: preroutingChain,
534
- ruleContents: rule,
535
- added: false
536
- });
537
- }
538
- // Port range with equal size - can use direct mapping
539
- else if ((fromRange.to - fromRange.from) === (toRange.to - toRange.from)) {
540
- const rule = `add rule ${family} ${this.tableName} ${preroutingChain} ${this.settings.protocol} dport ${fromRange.from}-${fromRange.to} dnat to ${this.settings.toHost}:${toRange.from}-${toRange.to} comment "${this.ruleTag}:DNAT_RANGE"`;
541
- rulesetContent += `${rule}\n`;
542
- this.rules.push({
543
- tableFamily: family,
544
- tableName: this.tableName,
545
- chainName: preroutingChain,
546
- ruleContents: rule,
547
- added: false
548
- });
549
- }
550
- // Unequal port ranges - need to map individually
551
- else {
552
- const toRangeSize = toRange.to - toRange.from + 1;
553
- for (let p = fromRange.from; p <= fromRange.to; p++) {
554
- const offset = (p - fromRange.from) % toRangeSize;
555
- const targetPort = toRange.from + offset;
556
- const rule = `add rule ${family} ${this.tableName} ${preroutingChain} ${this.settings.protocol} dport ${p} dnat to ${this.settings.toHost}:${targetPort} comment "${this.ruleTag}:DNAT_INDIVIDUAL"`;
557
- rulesetContent += `${rule}\n`;
558
- this.rules.push({
559
- tableFamily: family,
560
- tableName: this.tableName,
561
- chainName: preroutingChain,
562
- ruleContents: rule,
563
- added: false
564
- });
565
- }
566
- }
567
- // Add masquerade rule for this port range if not preserving source IP
568
- if (!this.settings.preserveSourceIP) {
569
- const masqRule = `add rule ${family} ${this.tableName} ${postroutingChain} ${this.settings.protocol} daddr ${this.settings.toHost} dport ${toRange.from}-${toRange.to} masquerade comment "${this.ruleTag}:MASQ"`;
570
- rulesetContent += `${masqRule}\n`;
571
- this.rules.push({
572
- tableFamily: family,
573
- tableName: this.tableName,
574
- chainName: postroutingChain,
575
- ruleContents: masqRule,
576
- added: false
577
- });
578
- }
579
- }
580
- // Apply the ruleset if we have any rules
581
- if (rulesetContent) {
582
- await this.executor.executeWithTempFile(rulesetContent);
583
- this.log('info', `Added port forwarding rules for ${family}`);
584
- // Mark rules as added
585
- for (const rule of this.rules) {
586
- if (rule.tableFamily === family && !rule.added) {
587
- rule.added = true;
588
- // Verify the rule was applied
589
- await this.verifyRuleApplication(rule);
590
- }
591
- }
592
- }
593
- return true;
594
- }
595
- catch (err) {
596
- this.log('error', `Failed to add port pair mappings: ${err.message}`);
597
- return false;
598
- }
599
- }
600
- /**
601
- * Setup quality of service rules
602
- */
603
- async addTrafficShaping(isIpv6 = false) {
604
- if (!this.settings.qos?.enabled) {
605
- return true;
606
- }
607
- const family = isIpv6 ? 'ip6' : 'ip';
608
- const qosChain = 'qos_forward';
609
- try {
610
- let rulesetContent = '';
611
- // Add rate limiting rule if specified
612
- if (this.settings.qos.maxRate) {
613
- const ruleContent = `add rule ${family} ${this.tableName} ${qosChain} ip daddr ${this.settings.toHost} ${this.settings.protocol} dport {${this.getAllPorts(this.settings.toPort)}} limit rate over ${this.settings.qos.maxRate} drop comment "${this.ruleTag}:QOS_RATE"`;
614
- rulesetContent += `${ruleContent}\n`;
615
- this.rules.push({
616
- tableFamily: family,
617
- tableName: this.tableName,
618
- chainName: qosChain,
619
- ruleContents: ruleContent,
620
- added: false
621
- });
622
- }
623
- // Add priority marking if specified
624
- if (this.settings.qos.priority !== undefined) {
625
- // Check if the chain exists
626
- const chainsOutput = await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} list chains ${family} ${this.tableName}`, this.settings.maxRetries, this.settings.retryDelayMs);
627
- // Check if we need to create priority queues
628
- const hasPrioChain = chainsOutput.includes(`chain prio${this.settings.qos.priority}`);
629
- if (!hasPrioChain) {
630
- // Create priority chain
631
- const prioChainRule = `add chain ${family} ${this.tableName} prio${this.settings.qos.priority} { type filter hook forward priority ${this.settings.qos.priority * 10}; }`;
632
- rulesetContent += `${prioChainRule}\n`;
633
- }
634
- // Add the rules to mark packets with this priority
635
- for (const range of normalizePortSpec(this.settings.toPort)) {
636
- const markRule = `add rule ${family} ${this.tableName} ${qosChain} ${this.settings.protocol} dport ${range.from}-${range.to} counter goto prio${this.settings.qos.priority} comment "${this.ruleTag}:QOS_PRIORITY"`;
637
- rulesetContent += `${markRule}\n`;
638
- this.rules.push({
639
- tableFamily: family,
640
- tableName: this.tableName,
641
- chainName: qosChain,
642
- ruleContents: markRule,
643
- added: false
644
- });
645
- }
646
- }
647
- // Apply the ruleset if we have any rules
648
- if (rulesetContent) {
649
- // Apply the ruleset using the helper
650
- await this.executor.executeWithTempFile(rulesetContent);
651
- this.log('info', `Added QoS rules for ${family}`);
652
- // Mark rules as added
653
- for (const rule of this.rules) {
654
- if (rule.tableFamily === family && !rule.added) {
655
- rule.added = true;
656
- // Verify the rule was applied
657
- await this.verifyRuleApplication(rule);
658
- }
659
- }
660
- }
661
- return true;
662
- }
663
- catch (err) {
664
- this.log('error', `Failed to add traffic shaping: ${err.message}`);
665
- return false;
666
- }
667
- }
668
- /**
669
- * Setup NetworkProxy integration rules
670
- */
671
- async setupNetworkProxyIntegration(isIpv6 = false) {
672
- if (!this.settings.netProxyIntegration?.enabled) {
673
- return true;
674
- }
675
- const netProxyConfig = this.settings.netProxyIntegration;
676
- const family = isIpv6 ? 'ip6' : 'ip';
677
- const outputChain = 'nat_output';
678
- try {
679
- // Only proceed if we're redirecting localhost and have a port
680
- if (netProxyConfig.redirectLocalhost && netProxyConfig.sslTerminationPort) {
681
- const localhost = isIpv6 ? '::1' : '127.0.0.1';
682
- // Create the redirect rule
683
- const rule = `add rule ${family} ${this.tableName} ${outputChain} ${this.settings.protocol} daddr ${localhost} redirect to :${netProxyConfig.sslTerminationPort} comment "${this.ruleTag}:NETPROXY_REDIRECT"`;
684
- // Apply the rule
685
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} ${rule}`, this.settings.maxRetries, this.settings.retryDelayMs);
686
- this.log('info', `Added NetworkProxy redirection rule for ${family}`);
687
- const newRule = {
688
- tableFamily: family,
689
- tableName: this.tableName,
690
- chainName: outputChain,
691
- ruleContents: rule,
692
- added: true
693
- };
694
- this.rules.push(newRule);
695
- // Verify the rule was actually applied
696
- await this.verifyRuleApplication(newRule);
697
- }
698
- return true;
699
- }
700
- catch (err) {
701
- this.log('error', `Failed to set up NetworkProxy integration: ${err.message}`);
702
- return false;
703
- }
704
- }
705
- /**
706
- * Verify that a rule was successfully applied
707
- */
708
- async verifyRuleApplication(rule) {
709
- try {
710
- const { tableFamily, tableName, chainName, ruleContents } = rule;
711
- // Extract the distinctive parts of the rule to create a search pattern
712
- const commentMatch = ruleContents.match(/comment "([^"]+)"/);
713
- if (!commentMatch)
714
- return false;
715
- const commentTag = commentMatch[1];
716
- // List the chain to check if our rule is there
717
- const stdout = await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} list chain ${tableFamily} ${tableName} ${chainName}`, this.settings.maxRetries, this.settings.retryDelayMs);
718
- // Check if the comment appears in the output
719
- const isApplied = stdout.includes(commentTag);
720
- rule.verified = isApplied;
721
- if (!isApplied) {
722
- this.log('warn', `Rule verification failed: ${commentTag} not found in chain ${chainName}`);
723
- }
724
- else {
725
- this.log('debug', `Rule verified: ${commentTag} found in chain ${chainName}`);
726
- }
727
- return isApplied;
728
- }
729
- catch (err) {
730
- this.log('error', `Failed to verify rule application: ${err.message}`);
731
- return false;
732
- }
733
- }
734
- /**
735
- * Rolls back rules in case of error during setup
736
- */
737
- async rollbackRules() {
738
- // Process rules in reverse order (LIFO)
739
- for (let i = this.rules.length - 1; i >= 0; i--) {
740
- const rule = this.rules[i];
741
- if (rule.added) {
742
- try {
743
- // For nftables, create a delete rule by replacing 'add' with 'delete'
744
- const deleteRule = rule.ruleContents.replace('add rule', 'delete rule');
745
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} ${deleteRule}`, this.settings.maxRetries, this.settings.retryDelayMs);
746
- this.log('info', `Rolled back rule: ${deleteRule}`);
747
- rule.added = false;
748
- rule.verified = false;
749
- }
750
- catch (err) {
751
- this.log('error', `Failed to roll back rule: ${err.message}`);
752
- }
753
- }
754
- }
755
- }
756
- /**
757
- * Checks if nftables table exists
758
- */
759
- async tableExists(family, tableName) {
760
- try {
761
- const stdout = await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} list tables ${family}`, this.settings.maxRetries, this.settings.retryDelayMs);
762
- return stdout.includes(`table ${family} ${tableName}`);
763
- }
764
- catch (err) {
765
- return false;
766
- }
767
- }
768
- /**
769
- * Get system metrics like connection counts
770
- */
771
- async getSystemMetrics() {
772
- const metrics = {};
773
- try {
774
- // Try to get connection metrics if conntrack is available
775
- try {
776
- const stdout = await this.executor.executeWithRetry('conntrack -C', this.settings.maxRetries, this.settings.retryDelayMs);
777
- metrics.activeConnections = parseInt(stdout.trim(), 10);
778
- }
779
- catch (err) {
780
- // conntrack not available, skip this metric
781
- }
782
- // Try to get forwarded connections count from nftables counters
783
- try {
784
- // Look for counters in our rules
785
- const stdout = await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} list table ip ${this.tableName}`, this.settings.maxRetries, this.settings.retryDelayMs);
786
- // Parse counter information from the output
787
- const counterMatches = stdout.matchAll(/counter packets (\d+) bytes (\d+)/g);
788
- let totalPackets = 0;
789
- let totalBytes = 0;
790
- for (const match of counterMatches) {
791
- totalPackets += parseInt(match[1], 10);
792
- totalBytes += parseInt(match[2], 10);
793
- }
794
- if (totalPackets > 0) {
795
- metrics.forwardedConnections = totalPackets;
796
- metrics.bytesForwarded = {
797
- sent: totalBytes,
798
- received: 0 // We can't easily determine this without additional rules
799
- };
800
- }
801
- }
802
- catch (err) {
803
- // Failed to get counter info, skip this metric
804
- }
805
- return metrics;
806
- }
807
- catch (err) {
808
- this.log('error', `Failed to get system metrics: ${err.message}`);
809
- return metrics;
810
- }
811
- }
812
- /**
813
- * Get status of IP sets
814
- */
815
- async getIPSetStatus() {
816
- const result = [];
817
- try {
818
- for (const family of ['ip', 'ip6']) {
819
- try {
820
- const stdout = await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} list sets ${family} ${this.tableName}`, this.settings.maxRetries, this.settings.retryDelayMs);
821
- const setMatches = stdout.matchAll(/set (\w+) {\s*type (\w+)/g);
822
- for (const match of setMatches) {
823
- const setName = match[1];
824
- const setType = match[2];
825
- // Get element count from tracking map
826
- const setKey = `${family}:${setName}`;
827
- const elements = this.ipSets.get(setKey) || [];
828
- result.push({
829
- name: setName,
830
- elementCount: elements.length,
831
- type: setType
832
- });
833
- }
834
- }
835
- catch (err) {
836
- // No sets for this family, or table doesn't exist
837
- }
838
- }
839
- return result;
840
- }
841
- catch (err) {
842
- this.log('error', `Failed to get IP set status: ${err.message}`);
843
- return result;
844
- }
845
- }
846
- /**
847
- * Get detailed status about the current state of the proxy
848
- */
849
- async getStatus() {
850
- const result = {
851
- active: this.rules.some(r => r.added),
852
- ruleCount: {
853
- total: this.rules.length,
854
- added: this.rules.filter(r => r.added).length,
855
- verified: this.rules.filter(r => r.verified).length
856
- },
857
- tablesConfigured: [],
858
- metrics: {},
859
- qosEnabled: this.settings.qos?.enabled || false
860
- };
861
- try {
862
- // Get list of configured tables
863
- const stdout = await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} list tables`, this.settings.maxRetries, this.settings.retryDelayMs);
864
- const tableRegex = /table (ip|ip6) (\w+)/g;
865
- let match;
866
- while ((match = tableRegex.exec(stdout)) !== null) {
867
- const [, family, name] = match;
868
- if (name === this.tableName) {
869
- result.tablesConfigured.push({ family, tableName: name });
870
- }
871
- }
872
- // Get system metrics
873
- result.metrics = await this.getSystemMetrics();
874
- // Get IP set status if using IP sets
875
- if (this.settings.useIPSets) {
876
- result.ipSetsConfigured = await this.getIPSetStatus();
877
- }
878
- return result;
879
- }
880
- catch (err) {
881
- this.log('error', `Failed to get status: ${err.message}`);
882
- return result;
883
- }
884
- }
885
- /**
886
- * Performs a dry run to see what commands would be executed without actually applying them
887
- */
888
- async dryRun() {
889
- const commands = [];
890
- // Simulate all the necessary setup steps and collect commands
891
- // Tables and chains
892
- commands.push(`add table ip ${this.tableName}`);
893
- commands.push(`add chain ip ${this.tableName} nat_prerouting { type nat hook prerouting priority -100; }`);
894
- if (!this.settings.preserveSourceIP) {
895
- commands.push(`add chain ip ${this.tableName} nat_postrouting { type nat hook postrouting priority 100; }`);
896
- }
897
- if (this.settings.netProxyIntegration?.enabled && this.settings.netProxyIntegration.redirectLocalhost) {
898
- commands.push(`add chain ip ${this.tableName} nat_output { type nat hook output priority 0; }`);
899
- }
900
- if (this.settings.qos?.enabled) {
901
- commands.push(`add chain ip ${this.tableName} qos_forward { type filter hook forward priority 0; }`);
902
- }
903
- // Add IPv6 tables if enabled
904
- if (this.settings.ipv6Support) {
905
- commands.push(`add table ip6 ${this.tableName}`);
906
- commands.push(`add chain ip6 ${this.tableName} nat_prerouting { type nat hook prerouting priority -100; }`);
907
- if (!this.settings.preserveSourceIP) {
908
- commands.push(`add chain ip6 ${this.tableName} nat_postrouting { type nat hook postrouting priority 100; }`);
909
- }
910
- if (this.settings.netProxyIntegration?.enabled && this.settings.netProxyIntegration.redirectLocalhost) {
911
- commands.push(`add chain ip6 ${this.tableName} nat_output { type nat hook output priority 0; }`);
912
- }
913
- if (this.settings.qos?.enabled) {
914
- commands.push(`add chain ip6 ${this.tableName} qos_forward { type filter hook forward priority 0; }`);
915
- }
916
- }
917
- // Source IP filters
918
- if (this.settings.useIPSets) {
919
- if (this.settings.ipBlockList?.length) {
920
- commands.push(`add set ip ${this.tableName} banned_ips { type ipv4_addr; }`);
921
- commands.push(`add element ip ${this.tableName} banned_ips { ${this.settings.ipBlockList.join(', ')} }`);
922
- commands.push(`add rule ip ${this.tableName} nat_prerouting ip saddr @banned_ips drop comment "${this.ruleTag}:BANNED_SET"`);
923
- }
924
- if (this.settings.ipAllowList?.length) {
925
- commands.push(`add set ip ${this.tableName} allowed_ips { type ipv4_addr; }`);
926
- commands.push(`add element ip ${this.tableName} allowed_ips { ${this.settings.ipAllowList.join(', ')} }`);
927
- commands.push(`add rule ip ${this.tableName} nat_prerouting ip saddr @allowed_ips ${this.settings.protocol} dport {${this.getAllPorts(this.settings.fromPort)}} accept comment "${this.ruleTag}:ALLOWED_SET"`);
928
- commands.push(`add rule ip ${this.tableName} nat_prerouting ${this.settings.protocol} dport {${this.getAllPorts(this.settings.fromPort)}} drop comment "${this.ruleTag}:DENY_ALL"`);
929
- }
930
- }
931
- else if (this.settings.ipBlockList?.length || this.settings.ipAllowList?.length) {
932
- // Traditional approach without IP sets
933
- if (this.settings.ipBlockList?.length) {
934
- for (const ip of this.settings.ipBlockList) {
935
- commands.push(`add rule ip ${this.tableName} nat_prerouting ip saddr ${ip} drop comment "${this.ruleTag}:BANNED"`);
936
- }
937
- }
938
- if (this.settings.ipAllowList?.length) {
939
- for (const ip of this.settings.ipAllowList) {
940
- commands.push(`add rule ip ${this.tableName} nat_prerouting ip saddr ${ip} ${this.settings.protocol} dport {${this.getAllPorts(this.settings.fromPort)}} accept comment "${this.ruleTag}:ALLOWED"`);
941
- }
942
- commands.push(`add rule ip ${this.tableName} nat_prerouting ${this.settings.protocol} dport {${this.getAllPorts(this.settings.fromPort)}} drop comment "${this.ruleTag}:DENY_ALL"`);
943
- }
944
- }
945
- // Port forwarding rules
946
- if (this.settings.useAdvancedNAT) {
947
- // Advanced NAT with connection tracking
948
- const fromPortRanges = normalizePortSpec(this.settings.fromPort);
949
- const toPortRanges = normalizePortSpec(this.settings.toPort);
950
- if (fromPortRanges.length === 1 && toPortRanges.length === 1) {
951
- const fromRange = fromPortRanges[0];
952
- const toRange = toPortRanges[0];
953
- if (fromRange.from === fromRange.to && toRange.from === toRange.to) {
954
- commands.push(`add rule ip ${this.tableName} nat_prerouting ${this.settings.protocol} dport ${fromRange.from} ct state new dnat to ${this.settings.toHost}:${toRange.from} comment "${this.ruleTag}:DNAT_CT"`);
955
- }
956
- else if ((fromRange.to - fromRange.from) === (toRange.to - toRange.from)) {
957
- commands.push(`add rule ip ${this.tableName} nat_prerouting ${this.settings.protocol} dport ${fromRange.from}-${fromRange.to} ct state new dnat to ${this.settings.toHost}:${toRange.from}-${toRange.to} comment "${this.ruleTag}:DNAT_RANGE_CT"`);
958
- }
959
- commands.push(`add rule ip ${this.tableName} nat_prerouting ct state established,related accept comment "${this.ruleTag}:CT_ESTABLISHED"`);
960
- }
961
- }
962
- else {
963
- // Standard NAT rules
964
- const fromRanges = normalizePortSpec(this.settings.fromPort);
965
- const toRanges = normalizePortSpec(this.settings.toPort);
966
- if (fromRanges.length === 1 && toRanges.length === 1) {
967
- const fromRange = fromRanges[0];
968
- const toRange = toRanges[0];
969
- if (fromRange.from === fromRange.to && toRange.from === toRange.to) {
970
- commands.push(`add rule ip ${this.tableName} nat_prerouting ${this.settings.protocol} dport ${fromRange.from} dnat to ${this.settings.toHost}:${toRange.from} comment "${this.ruleTag}:DNAT"`);
971
- }
972
- else {
973
- commands.push(`add rule ip ${this.tableName} nat_prerouting ${this.settings.protocol} dport ${fromRange.from}-${fromRange.to} dnat to ${this.settings.toHost}:${toRange.from}-${toRange.to} comment "${this.ruleTag}:DNAT_RANGE"`);
974
- }
975
- }
976
- else if (toRanges.length === 1) {
977
- // One-to-many mapping
978
- for (const fromRange of fromRanges) {
979
- commands.push(`add rule ip ${this.tableName} nat_prerouting ${this.settings.protocol} dport ${fromRange.from}-${fromRange.to} dnat to ${this.settings.toHost}:${toRanges[0].from}-${toRanges[0].to} comment "${this.ruleTag}:DNAT_RANGE"`);
980
- }
981
- }
982
- else {
983
- // One-to-one mapping of multiple ranges
984
- for (let i = 0; i < fromRanges.length; i++) {
985
- commands.push(`add rule ip ${this.tableName} nat_prerouting ${this.settings.protocol} dport ${fromRanges[i].from}-${fromRanges[i].to} dnat to ${this.settings.toHost}:${toRanges[i].from}-${toRanges[i].to} comment "${this.ruleTag}:DNAT_RANGE"`);
986
- }
987
- }
988
- }
989
- // Masquerade rules if not preserving source IP
990
- if (!this.settings.preserveSourceIP) {
991
- commands.push(`add rule ip ${this.tableName} nat_postrouting ${this.settings.protocol} daddr ${this.settings.toHost} dport {${this.getAllPorts(this.settings.toPort)}} masquerade comment "${this.ruleTag}:MASQ"`);
992
- }
993
- // NetworkProxy integration
994
- if (this.settings.netProxyIntegration?.enabled &&
995
- this.settings.netProxyIntegration.redirectLocalhost &&
996
- this.settings.netProxyIntegration.sslTerminationPort) {
997
- commands.push(`add rule ip ${this.tableName} nat_output ${this.settings.protocol} daddr 127.0.0.1 redirect to :${this.settings.netProxyIntegration.sslTerminationPort} comment "${this.ruleTag}:NETPROXY_REDIRECT"`);
998
- }
999
- // QoS rules
1000
- if (this.settings.qos?.enabled) {
1001
- if (this.settings.qos.maxRate) {
1002
- commands.push(`add rule ip ${this.tableName} qos_forward ip daddr ${this.settings.toHost} ${this.settings.protocol} dport {${this.getAllPorts(this.settings.toPort)}} limit rate over ${this.settings.qos.maxRate} drop comment "${this.ruleTag}:QOS_RATE"`);
1003
- }
1004
- if (this.settings.qos.priority !== undefined) {
1005
- commands.push(`add chain ip ${this.tableName} prio${this.settings.qos.priority} { type filter hook forward priority ${this.settings.qos.priority * 10}; }`);
1006
- for (const range of normalizePortSpec(this.settings.toPort)) {
1007
- commands.push(`add rule ip ${this.tableName} qos_forward ${this.settings.protocol} dport ${range.from}-${range.to} counter goto prio${this.settings.qos.priority} comment "${this.ruleTag}:QOS_PRIORITY"`);
1008
- }
1009
- }
1010
- }
1011
- return commands;
1012
- }
1013
- /**
1014
- * Starts the proxy by setting up all nftables rules
1015
- */
1016
- async start() {
1017
- // Check if nftables is available
1018
- const nftablesAvailable = await this.checkNftablesAvailability();
1019
- if (!nftablesAvailable) {
1020
- throw new NftResourceError('nftables is not available or not properly configured');
1021
- }
1022
- // Optionally clean slate first
1023
- if (this.settings.forceCleanSlate) {
1024
- await NfTablesProxy.cleanSlate();
1025
- }
1026
- // Set up tables and chains for IPv4
1027
- const setupSuccess = await this.setupTablesAndChains();
1028
- if (!setupSuccess) {
1029
- throw new NftExecutionError('Failed to set up nftables tables and chains');
1030
- }
1031
- // Set up IPv6 tables and chains if enabled
1032
- if (this.settings.ipv6Support) {
1033
- const setupIPv6Success = await this.setupTablesAndChains(true);
1034
- if (!setupIPv6Success) {
1035
- this.log('warn', 'Failed to set up IPv6 tables and chains, continuing with IPv4 only');
1036
- }
1037
- }
1038
- // Add source IP filters
1039
- await this.addSourceIPFilters();
1040
- if (this.settings.ipv6Support) {
1041
- await this.addSourceIPFilters(true);
1042
- }
1043
- // Set up advanced NAT with connection tracking if enabled
1044
- if (this.settings.useAdvancedNAT) {
1045
- const advancedNatSuccess = await this.setupAdvancedNAT();
1046
- if (!advancedNatSuccess) {
1047
- this.log('warn', 'Failed to set up advanced NAT, falling back to standard NAT');
1048
- this.settings.useAdvancedNAT = false;
1049
- }
1050
- else if (this.settings.ipv6Support) {
1051
- await this.setupAdvancedNAT(true);
1052
- }
1053
- }
1054
- // Add port forwarding rules (skip if using advanced NAT)
1055
- if (!this.settings.useAdvancedNAT) {
1056
- const forwardingSuccess = await this.addPortForwardingRules();
1057
- if (!forwardingSuccess) {
1058
- throw new NftExecutionError('Failed to add port forwarding rules');
1059
- }
1060
- // Add IPv6 port forwarding rules if enabled
1061
- if (this.settings.ipv6Support) {
1062
- const forwardingIPv6Success = await this.addPortForwardingRules(true);
1063
- if (!forwardingIPv6Success) {
1064
- this.log('warn', 'Failed to add IPv6 port forwarding rules');
1065
- }
1066
- }
1067
- }
1068
- // Set up QoS if enabled
1069
- if (this.settings.qos?.enabled) {
1070
- const qosSuccess = await this.addTrafficShaping();
1071
- if (!qosSuccess) {
1072
- this.log('warn', 'Failed to set up QoS rules, continuing without traffic shaping');
1073
- }
1074
- else if (this.settings.ipv6Support) {
1075
- await this.addTrafficShaping(true);
1076
- }
1077
- }
1078
- // Set up NetworkProxy integration if enabled
1079
- if (this.settings.netProxyIntegration?.enabled) {
1080
- const netProxySetupSuccess = await this.setupNetworkProxyIntegration();
1081
- if (!netProxySetupSuccess) {
1082
- this.log('warn', 'Failed to set up NetworkProxy integration');
1083
- }
1084
- if (this.settings.ipv6Support) {
1085
- await this.setupNetworkProxyIntegration(true);
1086
- }
1087
- }
1088
- // Final check - ensure we have at least one rule added
1089
- if (this.rules.filter(r => r.added).length === 0) {
1090
- throw new NftExecutionError('No rules were added');
1091
- }
1092
- this.log('info', 'NfTablesProxy started successfully');
1093
- }
1094
- /**
1095
- * Stops the proxy by removing all added rules
1096
- */
1097
- async stop() {
1098
- try {
1099
- let rulesetContent = '';
1100
- // Process rules in reverse order (LIFO)
1101
- for (let i = this.rules.length - 1; i >= 0; i--) {
1102
- const rule = this.rules[i];
1103
- if (rule.added) {
1104
- // Create delete rules by replacing 'add' with 'delete'
1105
- const deleteRule = rule.ruleContents.replace('add rule', 'delete rule');
1106
- rulesetContent += `${deleteRule}\n`;
1107
- }
1108
- }
1109
- // Apply the ruleset if we have any rules to delete
1110
- if (rulesetContent) {
1111
- // Write to temporary file
1112
- await AsyncFileSystem.writeFile(this.tempFilePath, rulesetContent);
1113
- try {
1114
- // Apply the ruleset
1115
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} -f ${this.tempFilePath}`, this.settings.maxRetries, this.settings.retryDelayMs);
1116
- this.log('info', 'Removed all added rules');
1117
- // Mark all rules as removed
1118
- this.rules.forEach(rule => {
1119
- rule.added = false;
1120
- rule.verified = false;
1121
- });
1122
- }
1123
- finally {
1124
- // Remove temporary file
1125
- await AsyncFileSystem.remove(this.tempFilePath);
1126
- }
1127
- }
1128
- // Clean up IP sets if we created any
1129
- if (this.settings.useIPSets && this.ipSets.size > 0) {
1130
- for (const [key, _] of this.ipSets) {
1131
- const [family, setName] = key.split(':');
1132
- try {
1133
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} delete set ${family} ${this.tableName} ${setName}`, this.settings.maxRetries, this.settings.retryDelayMs);
1134
- this.log('info', `Removed IP set ${setName} from ${family} ${this.tableName}`);
1135
- }
1136
- catch (err) {
1137
- this.log('warn', `Failed to remove IP set ${setName}: ${err.message}`);
1138
- }
1139
- }
1140
- this.ipSets.clear();
1141
- }
1142
- // Optionally clean up tables if they're empty
1143
- await this.cleanupEmptyTables();
1144
- this.log('info', 'NfTablesProxy stopped successfully');
1145
- }
1146
- catch (err) {
1147
- this.log('error', `Error stopping NfTablesProxy: ${err.message}`);
1148
- throw err;
1149
- }
1150
- }
1151
- /**
1152
- * Synchronous version of stop, for use in exit handlers only.
1153
- * Uses single-attempt commands without retry (process is exiting anyway).
1154
- */
1155
- stopSync() {
1156
- try {
1157
- let rulesetContent = '';
1158
- // Process rules in reverse order (LIFO)
1159
- for (let i = this.rules.length - 1; i >= 0; i--) {
1160
- const rule = this.rules[i];
1161
- if (rule.added) {
1162
- // Create delete rules by replacing 'add' with 'delete'
1163
- const deleteRule = rule.ruleContents.replace('add rule', 'delete rule');
1164
- rulesetContent += `${deleteRule}\n`;
1165
- }
1166
- }
1167
- // Apply the ruleset if we have any rules to delete
1168
- if (rulesetContent) {
1169
- // Write to temporary file
1170
- fs.writeFileSync(this.tempFilePath, rulesetContent);
1171
- // Apply the ruleset (single attempt, no retry - process is exiting)
1172
- this.executor.executeSync(`${NfTablesProxy.NFT_CMD} -f ${this.tempFilePath}`);
1173
- this.log('info', 'Removed all added rules');
1174
- // Mark all rules as removed
1175
- this.rules.forEach(rule => {
1176
- rule.added = false;
1177
- rule.verified = false;
1178
- });
1179
- // Remove temporary file
1180
- try {
1181
- fs.unlinkSync(this.tempFilePath);
1182
- }
1183
- catch {
1184
- // Ignore - process is exiting
1185
- }
1186
- }
1187
- // Clean up IP sets if we created any
1188
- if (this.settings.useIPSets && this.ipSets.size > 0) {
1189
- for (const [key, _] of this.ipSets) {
1190
- const [family, setName] = key.split(':');
1191
- try {
1192
- this.executor.executeSync(`${NfTablesProxy.NFT_CMD} delete set ${family} ${this.tableName} ${setName}`);
1193
- }
1194
- catch {
1195
- // Non-critical error, continue
1196
- }
1197
- }
1198
- }
1199
- // Optionally clean up tables if they're empty (sync version)
1200
- this.cleanupEmptyTablesSync();
1201
- this.log('info', 'NfTablesProxy stopped successfully');
1202
- }
1203
- catch (err) {
1204
- this.log('error', `Error stopping NfTablesProxy: ${err.message}`);
1205
- }
1206
- }
1207
- /**
1208
- * Cleans up empty tables
1209
- */
1210
- async cleanupEmptyTables() {
1211
- // Check if tables are empty, and if so, delete them
1212
- for (const family of ['ip', 'ip6']) {
1213
- // Skip IPv6 if not enabled
1214
- if (family === 'ip6' && !this.settings.ipv6Support) {
1215
- continue;
1216
- }
1217
- try {
1218
- // Check if table exists
1219
- const tableExists = await this.tableExists(family, this.tableName);
1220
- if (!tableExists) {
1221
- continue;
1222
- }
1223
- // Check if the table has any rules
1224
- const stdout = await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} list table ${family} ${this.tableName}`, this.settings.maxRetries, this.settings.retryDelayMs);
1225
- const hasRules = stdout.includes('rule');
1226
- if (!hasRules) {
1227
- // Table is empty, delete it
1228
- await this.executor.executeWithRetry(`${NfTablesProxy.NFT_CMD} delete table ${family} ${this.tableName}`, this.settings.maxRetries, this.settings.retryDelayMs);
1229
- this.log('info', `Deleted empty table ${family} ${this.tableName}`);
1230
- }
1231
- }
1232
- catch (err) {
1233
- this.log('error', `Error cleaning up tables: ${err.message}`);
1234
- }
1235
- }
1236
- }
1237
- /**
1238
- * Synchronous version of cleanupEmptyTables (for exit handlers only)
1239
- */
1240
- cleanupEmptyTablesSync() {
1241
- // Check if tables are empty, and if so, delete them
1242
- for (const family of ['ip', 'ip6']) {
1243
- // Skip IPv6 if not enabled
1244
- if (family === 'ip6' && !this.settings.ipv6Support) {
1245
- continue;
1246
- }
1247
- try {
1248
- // Check if table exists
1249
- const tableExistsOutput = this.executor.executeSync(`${NfTablesProxy.NFT_CMD} list tables ${family}`);
1250
- const tableExists = tableExistsOutput.includes(`table ${family} ${this.tableName}`);
1251
- if (!tableExists) {
1252
- continue;
1253
- }
1254
- // Check if the table has any rules
1255
- const stdout = this.executor.executeSync(`${NfTablesProxy.NFT_CMD} list table ${family} ${this.tableName}`);
1256
- const hasRules = stdout.includes('rule');
1257
- if (!hasRules) {
1258
- // Table is empty, delete it
1259
- this.executor.executeSync(`${NfTablesProxy.NFT_CMD} delete table ${family} ${this.tableName}`);
1260
- this.log('info', `Deleted empty table ${family} ${this.tableName}`);
1261
- }
1262
- }
1263
- catch (err) {
1264
- this.log('error', `Error cleaning up tables: ${err.message}`);
1265
- }
1266
- }
1267
- }
1268
- /**
1269
- * Removes all nftables rules created by this module
1270
- */
1271
- static async cleanSlate() {
1272
- try {
1273
- // Check for rules with our comment pattern
1274
- const stdout = await execAsync(`${NfTablesProxy.NFT_CMD} list ruleset`);
1275
- // Extract our tables
1276
- const tableMatches = stdout.stdout.match(/table (ip|ip6) (\w+) {[^}]*NfTablesProxy:[^}]*}/g);
1277
- if (tableMatches) {
1278
- for (const tableMatch of tableMatches) {
1279
- // Extract table family and name
1280
- const familyMatch = tableMatch.match(/table (ip|ip6) (\w+)/);
1281
- if (familyMatch) {
1282
- const family = familyMatch[1];
1283
- const tableName = familyMatch[2];
1284
- // Delete the table
1285
- await execAsync(`${NfTablesProxy.NFT_CMD} delete table ${family} ${tableName}`);
1286
- console.log(`Deleted table ${family} ${tableName} containing NfTablesProxy rules`);
1287
- }
1288
- }
1289
- }
1290
- else {
1291
- console.log('No NfTablesProxy rules found to clean up');
1292
- }
1293
- }
1294
- catch (err) {
1295
- console.error(`Error in cleanSlate: ${err}`);
1296
- }
1297
- }
1298
- /**
1299
- * Synchronous version of cleanSlate
1300
- * @deprecated This method blocks the event loop and should be avoided. Use cleanSlate() instead.
1301
- * WARNING: This method uses execSync which blocks the entire Node.js event loop!
1302
- */
1303
- static cleanSlateSync() {
1304
- console.warn('[DEPRECATION WARNING] cleanSlateSync blocks the event loop and should not be used. Consider using the async cleanSlate() method instead.');
1305
- try {
1306
- // Check for rules with our comment pattern
1307
- const stdout = execSync(`${NfTablesProxy.NFT_CMD} list ruleset`).toString();
1308
- // Extract our tables
1309
- const tableMatches = stdout.match(/table (ip|ip6) (\w+) {[^}]*NfTablesProxy:[^}]*}/g);
1310
- if (tableMatches) {
1311
- for (const tableMatch of tableMatches) {
1312
- // Extract table family and name
1313
- const familyMatch = tableMatch.match(/table (ip|ip6) (\w+)/);
1314
- if (familyMatch) {
1315
- const family = familyMatch[1];
1316
- const tableName = familyMatch[2];
1317
- // Delete the table
1318
- execSync(`${NfTablesProxy.NFT_CMD} delete table ${family} ${tableName}`);
1319
- console.log(`Deleted table ${family} ${tableName} containing NfTablesProxy rules`);
1320
- }
1321
- }
1322
- }
1323
- else {
1324
- console.log('No NfTablesProxy rules found to clean up');
1325
- }
1326
- }
1327
- catch (err) {
1328
- console.error(`Error in cleanSlateSync: ${err}`);
1329
- }
1330
- }
1331
- /**
1332
- * Improved logging with structured output
1333
- */
1334
- log(level, message, meta) {
1335
- if (!this.settings.enableLogging && (level === 'info' || level === 'debug')) {
1336
- return;
1337
- }
1338
- const timestamp = new Date().toISOString();
1339
- const logData = {
1340
- timestamp,
1341
- level: level.toUpperCase(),
1342
- message,
1343
- ...meta,
1344
- context: {
1345
- instance: this.ruleTag,
1346
- table: this.tableName
1347
- }
1348
- };
1349
- // Determine if output should be JSON or plain text based on settings
1350
- const useJson = this.settings.logFormat === 'json';
1351
- if (useJson) {
1352
- const logOutput = JSON.stringify(logData);
1353
- console.log(logOutput);
1354
- return;
1355
- }
1356
- // Plain text format
1357
- const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';
1358
- switch (level) {
1359
- case 'info':
1360
- console.log(`[${timestamp}] [INFO] ${message}${metaStr}`);
1361
- break;
1362
- case 'warn':
1363
- console.warn(`[${timestamp}] [WARN] ${message}${metaStr}`);
1364
- break;
1365
- case 'error':
1366
- console.error(`[${timestamp}] [ERROR] ${message}${metaStr}`);
1367
- break;
1368
- case 'debug':
1369
- console.log(`[${timestamp}] [DEBUG] ${message}${metaStr}`);
1370
- break;
1371
- }
1372
- }
1373
- }
1374
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmZ0YWJsZXMtcHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL25mdGFibGVzLXByb3h5L25mdGFibGVzLXByb3h5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQy9DLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDakMsT0FBTyxLQUFLLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFDekIsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxLQUFLLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFDekIsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQy9ELE9BQU8sRUFDTCxrQkFBa0IsRUFDbEIsaUJBQWlCLEVBQ2pCLGdCQUFnQixFQUNqQixNQUFNLG1CQUFtQixDQUFDO0FBTTNCLE9BQU8sRUFDTCxrQkFBa0IsRUFDbEIsaUJBQWlCLEVBQ2pCLGdCQUFnQixFQUNoQixpQkFBaUIsRUFDbEIsTUFBTSxrQkFBa0IsQ0FBQztBQUUxQixNQUFNLFNBQVMsR0FBRyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7QUFlbEM7Ozs7R0FJRztBQUNILE1BQU0sT0FBTyxhQUFhO2FBUVQsWUFBTyxHQUFHLEtBQUssQUFBUixDQUFTO0lBRS9CLFlBQVksUUFBNkI7UUFSakMsVUFBSyxHQUFtQixFQUFFLENBQUM7UUFDM0IsV0FBTSxHQUEwQixJQUFJLEdBQUcsRUFBRSxDQUFDLENBQUMsNkJBQTZCO1FBUTlFLCtDQUErQztRQUMvQyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUUzQix1QkFBdUI7UUFDdkIsSUFBSSxDQUFDLFFBQVEsR0FBRztZQUNkLEdBQUcsUUFBUTtZQUNYLE1BQU0sRUFBRSxRQUFRLENBQUMsTUFBTSxJQUFJLFdBQVc7WUFDdEMsUUFBUSxF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