# SmartProxy Unified Forwarding Configuration Plan ## Project Goal Create a clean, use-case driven forwarding configuration interface for SmartProxy that elegantly handles all forwarding scenarios: SNI-based forwarding, termination-based forwarding (NetworkProxy), HTTP forwarding, and ACME challenge forwarding. ## Current State Currently, SmartProxy has several different forwarding mechanisms configured separately: 1. **HTTPS/SNI forwarding** via `IDomainConfig` properties 2. **NetworkProxy forwarding** via `useNetworkProxy` in domain configs 3. **HTTP forwarding** via Port80Handler's `forward` configuration 4. **ACME challenge forwarding** via `acmeForward` configuration This separation creates configuration complexity and reduced cohesion between related settings. ## Proposed Solution: Clean Use-Case Driven Forwarding Interface ### Phase 1: Design Streamlined Forwarding Interface - [ ] Create a use-case driven `IForwardConfig` interface that simplifies configuration: ```typescript export interface IForwardConfig { // Define the primary forwarding type - use-case driven approach type: 'http-only' | 'https-passthrough' | 'https-terminate-to-http' | 'https-terminate-to-https'; // Target configuration target: { host: string | string[]; // Support single host or round-robin port: number; }; // HTTP-specific options http?: { enabled?: boolean; // Defaults to true for http-only, optional for others redirectToHttps?: boolean; // Redirect HTTP to HTTPS headers?: Record; // Custom headers for HTTP responses }; // HTTPS-specific options https?: { customCert?: { // Use custom cert instead of auto-provisioned key: string; cert: string; }; forwardSni?: boolean; // Forward SNI info in passthrough mode }; // ACME certificate handling acme?: { enabled?: boolean; // Enable ACME certificate provisioning maintenance?: boolean; // Auto-renew certificates production?: boolean; // Use production ACME servers forwardChallenges?: { // Forward ACME challenges host: string; port: number; useTls?: boolean; }; }; // Security options security?: { allowedIps?: string[]; // IPs allowed to connect blockedIps?: string[]; // IPs blocked from connecting maxConnections?: number; // Max simultaneous connections }; // Advanced options advanced?: { portRanges?: Array<{ from: number; to: number }>; // Allowed port ranges networkProxyPort?: number; // Custom NetworkProxy port if using terminate mode keepAlive?: boolean; // Enable TCP keepalive timeout?: number; // Connection timeout in ms headers?: Record; // Custom headers with support for variables like {sni} }; } ``` ### Phase 2: Create New Domain Configuration Interface - [ ] Replace existing `IDomainConfig` interface with a new one using the forwarding pattern: ```typescript export interface IDomainConfig { // Core properties domains: string[]; // Domain patterns to match // Unified forwarding configuration forwarding: IForwardConfig; } ``` ### Phase 3: Implement Forwarding Handler System - [ ] Create an implementation strategy focused on the new forwarding types: ```typescript /** * Base class for all forwarding handlers */ abstract class ForwardingHandler { constructor(protected config: IForwardConfig) {} abstract handleConnection(socket: Socket): void; abstract handleHttpRequest(req: IncomingMessage, res: ServerResponse): void; } /** * Factory for creating the appropriate handler based on forwarding type */ class ForwardingHandlerFactory { public static createHandler(config: IForwardConfig): ForwardingHandler { switch (config.type) { case 'http-only': return new HttpForwardingHandler(config); case 'https-passthrough': return new HttpsPassthroughHandler(config); case 'https-terminate-to-http': return new HttpsTerminateToHttpHandler(config); case 'https-terminate-to-https': return new HttpsTerminateToHttpsHandler(config); default: throw new Error(`Unknown forwarding type: ${config.type}`); } } } ``` ## Usage Examples for Common Scenarios ### 1. Basic HTTP Server ```typescript { domains: ['example.com'], forwarding: { type: 'http-only', target: { host: 'localhost', port: 3000 } } } ``` ### 2. HTTPS Termination with HTTP Backend ```typescript { domains: ['secure.example.com'], forwarding: { type: 'https-terminate-to-http', target: { host: 'localhost', port: 3000 }, acme: { production: true // Use production Let's Encrypt } } } ``` ### 3. HTTPS Termination with HTTPS Backend ```typescript { domains: ['secure-backend.example.com'], forwarding: { type: 'https-terminate-to-https', target: { host: 'internal-api', port: 8443 }, http: { redirectToHttps: true // Redirect HTTP requests to HTTPS } } } ``` ### 4. SNI Passthrough ```typescript { domains: ['passthrough.example.com'], forwarding: { type: 'https-passthrough', target: { host: '10.0.0.5', port: 443 } } } ``` ### 5. Mixed HTTP/HTTPS with Custom ACME Forwarding ```typescript { domains: ['mixed.example.com'], forwarding: { type: 'https-terminate-to-http', target: { host: 'localhost', port: 3000 }, http: { redirectToHttps: false // Allow both HTTP and HTTPS access }, acme: { enabled: true, maintenance: true, forwardChallenges: { host: '192.168.1.100', port: 8080 } } } } ``` ### 6. Load-Balanced Backend ```typescript { domains: ['api.example.com'], forwarding: { type: 'https-terminate-to-https', target: { host: ['10.0.0.10', '10.0.0.11', '10.0.0.12'], // Round-robin port: 8443 }, security: { allowedIps: ['10.0.0.*', '192.168.1.*'] // Restrict access } } } ``` ### 7. Advanced Proxy Chain with Custom Headers ```typescript { domains: ['secure-chain.example.com'], forwarding: { type: 'https-terminate-to-https', target: { host: 'backend-gateway.internal', port: 443 }, advanced: { // Pass original client info to backend headers: { 'X-Original-SNI': '{sni}', 'X-Client-IP': '{clientIp}' } } } } ``` ## Implementation Plan ### Task 1: Core Types and Interfaces (Week 1) - [ ] Create the new `IForwardConfig` interface in `classes.pp.interfaces.ts` - [ ] Design the new `IDomainConfig` interface using the forwarding property - [ ] Define the internal data types for expanded configuration ### Task 2: Forwarding Handlers (Week 1-2) - [ ] Create abstract `ForwardingHandler` base class - [ ] Implement concrete handlers for each forwarding type: - [ ] `HttpForwardingHandler` - For HTTP-only configurations - [ ] `HttpsPassthroughHandler` - For SNI passthrough - [ ] `HttpsTerminateToHttpHandler` - For TLS termination to HTTP backends - [ ] `HttpsTerminateToHttpsHandler` - For TLS termination to HTTPS backends - [ ] Implement `ForwardingHandlerFactory` to create the appropriate handler ### Task 3: SmartProxy Integration (Week 2-3) - [ ] Update `SmartProxy` class to use the new forwarding system - [ ] Modify `ConnectionHandler` to delegate to forwarding handlers - [ ] Refactor domain configuration processing to use forwarding types - [ ] Update `Port80Handler` integration to work with the new system ### Task 4: Certificate Management (Week 3) - [ ] Create a certificate management system that works with forwarding types - [ ] Implement automatic ACME provisioning based on forwarding type - [ ] Add custom certificate support ### Task 5: Testing & Helper Functions (Week 4) - [ ] Create helper functions for common forwarding patterns - [ ] Implement comprehensive test suite for each forwarding handler - [ ] Add validation for forwarding configurations ### Task 6: Documentation (Week 4) - [ ] Create detailed documentation for the new forwarding system - [ ] Document the forwarding types and their use cases - [ ] Update README with the new configuration examples ## Detailed Type Documentation ### Core Forwarding Types ```typescript /** * The primary forwarding types supported by SmartProxy */ export type ForwardingType = | 'http-only' // HTTP forwarding only (no HTTPS) | 'https-passthrough' // Pass-through TLS traffic (SNI forwarding) | 'https-terminate-to-http' // Terminate TLS and forward to HTTP backend | 'https-terminate-to-https'; // Terminate TLS and forward to HTTPS backend ``` ### Type-Specific Behavior Each forwarding type has specific default behavior: #### HTTP-Only - Handles only HTTP traffic - No TLS/HTTPS support - No certificate management #### HTTPS Passthrough - Forwards raw TLS traffic to backend (no termination) - Passes SNI information through - No HTTP support (TLS only) - No certificate management #### HTTPS Terminate to HTTP - Terminates TLS at SmartProxy - Connects to backend using HTTP (non-TLS) - Manages certificates automatically (ACME) - Supports HTTP requests with option to redirect to HTTPS #### HTTPS Terminate to HTTPS - Terminates client TLS at SmartProxy - Creates new TLS connection to backend - Manages certificates automatically (ACME) - Supports HTTP requests with option to redirect to HTTPS ## Handler Implementation Strategy ```typescript /** * Handler for HTTP-only forwarding */ class HttpForwardingHandler extends ForwardingHandler { public handleConnection(socket: Socket): void { // Process HTTP connection // For HTTP-only, we'll mostly defer to handleHttpRequest } public handleHttpRequest(req: IncomingMessage, res: ServerResponse): void { // Forward HTTP request to target const target = this.getTargetFromConfig(); this.proxyRequest(req, res, target); } } /** * Handler for HTTPS passthrough (SNI forwarding) */ class HttpsPassthroughHandler extends ForwardingHandler { public handleConnection(socket: Socket): void { // Extract SNI from TLS ClientHello if needed // Forward raw TLS traffic to target without termination const target = this.getTargetFromConfig(); this.forwardTlsConnection(socket, target); } public handleHttpRequest(req: IncomingMessage, res: ServerResponse): void { // HTTP not supported in SNI passthrough mode res.statusCode = 404; res.end('HTTP not supported for this domain'); } } /** * Handler for HTTPS termination with HTTP backend */ class HttpsTerminateToHttpHandler extends ForwardingHandler { private tlsContext: SecureContext; public async initialize(): Promise { // Set up TLS termination context this.tlsContext = await this.createTlsContext(); } public handleConnection(socket: Socket): void { // Terminate TLS const tlsSocket = this.createTlsSocket(socket, this.tlsContext); // Forward to HTTP backend after TLS termination tlsSocket.on('data', (data) => { this.forwardToHttpBackend(data); }); } public handleHttpRequest(req: IncomingMessage, res: ServerResponse): void { if (this.config.http?.redirectToHttps) { // Redirect to HTTPS if configured this.redirectToHttps(req, res); } else { // Handle HTTP request const target = this.getTargetFromConfig(); this.proxyRequest(req, res, target); } } } /** * Handler for HTTPS termination with HTTPS backend */ class HttpsTerminateToHttpsHandler extends ForwardingHandler { private tlsContext: SecureContext; public async initialize(): Promise { // Set up TLS termination context this.tlsContext = await this.createTlsContext(); } public handleConnection(socket: Socket): void { // Terminate client TLS const tlsSocket = this.createTlsSocket(socket, this.tlsContext); // Create new TLS connection to backend tlsSocket.on('data', (data) => { this.forwardToHttpsBackend(data); }); } public handleHttpRequest(req: IncomingMessage, res: ServerResponse): void { if (this.config.http?.redirectToHttps) { // Redirect to HTTPS if configured this.redirectToHttps(req, res); } else { // Handle HTTP request via HTTPS to backend const target = this.getTargetFromConfig(); this.proxyRequestOverHttps(req, res, target); } } } ``` ## Benefits of This Approach 1. **Clean, Type-Driven Design** - Forwarding types clearly express intent - No backward compatibility compromises - Code structure follows the domain model 2. **Explicit Configuration** - Configuration directly maps to behavior - Reduced chance of unexpected behavior 3. **Modular Implementation** - Each forwarding type handled by dedicated class - Clear separation of concerns - Easier to test and extend 4. **Simplified Mental Model** - Users think in terms of use cases, not low-level settings - Configuration matches mental model 5. **Future-Proof** - Easy to add new forwarding types - Clean extension points for new features