diff --git a/changelog.md b/changelog.md index d269db0..f126d50 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 1ce0c99..10b5210 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -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' } diff --git a/ts/classes.iptablesproxy.ts b/ts/classes.iptablesproxy.ts new file mode 100644 index 0000000..e64fc4a --- /dev/null +++ b/ts/classes.iptablesproxy.ts @@ -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 { + 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 { + 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; + } +} \ No newline at end of file diff --git a/ts/classes.portproxy.ts b/ts/classes.portproxy.ts index 087b0c6..e83bd18 100644 --- a/ts/classes.portproxy.ts +++ b/ts/classes.portproxy.ts @@ -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 = 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', diff --git a/ts/index.ts b/ts/index.ts index d8259f9..4956c92 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -1,3 +1,4 @@ +export * from './classes.iptablesproxy.js'; export * from './classes.networkproxy.js'; export * from './classes.portproxy.js'; export * from './classes.port80handler.js';