feat(IPTablesProxy): Introduce IPTablesProxy class for managing iptables NAT rules

This commit is contained in:
Philipp Kunz 2025-02-24 23:27:48 +00:00
parent 63ebad06ea
commit ff4f44d6fc
5 changed files with 100 additions and 4 deletions

View File

@ -1,5 +1,12 @@
# Changelog
## 2025-02-24 - 3.12.0 - feat(IPTablesProxy)
Introduce IPTablesProxy class for managing iptables NAT rules
- Added IPTablesProxy class to facilitate basic port forwarding using iptables.
- Introduced IIpTableProxySettings interface for configuring IPTablesProxy.
- Implemented start and stop methods for managing iptables rules dynamically.
## 2025-02-24 - 3.11.0 - feat(Port80Handler)
Add automatic certificate issuance with ACME client

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartproxy',
version: '3.11.0',
version: '3.12.0',
description: 'a proxy for handling high workloads of proxying'
}

View File

@ -0,0 +1,88 @@
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
export interface IIpTableProxySettings {
fromPort: number;
toPort: number;
toHost?: string; // Target host for proxying; defaults to 'localhost'
preserveSourceIP?: boolean; // If true, the original source IP is preserved.
}
/**
* IPTablesProxy sets up iptables NAT rules to forward TCP traffic.
* It only supports basic port forwarding.
*/
export class IPTablesProxy {
public settings: IIpTableProxySettings;
private rulesInstalled: boolean = false;
constructor(settings: IIpTableProxySettings) {
this.settings = {
...settings,
toHost: settings.toHost || 'localhost',
};
}
/**
* Sets up iptables rules for port forwarding.
*/
public async start(): Promise<void> {
const dnatCmd = `iptables -t nat -A PREROUTING -p tcp --dport ${this.settings.fromPort} -j DNAT --to-destination ${this.settings.toHost}:${this.settings.toPort}`;
try {
await execAsync(dnatCmd);
console.log(`Added iptables rule: ${dnatCmd}`);
this.rulesInstalled = true;
} catch (err) {
console.error(`Failed to add iptables DNAT rule: ${err}`);
throw err;
}
if (!this.settings.preserveSourceIP) {
const masqueradeCmd = `iptables -t nat -A POSTROUTING -p tcp -d ${this.settings.toHost} --dport ${this.settings.toPort} -j MASQUERADE`;
try {
await execAsync(masqueradeCmd);
console.log(`Added iptables rule: ${masqueradeCmd}`);
} catch (err) {
console.error(`Failed to add iptables MASQUERADE rule: ${err}`);
// Roll back the DNAT rule if MASQUERADE fails.
try {
const rollbackCmd = `iptables -t nat -D PREROUTING -p tcp --dport ${this.settings.fromPort} -j DNAT --to-destination ${this.settings.toHost}:${this.settings.toPort}`;
await execAsync(rollbackCmd);
this.rulesInstalled = false;
} catch (rollbackErr) {
console.error(`Rollback failed: ${rollbackErr}`);
}
throw err;
}
}
}
/**
* Removes the iptables rules that were added in start().
*/
public async stop(): Promise<void> {
if (!this.rulesInstalled) return;
const dnatDelCmd = `iptables -t nat -D PREROUTING -p tcp --dport ${this.settings.fromPort} -j DNAT --to-destination ${this.settings.toHost}:${this.settings.toPort}`;
try {
await execAsync(dnatDelCmd);
console.log(`Removed iptables rule: ${dnatDelCmd}`);
} catch (err) {
console.error(`Failed to remove iptables DNAT rule: ${err}`);
}
if (!this.settings.preserveSourceIP) {
const masqueradeDelCmd = `iptables -t nat -D POSTROUTING -p tcp -d ${this.settings.toHost} --dport ${this.settings.toPort} -j MASQUERADE`;
try {
await execAsync(masqueradeDelCmd);
console.log(`Removed iptables rule: ${masqueradeDelCmd}`);
} catch (err) {
console.error(`Failed to remove iptables MASQUERADE rule: ${err}`);
}
}
this.rulesInstalled = false;
}
}

View File

@ -6,7 +6,7 @@ export interface IDomainConfig {
targetIP?: string; // Optional target IP for this domain
}
export interface IProxySettings extends plugins.tls.TlsOptions {
export interface IPortProxySettings extends plugins.tls.TlsOptions {
fromPort: number;
toPort: number;
toHost?: string; // Target host to proxy to, defaults to 'localhost'
@ -89,7 +89,7 @@ interface IConnectionRecord {
export class PortProxy {
netServer: plugins.net.Server;
settings: IProxySettings;
settings: IPortProxySettings;
// Unified record tracking each connection pair.
private connectionRecords: Set<IConnectionRecord> = new Set();
private connectionLogger: NodeJS.Timeout | null = null;
@ -102,7 +102,7 @@ export class PortProxy {
outgoing: {},
};
constructor(settings: IProxySettings) {
constructor(settings: IPortProxySettings) {
this.settings = {
...settings,
toHost: settings.toHost || 'localhost',

View File

@ -1,3 +1,4 @@
export * from './classes.iptablesproxy.js';
export * from './classes.networkproxy.js';
export * from './classes.portproxy.js';
export * from './classes.port80handler.js';