From 4b381915e1a8d8251167aa824dc99262bb56a0b8 Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Mon, 19 May 2025 17:39:35 +0000 Subject: [PATCH] fix(docs, tests, acme): fix: update changelog, documentation, examples and tests for v19.4.0 release. Adjust global ACME configuration to use ssl@bleu.de and add non-privileged port examples. --- changelog.md | 22 +++ docs/certificate-management.md | 50 +++++-- examples/certificate-management-v19.ts | 119 ++++++++++++++++ examples/complete-example-v19.ts | 188 +++++++++++++++++++++++++ examples/dynamic-port-management.ts | 24 ++-- examples/nftables-integration.ts | 12 +- readme.md | 29 +--- readme.plan.md | 69 +++++---- ts/00_commitinfo_data.ts | 2 +- 9 files changed, 436 insertions(+), 79 deletions(-) create mode 100644 examples/certificate-management-v19.ts create mode 100644 examples/complete-example-v19.ts diff --git a/changelog.md b/changelog.md index b89df01..d2626f1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,27 @@ # Changelog +## 2025-05-19 - 19.3.4 - fix(docs, tests, acme) +fix: update changelog, documentation, examples and tests for v19.4.0 release. Adjust global ACME configuration to use ssl@bleu.de and add non-privileged port examples. + +- Updated changelog with new v19.4.0 entry detailing fixes in tests and docs +- Revised README and certificate-management.md to demonstrate global ACME settings (using ssl@bleu.de, non-privileged port support, auto-renewal configuration, and renewCheckIntervalHours) +- Added new examples (certificate-management-v19.ts and complete-example-v19.ts) and updated existing examples (dynamic port management, NFTables integration) to reflect v19.4.0 features +- Fixed test exports and port mapping issues in several test files (acme-state-manager, port80-management, race-conditions, etc.) +- Updated readme.plan.md to reflect completed refactoring and breaking changes from v19.3.3 + +## 2025-05-19 - 19.4.0 - fix(tests) & docs +Fix failing tests and update documentation for v19+ features + +- Fix ForwardingHandlerFactory.applyDefaults to set port and socket properties correctly +- Fix route finding logic in forwarding tests to properly identify redirect routes +- Fix test exports in acme-state-manager.node.ts, port80-management.node.ts, and race-conditions.node.ts +- Update ACME email configuration to use ssl@bleu.de instead of test domains +- Update README with v19.4.0 features including global ACME configuration +- Update certificate-management.md documentation to reflect v19+ changes +- Add new examples: certificate-management-v19.ts and complete-example-v19.ts +- Update existing examples to demonstrate global ACME configuration +- Update readme.plan.md to reflect completed refactoring + ## 2025-05-19 - 19.3.3 - fix(core) No changes detected – project structure and documentation remain unchanged. diff --git a/docs/certificate-management.md b/docs/certificate-management.md index 4f5ac62..9f1320c 100644 --- a/docs/certificate-management.md +++ b/docs/certificate-management.md @@ -208,11 +208,18 @@ const smartproxy = new SmartProxy({ // Certificate provisioning was automatic or via certProvisionFunction ``` -### After (v18+) +### After (v19+) ```typescript -// New approach with route-based configuration +// New approach with global ACME configuration const smartproxy = new SmartProxy({ + // Global ACME defaults (v19+) + acme: { + email: 'ssl@bleu.de', + useProduction: true, + port: 80 // Or 8080 for non-privileged + }, + routes: [{ match: { ports: 443, domains: 'example.com' }, action: { @@ -220,11 +227,7 @@ const smartproxy = new SmartProxy({ target: { host: 'localhost', port: 8080 }, tls: { mode: 'terminate', - certificate: 'auto', - acme: { - email: 'admin@example.com', - useProduction: true - } + certificate: 'auto' // Uses global ACME settings } } }] @@ -235,9 +238,38 @@ const smartproxy = new SmartProxy({ ### Common Issues -1. **Certificate not provisioning**: Ensure port 80 is accessible for ACME challenges -2. **ACME rate limits**: Use staging environment for testing +1. **Certificate not provisioning**: Ensure the ACME challenge port (80 or configured port) is accessible +2. **ACME rate limits**: Use staging environment for testing (`useProduction: false`) 3. **Permission errors**: Ensure the certificate directory is writable +4. **Invalid email domain**: ACME servers may reject certain email domains (e.g., example.com). Use a real email domain +5. **Port binding errors**: Use higher ports (e.g., 8080) if running without root privileges + +### Using Non-Privileged Ports + +For development or non-root environments: + +```typescript +const proxy = new SmartProxy({ + acme: { + email: 'ssl@bleu.de', + port: 8080, // Use 8080 instead of 80 + useProduction: false + }, + routes: [ + { + match: { ports: 8443, domains: 'example.com' }, + action: { + type: 'forward', + target: { host: 'localhost', port: 3000 }, + tls: { + mode: 'terminate', + certificate: 'auto' + } + } + } + ] +}); +``` ### Debug Mode diff --git a/examples/certificate-management-v19.ts b/examples/certificate-management-v19.ts new file mode 100644 index 0000000..7c12faa --- /dev/null +++ b/examples/certificate-management-v19.ts @@ -0,0 +1,119 @@ +/** + * Certificate Management Example (v19+) + * + * This example demonstrates the new global ACME configuration introduced in v19+ + * along with route-level overrides for specific domains. + */ + +import { + SmartProxy, + createHttpRoute, + createHttpsTerminateRoute, + createCompleteHttpsServer +} from '../dist_ts/index.js'; + +async function main() { + // Create a SmartProxy instance with global ACME configuration + const proxy = new SmartProxy({ + // Global ACME configuration (v19+) + // These settings apply to all routes with certificate: 'auto' + acme: { + email: 'ssl@bleu.de', // Global contact email + useProduction: false, // Use staging by default + port: 8080, // Use non-privileged port + renewThresholdDays: 30, // Renew 30 days before expiry + autoRenew: true, // Enable automatic renewal + renewCheckIntervalHours: 12 // Check twice daily + }, + + routes: [ + // Route that uses global ACME settings + createHttpsTerminateRoute('app.example.com', + { host: 'localhost', port: 3000 }, + { certificate: 'auto' } // Uses global ACME configuration + ), + + // Route with route-level ACME override + { + name: 'production-api', + match: { ports: 443, domains: 'api.example.com' }, + action: { + type: 'forward', + target: { host: 'localhost', port: 3001 }, + tls: { + mode: 'terminate', + certificate: 'auto', + acme: { + email: 'api-certs@example.com', // Override email + useProduction: true, // Use production for API + renewThresholdDays: 60 // Earlier renewal for critical API + } + } + } + }, + + // Complete HTTPS server with automatic redirects + ...createCompleteHttpsServer('website.example.com', + { host: 'localhost', port: 8080 }, + { certificate: 'auto' } + ), + + // Static certificate (not using ACME) + { + name: 'internal-service', + match: { ports: 8443, domains: 'internal.local' }, + action: { + type: 'forward', + target: { host: 'localhost', port: 3002 }, + tls: { + mode: 'terminate', + certificate: { + cert: '-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----', + key: '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----' + } + } + } + } + ] + }); + + // Monitor certificate events + proxy.on('certificate:issued', (event) => { + console.log(`Certificate issued for ${event.domain}`); + console.log(`Expires: ${event.expiryDate}`); + }); + + proxy.on('certificate:renewed', (event) => { + console.log(`Certificate renewed for ${event.domain}`); + }); + + proxy.on('certificate:error', (event) => { + console.error(`Certificate error for ${event.domain}: ${event.error}`); + }); + + // Start the proxy + await proxy.start(); + console.log('SmartProxy started with global ACME configuration'); + + // Check certificate status programmatically + setTimeout(async () => { + // Get status for a specific route + const status = proxy.getCertificateStatus('app-route'); + console.log('Certificate status:', status); + + // Manually trigger renewal if needed + if (status && status.status === 'expiring') { + await proxy.renewCertificate('app-route'); + } + }, 10000); + + // Handle shutdown gracefully + process.on('SIGINT', async () => { + console.log('Shutting down proxy...'); + await proxy.stop(); + process.exit(0); + }); +} + +// Run the example +main().catch(console.error); \ No newline at end of file diff --git a/examples/complete-example-v19.ts b/examples/complete-example-v19.ts new file mode 100644 index 0000000..0fc2275 --- /dev/null +++ b/examples/complete-example-v19.ts @@ -0,0 +1,188 @@ +/** + * Complete SmartProxy Example (v19+) + * + * This comprehensive example demonstrates all major features of SmartProxy v19+: + * - Global ACME configuration + * - Route-based configuration + * - Helper functions for common patterns + * - Dynamic route management + * - Certificate status monitoring + * - Error handling and recovery + */ + +import { + SmartProxy, + createHttpRoute, + createHttpsTerminateRoute, + createHttpsPassthroughRoute, + createHttpToHttpsRedirect, + createCompleteHttpsServer, + createLoadBalancerRoute, + createApiRoute, + createWebSocketRoute, + createStaticFileRoute, + createNfTablesRoute +} from '../dist_ts/index.js'; + +async function main() { + // Create SmartProxy with comprehensive configuration + const proxy = new SmartProxy({ + // Global ACME configuration (v19+) + acme: { + email: 'ssl@bleu.de', + useProduction: false, // Use staging for this example + port: 8080, // Non-privileged port for development + autoRenew: true, + renewCheckIntervalHours: 12 + }, + + // Initial routes + routes: [ + // Basic HTTP service + createHttpRoute('api.example.com', { host: 'localhost', port: 3000 }), + + // HTTPS with automatic certificates + createHttpsTerminateRoute('secure.example.com', + { host: 'localhost', port: 3001 }, + { certificate: 'auto' } + ), + + // Complete HTTPS server with HTTP->HTTPS redirect + ...createCompleteHttpsServer('www.example.com', + { host: 'localhost', port: 8080 }, + { certificate: 'auto' } + ), + + // Load balancer with multiple backends + createLoadBalancerRoute( + 'app.example.com', + ['10.0.0.1', '10.0.0.2', '10.0.0.3'], + 8080, + { + tls: { + mode: 'terminate', + certificate: 'auto' + } + } + ), + + // API route with CORS + createApiRoute('api.example.com', '/v1', + { host: 'api-backend', port: 8081 }, + { + useTls: true, + certificate: 'auto', + addCorsHeaders: true + } + ), + + // WebSocket support + createWebSocketRoute('ws.example.com', '/socket', + { host: 'websocket-server', port: 8082 }, + { + useTls: true, + certificate: 'auto' + } + ), + + // Static file server + createStaticFileRoute(['cdn.example.com', 'static.example.com'], + '/var/www/static', + { + serveOnHttps: true, + certificate: 'auto' + } + ), + + // HTTPS passthrough for services that handle their own TLS + createHttpsPassthroughRoute('legacy.example.com', + { host: '192.168.1.100', port: 443 } + ), + + // HTTP to HTTPS redirects + createHttpToHttpsRedirect(['*.example.com', 'example.com']) + ], + + // Enable detailed logging for debugging + enableDetailedLogging: true + }); + + // Event handlers + proxy.on('connection', (event) => { + console.log(`New connection: ${event.source} -> ${event.destination}`); + }); + + proxy.on('certificate:issued', (event) => { + console.log(`Certificate issued for ${event.domain}`); + }); + + proxy.on('certificate:renewed', (event) => { + console.log(`Certificate renewed for ${event.domain}`); + }); + + proxy.on('error', (error) => { + console.error('Proxy error:', error); + }); + + // Start the proxy + await proxy.start(); + console.log('SmartProxy started successfully'); + console.log('Listening on ports:', proxy.getListeningPorts()); + + // Demonstrate dynamic route management + setTimeout(async () => { + console.log('Adding new route dynamically...'); + + // Get current routes and add a new one + const currentRoutes = proxy.settings.routes; + const newRoutes = [ + ...currentRoutes, + createHttpsTerminateRoute('new-service.example.com', + { host: 'localhost', port: 3003 }, + { certificate: 'auto' } + ) + ]; + + // Update routes + await proxy.updateRoutes(newRoutes); + console.log('New route added successfully'); + }, 5000); + + // Check certificate status periodically + setInterval(async () => { + const routes = proxy.settings.routes; + for (const route of routes) { + if (route.action.tls?.certificate === 'auto') { + const status = proxy.getCertificateStatus(route.name); + if (status) { + console.log(`Certificate status for ${route.name}:`, status); + + // Renew if expiring soon + if (status.status === 'expiring') { + console.log(`Renewing certificate for ${route.name}...`); + await proxy.renewCertificate(route.name); + } + } + } + } + }, 3600000); // Check every hour + + // Graceful shutdown + process.on('SIGINT', async () => { + console.log('Shutting down gracefully...'); + await proxy.stop(); + process.exit(0); + }); + + process.on('SIGTERM', async () => { + console.log('Received SIGTERM, shutting down...'); + await proxy.stop(); + process.exit(0); + }); +} + +// Run the example +main().catch((error) => { + console.error('Failed to start proxy:', error); + process.exit(1); +}); \ No newline at end of file diff --git a/examples/dynamic-port-management.ts b/examples/dynamic-port-management.ts index af96bb2..a917d5b 100644 --- a/examples/dynamic-port-management.ts +++ b/examples/dynamic-port-management.ts @@ -3,27 +3,25 @@ * * This example demonstrates how to dynamically add and remove ports * while SmartProxy is running, without requiring a restart. + * Also shows the new v19+ global ACME configuration. */ -import { SmartProxy } from '../dist_ts/index.js'; +import { SmartProxy, createHttpRoute, createHttpsTerminateRoute } from '../dist_ts/index.js'; import type { IRouteConfig } from '../dist_ts/index.js'; async function main() { - // Create a SmartProxy instance with initial routes + // Create a SmartProxy instance with initial routes and global ACME config const proxy = new SmartProxy({ + // Global ACME configuration (v19+) + acme: { + email: 'ssl@bleu.de', + useProduction: false, + port: 8080 // Using non-privileged port for ACME challenges + }, + routes: [ // Initial route on port 8080 - { - match: { - ports: 8080, - domains: ['example.com', '*.example.com'] - }, - action: { - type: 'forward', - target: { host: 'localhost', port: 3000 } - }, - name: 'Initial HTTP Route' - } + createHttpRoute(['example.com', '*.example.com'], { host: 'localhost', port: 3000 }) ] }); diff --git a/examples/nftables-integration.ts b/examples/nftables-integration.ts index 90af43b..4bf0144 100644 --- a/examples/nftables-integration.ts +++ b/examples/nftables-integration.ts @@ -5,6 +5,7 @@ * for high-performance network routing that operates at the kernel level. * * NOTE: This requires elevated privileges to run (sudo) as it interacts with nftables. + * Also shows the new v19+ global ACME configuration. */ import { SmartProxy } from '../ts/proxies/smart-proxy/index.js'; @@ -50,15 +51,22 @@ async function simpleForwardingExample() { async function httpsTerminationExample() { console.log('Starting HTTPS termination with NFTables example...'); - // Create a SmartProxy instance with an HTTPS termination route using NFTables + // Create a SmartProxy instance with global ACME and NFTables HTTPS termination const proxy = new SmartProxy({ + // Global ACME configuration (v19+) + acme: { + email: 'ssl@bleu.de', + useProduction: false, + port: 80 // NFTables needs root, so we can use port 80 + }, + routes: [ createNfTablesTerminateRoute('secure.example.com', { host: 'localhost', port: 8443 }, { ports: 443, - certificate: 'auto', // Automatic certificate provisioning + certificate: 'auto', // Uses global ACME configuration tableName: 'smartproxy_https' }) ], diff --git a/readme.md b/readme.md index 53d9bba..26fcee3 100644 --- a/readme.md +++ b/readme.md @@ -113,7 +113,7 @@ npm install @push.rocks/smartproxy ## Quick Start with SmartProxy -SmartProxy v18.0.0 continues the evolution of the unified route-based configuration system making your proxy setup more flexible and intuitive with improved helper functions and NFTables integration for high-performance kernel-level routing. +SmartProxy v19.4.0 provides a unified route-based configuration system with enhanced certificate management, NFTables integration for high-performance kernel-level routing, and improved helper functions for common proxy setups. ```typescript import { @@ -136,10 +136,12 @@ import { const proxy = new SmartProxy({ // Global ACME settings for all routes with certificate: 'auto' acme: { - email: 'ssl@example.com', // Required for Let's Encrypt + email: 'ssl@bleu.de', // Required for Let's Encrypt useProduction: false, // Use staging by default renewThresholdDays: 30, // Renew 30 days before expiry - port: 80 // Port for HTTP-01 challenges + port: 80, // Port for HTTP-01 challenges (use 8080 for non-privileged) + autoRenew: true, // Enable automatic renewal + renewCheckIntervalHours: 24 // Check for renewals daily }, // Define all your routing rules in a single array @@ -216,26 +218,7 @@ const proxy = new SmartProxy({ certificate: 'auto', maxRate: '100mbps' }) - ], - - // Global settings that apply to all routes - defaults: { - security: { - maxConnections: 500 - } - }, - - // Automatic Let's Encrypt integration - acme: { - enabled: true, - contactEmail: 'admin@example.com', - useProduction: true - } -}); - -// Listen for certificate events -proxy.on('certificate', evt => { - console.log(`Certificate for ${evt.domain} ready, expires: ${evt.expiryDate}`); + ] }); // Start the proxy diff --git a/readme.plan.md b/readme.plan.md index 100f342..1c4dd8d 100644 --- a/readme.plan.md +++ b/readme.plan.md @@ -1,22 +1,15 @@ -# SmartProxy Architecture Refactoring Plan +# SmartProxy v19.4.0 - Completed Refactoring ## Overview -Refactor the proxy architecture to provide clearer separation of concerns between HTTP/HTTPS traffic handling and low-level connection routing. +SmartProxy has been successfully refactored with clearer separation of concerns between HTTP/HTTPS traffic handling and low-level connection routing. Version 19.4.0 introduces global ACME configuration and enhanced route management. -## Current Architecture Problems +## Current Architecture (v19.4.0) -1. NetworkProxy name doesn't clearly indicate it handles HTTP/HTTPS -2. HTTP parsing logic is duplicated in RouteConnectionHandler -3. Redirect and static route handling is embedded in SmartProxy -4. Unclear separation between TCP routing and HTTP processing - -## Proposed Architecture - -### HttpProxy (renamed from NetworkProxy) +### HttpProxy (formerly NetworkProxy) **Purpose**: Handle all HTTP/HTTPS traffic with TLS termination -**Responsibilities**: +**Current Responsibilities**: - TLS termination for HTTPS - HTTP/1.1 and HTTP/2 protocol handling - HTTP request/response parsing @@ -25,29 +18,33 @@ Refactor the proxy architecture to provide clearer separation of concerns betwee - Static route handlers - WebSocket protocol upgrades - Connection pooling for backend servers -- Certificate management (ACME and static) +- Certificate management integration ### SmartProxy -**Purpose**: Low-level connection router and port manager +**Purpose**: Central API for all proxy needs with route-based configuration -**Responsibilities**: +**Current Responsibilities**: - Port management (listen on multiple ports) - Route-based connection routing - TLS passthrough (SNI-based routing) - NFTables integration -- Delegate HTTP/HTTPS connections to HttpProxy +- Certificate management via SmartCertManager - Raw TCP proxying - Connection lifecycle management +- Global ACME configuration (v19+) -## Implementation Plan +## Completed Implementation -### Phase 1: Rename and Reorganize NetworkProxy ✅ +### Phase 1: Rename and Reorganize ✅ +- NetworkProxy renamed to HttpProxy +- Directory structure reorganized +- All imports and references updated -1. **Rename NetworkProxy to HttpProxy** - - Renamed directory from `network-proxy` to `http-proxy` - - Updated all imports and references - -2. **Update class and file names** +### Phase 2: Certificate Management ✅ +- Unified certificate management in SmartCertManager +- Global ACME configuration support (v19+) +- Route-level certificate overrides +- Automatic renewal system - Renamed `network-proxy.ts` to `http-proxy.ts` - Updated `NetworkProxy` class to `HttpProxy` class - Updated all type definitions and interfaces @@ -157,16 +154,26 @@ After this refactoring, we can more easily add: 4. Protocol-specific optimizations 5. Better HTTP/2 multiplexing -## Breaking Changes +## Breaking Changes from v18 to v19 1. `NetworkProxy` class renamed to `HttpProxy` 2. Import paths change from `network-proxy` to `http-proxy` -3. Some type names may change for consistency +3. Global ACME configuration now available at the top level +4. Certificate management unified under SmartCertManager -## Rollback Plan +## Future Enhancements -If issues arise: -1. Git revert to previous commit -2. Re-deploy previous version -3. Document lessons learned -4. Plan incremental changes \ No newline at end of file +1. HTTP/3 (QUIC) support in HttpProxy +2. Advanced HTTP features (compression, caching) +3. HTTP middleware system +4. Protocol-specific optimizations +5. Better HTTP/2 multiplexing +6. Enhanced monitoring and metrics + +## Key Features in v19.4.0 + +1. **Global ACME Configuration**: Default settings for all routes with `certificate: 'auto'` +2. **Enhanced Route Management**: Better separation between routing and certificate management +3. **Improved Test Coverage**: Fixed test exports and port bindings +4. **Better Error Messages**: Clear guidance for ACME configuration issues +5. **Non-Privileged Port Support**: Examples for development environments \ No newline at end of file diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 53b9b60..cc137e5 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: '19.3.3', + version: '19.3.4', description: '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.' }