UNPKG

@push.rocks/smartproxy

Version:

A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.

237 lines 16.3 kB
import * as plugins from '../../plugins.js'; import { NfTablesProxy } from '../nftables-proxy/nftables-proxy.js'; /** * Manages NFTables rules based on SmartProxy route configurations * * This class bridges the gap between SmartProxy routes and the NFTablesProxy, * allowing high-performance kernel-level packet forwarding for routes that * specify NFTables as their forwarding engine. */ export class NFTablesManager { /** * Creates a new NFTablesManager * * @param smartProxy The SmartProxy instance */ constructor(smartProxy) { this.smartProxy = smartProxy; this.rulesMap = new Map(); } /** * Provision NFTables rules for a route * * @param route The route configuration * @returns A promise that resolves to true if successful, false otherwise */ async provisionRoute(route) { // Generate a unique ID for this route const routeId = this.generateRouteId(route); // Skip if route doesn't use NFTables if (route.action.forwardingEngine !== 'nftables') { return true; } // Create NFTables options from route configuration const nftOptions = this.createNfTablesOptions(route); // Create and start an NFTablesProxy instance const proxy = new NfTablesProxy(nftOptions); try { await proxy.start(); this.rulesMap.set(routeId, proxy); return true; } catch (err) { console.error(`Failed to provision NFTables rules for route ${route.name || 'unnamed'}: ${err.message}`); return false; } } /** * Remove NFTables rules for a route * * @param route The route configuration * @returns A promise that resolves to true if successful, false otherwise */ async deprovisionRoute(route) { const routeId = this.generateRouteId(route); const proxy = this.rulesMap.get(routeId); if (!proxy) { return true; // Nothing to remove } try { await proxy.stop(); this.rulesMap.delete(routeId); return true; } catch (err) { console.error(`Failed to deprovision NFTables rules for route ${route.name || 'unnamed'}: ${err.message}`); return false; } } /** * Update NFTables rules when route changes * * @param oldRoute The previous route configuration * @param newRoute The new route configuration * @returns A promise that resolves to true if successful, false otherwise */ async updateRoute(oldRoute, newRoute) { // Remove old rules and add new ones await this.deprovisionRoute(oldRoute); return this.provisionRoute(newRoute); } /** * Generate a unique ID for a route * * @param route The route configuration * @returns A unique ID string */ generateRouteId(route) { // Generate a unique ID based on route properties // Include the route name, match criteria, and a timestamp const matchStr = JSON.stringify({ ports: route.match.ports, domains: route.match.domains }); return `${route.name || 'unnamed'}-${matchStr}-${route.id || Date.now().toString()}`; } /** * Create NFTablesProxy options from a route configuration * * @param route The route configuration * @returns NFTableProxyOptions object */ createNfTablesOptions(route) { const { action } = route; // Ensure we have targets if (!action.targets || action.targets.length === 0) { throw new Error('Route must have targets to use NFTables forwarding'); } // NFTables can only handle a single target, so we use the first target without match criteria // or the first target if all have match criteria const defaultTarget = action.targets.find(t => !t.match) || action.targets[0]; // Convert port specifications const fromPorts = this.expandPortRange(route.match.ports); // Determine target port let toPorts; if (defaultTarget.port === 'preserve') { // 'preserve' means use the same ports as the source toPorts = fromPorts; } else if (typeof defaultTarget.port === 'function') { // For function-based ports, we can't determine at setup time // Use the "preserve" approach and let NFTables handle it toPorts = fromPorts; } else { toPorts = defaultTarget.port; } // Determine target host let toHost; if (typeof defaultTarget.host === 'function') { // Can't determine at setup time, use localhost as a placeholder // and rely on run-time handling toHost = 'localhost'; } else if (Array.isArray(defaultTarget.host)) { // Use first host for now - NFTables will do simple round-robin toHost = defaultTarget.host[0]; } else { toHost = defaultTarget.host; } // Create options const options = { fromPort: fromPorts, toPort: toPorts, toHost: toHost, protocol: action.nftables?.protocol || 'tcp', preserveSourceIP: action.nftables?.preserveSourceIP !== undefined ? action.nftables.preserveSourceIP : this.smartProxy.settings.preserveSourceIP, useIPSets: action.nftables?.useIPSets !== false, useAdvancedNAT: action.nftables?.useAdvancedNAT, enableLogging: this.smartProxy.settings.enableDetailedLogging, deleteOnExit: true, tableName: action.nftables?.tableName || 'smartproxy' }; // Add security-related options if (route.security?.ipAllowList?.length) { options.ipAllowList = route.security.ipAllowList; } if (route.security?.ipBlockList?.length) { options.ipBlockList = route.security.ipBlockList; } // Add QoS options if (action.nftables?.maxRate || action.nftables?.priority) { options.qos = { enabled: true, maxRate: action.nftables.maxRate, priority: action.nftables.priority }; } return options; } /** * Expand port range specifications * * @param ports The port range specification * @returns Expanded port range */ expandPortRange(ports) { // Process different port specifications if (typeof ports === 'number') { return ports; } else if (Array.isArray(ports)) { const result = []; for (const item of ports) { if (typeof item === 'number') { result.push(item); } else if ('from' in item && 'to' in item) { result.push({ from: item.from, to: item.to }); } } return result; } else if (typeof ports === 'object' && ports !== null && 'from' in ports && 'to' in ports) { return { from: ports.from, to: ports.to }; } // Fallback to port 80 if something went wrong console.warn('Invalid port range specification, using port 80 as fallback'); return 80; } /** * Get status of all managed rules * * @returns A promise that resolves to a record of NFTables status objects */ async getStatus() { const result = {}; for (const [routeId, proxy] of this.rulesMap.entries()) { result[routeId] = await proxy.getStatus(); } return result; } /** * Check if a route is currently provisioned * * @param route The route configuration * @returns True if the route is provisioned, false otherwise */ isRouteProvisioned(route) { const routeId = this.generateRouteId(route); return this.rulesMap.has(routeId); } /** * Stop all NFTables rules * * @returns A promise that resolves when all rules have been stopped */ async stop() { // Stop all NFTables proxies const stopPromises = Array.from(this.rulesMap.values()).map(proxy => proxy.stop()); await Promise.all(stopPromises); this.rulesMap.clear(); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmZ0YWJsZXMtbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvbmZ0YWJsZXMtbWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQWFwRTs7Ozs7O0dBTUc7QUFDSCxNQUFNLE9BQU8sZUFBZTtJQUcxQjs7OztPQUlHO0lBQ0gsWUFBb0IsVUFBc0I7UUFBdEIsZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQVBsQyxhQUFRLEdBQStCLElBQUksR0FBRyxFQUFFLENBQUM7SUFPWixDQUFDO0lBRTlDOzs7OztPQUtHO0lBQ0ksS0FBSyxDQUFDLGNBQWMsQ0FBQyxLQUFtQjtRQUM3QyxzQ0FBc0M7UUFDdEMsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUU1QyxxQ0FBcUM7UUFDckMsSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDLGdCQUFnQixLQUFLLFVBQVUsRUFBRSxDQUFDO1lBQ2pELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUVELG1EQUFtRDtRQUNuRCxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMscUJBQXFCLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFckQsNkNBQTZDO1FBQzdDLE1BQU0sS0FBSyxHQUFHLElBQUksYUFBYSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBRTVDLElBQUksQ0FBQztZQUNILE1BQU0sS0FBSyxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ3BCLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztZQUNsQyxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1lBQ2IsT0FBTyxDQUFD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