13 KiB
13 KiB
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:
- HTTPS/SNI forwarding via
IDomainConfig
properties - NetworkProxy forwarding via
useNetworkProxy
in domain configs - HTTP forwarding via Port80Handler's
forward
configuration - 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:
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<string, string>; // 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<string, string>; // 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:
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:
/**
* 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
{
domains: ['example.com'],
forwarding: {
type: 'http-only',
target: {
host: 'localhost',
port: 3000
}
}
}
2. HTTPS Termination with HTTP Backend
{
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
{
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
{
domains: ['passthrough.example.com'],
forwarding: {
type: 'https-passthrough',
target: {
host: '10.0.0.5',
port: 443
}
}
}
5. Mixed HTTP/HTTPS with Custom ACME Forwarding
{
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
{
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
{
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 inclasses.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 configurationsHttpsPassthroughHandler
- For SNI passthroughHttpsTerminateToHttpHandler
- For TLS termination to HTTP backendsHttpsTerminateToHttpsHandler
- 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
/**
* 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
/**
* 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<void> {
// 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<void> {
// 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
-
Clean, Type-Driven Design
- Forwarding types clearly express intent
- No backward compatibility compromises
- Code structure follows the domain model
-
Explicit Configuration
- Configuration directly maps to behavior
- Reduced chance of unexpected behavior
-
Modular Implementation
- Each forwarding type handled by dedicated class
- Clear separation of concerns
- Easier to test and extend
-
Simplified Mental Model
- Users think in terms of use cases, not low-level settings
- Configuration matches mental model
-
Future-Proof
- Easy to add new forwarding types
- Clean extension points for new features