import * as plugins from './plugins.js'; import { NetworkProxy } from './classes.networkproxy.js'; import type { IConnectionRecord, IPortProxySettings, IDomainConfig } from './classes.pp.interfaces.js'; /** * Manages NetworkProxy integration for TLS termination */ export class NetworkProxyBridge { private networkProxy: NetworkProxy | null = null; constructor(private settings: IPortProxySettings) {} /** * Initialize NetworkProxy instance */ public async initialize(): Promise { if (!this.networkProxy && this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0) { // Configure NetworkProxy options based on PortProxy settings const networkProxyOptions: any = { port: this.settings.networkProxyPort!, portProxyIntegration: true, logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info', }; // Add ACME settings if configured if (this.settings.acme) { networkProxyOptions.acme = { ...this.settings.acme }; } this.networkProxy = new NetworkProxy(networkProxyOptions); console.log(`Initialized NetworkProxy on port ${this.settings.networkProxyPort}`); // Convert and apply domain configurations to NetworkProxy await this.syncDomainConfigsToNetworkProxy(); } } /** * Get the NetworkProxy instance */ public getNetworkProxy(): NetworkProxy | null { return this.networkProxy; } /** * Get the NetworkProxy port */ public getNetworkProxyPort(): number { return this.networkProxy ? this.networkProxy.getListeningPort() : this.settings.networkProxyPort || 8443; } /** * Start NetworkProxy */ public async start(): Promise { if (this.networkProxy) { await this.networkProxy.start(); console.log(`NetworkProxy started on port ${this.settings.networkProxyPort}`); // Log ACME status if (this.settings.acme?.enabled) { console.log( `ACME certificate management is enabled (${ this.settings.acme.useProduction ? 'Production' : 'Staging' } mode)` ); console.log(`ACME HTTP challenge server on port ${this.settings.acme.port}`); // Register domains for ACME certificates if enabled if (this.networkProxy.options.acme?.enabled) { console.log('Registering domains with ACME certificate manager...'); // The NetworkProxy will handle this internally via registerDomainsWithAcmeManager() } } } } /** * Stop NetworkProxy */ public async stop(): Promise { if (this.networkProxy) { try { console.log('Stopping NetworkProxy...'); await this.networkProxy.stop(); console.log('NetworkProxy stopped successfully'); // Log ACME shutdown if it was enabled if (this.settings.acme?.enabled) { console.log('ACME certificate manager stopped'); } } catch (err) { console.log(`Error stopping NetworkProxy: ${err}`); } } } /** * Forwards a TLS connection to a NetworkProxy for handling */ public forwardToNetworkProxy( connectionId: string, socket: plugins.net.Socket, record: IConnectionRecord, initialData: Buffer, customProxyPort?: number, onError?: (reason: string) => void ): void { // Ensure NetworkProxy is initialized if (!this.networkProxy) { console.log( `[${connectionId}] NetworkProxy not initialized. Cannot forward connection.` ); if (onError) { onError('network_proxy_not_initialized'); } return; } // Use the custom port if provided, otherwise use the default NetworkProxy port const proxyPort = customProxyPort || this.networkProxy.getListeningPort(); const proxyHost = 'localhost'; // Assuming NetworkProxy runs locally if (this.settings.enableDetailedLogging) { console.log( `[${connectionId}] Forwarding TLS connection to NetworkProxy at ${proxyHost}:${proxyPort}` ); } // Create a connection to the NetworkProxy const proxySocket = plugins.net.connect({ host: proxyHost, port: proxyPort, }); // Store the outgoing socket in the record record.outgoing = proxySocket; record.outgoingStartTime = Date.now(); record.usingNetworkProxy = true; // Set up error handlers proxySocket.on('error', (err) => { console.log(`[${connectionId}] Error connecting to NetworkProxy: ${err.message}`); if (onError) { onError('network_proxy_connect_error'); } }); // Handle connection to NetworkProxy proxySocket.on('connect', () => { if (this.settings.enableDetailedLogging) { console.log(`[${connectionId}] Connected to NetworkProxy at ${proxyHost}:${proxyPort}`); } // First send the initial data that contains the TLS ClientHello proxySocket.write(initialData); // Now set up bidirectional piping between client and NetworkProxy socket.pipe(proxySocket); proxySocket.pipe(socket); // Update activity on data transfer (caller should handle this) if (this.settings.enableDetailedLogging) { console.log(`[${connectionId}] TLS connection successfully forwarded to NetworkProxy`); } }); } /** * Synchronizes domain configurations to NetworkProxy */ public async syncDomainConfigsToNetworkProxy(): Promise { if (!this.networkProxy) { console.log('Cannot sync configurations - NetworkProxy not initialized'); return; } try { // Get SSL certificates from assets // Import fs directly since it's not in plugins const fs = await import('fs'); let certPair; try { certPair = { key: fs.readFileSync('assets/certs/key.pem', 'utf8'), cert: fs.readFileSync('assets/certs/cert.pem', 'utf8'), }; } catch (certError) { console.log(`Warning: Could not read default certificates: ${certError}`); console.log( 'Using empty certificate placeholders - ACME will generate proper certificates if enabled' ); // Use empty placeholders - NetworkProxy will use its internal defaults // or ACME will generate proper ones if enabled certPair = { key: '', cert: '', }; } // Convert domain configs to NetworkProxy configs const proxyConfigs = this.networkProxy.convertPortProxyConfigs( this.settings.domainConfigs, certPair ); // Log ACME-eligible domains if ACME is enabled if (this.settings.acme?.enabled) { const acmeEligibleDomains = proxyConfigs .filter((config) => !config.hostName.includes('*')) // Exclude wildcards .map((config) => config.hostName); if (acmeEligibleDomains.length > 0) { console.log(`Domains eligible for ACME certificates: ${acmeEligibleDomains.join(', ')}`); } else { console.log('No domains eligible for ACME certificates found in configuration'); } } // Update NetworkProxy with the converted configs await this.networkProxy.updateProxyConfigs(proxyConfigs); console.log(`Successfully synchronized ${proxyConfigs.length} domain configurations to NetworkProxy`); } catch (err) { console.log(`Failed to sync configurations: ${err}`); } } /** * Request a certificate for a specific domain */ public async requestCertificate(domain: string): Promise { if (!this.networkProxy) { console.log('Cannot request certificate - NetworkProxy not initialized'); return false; } if (!this.settings.acme?.enabled) { console.log('Cannot request certificate - ACME is not enabled'); return false; } try { const result = await this.networkProxy.requestCertificate(domain); if (result) { console.log(`Certificate request for ${domain} submitted successfully`); } else { console.log(`Certificate request for ${domain} failed`); } return result; } catch (err) { console.log(`Error requesting certificate: ${err}`); return false; } } }