@push.rocks/smartproxy 19.6.16 → 20.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist_ts/core/utils/shared-security-manager.js +30 -5
  2. package/dist_ts/proxies/http-proxy/request-handler.d.ts +4 -0
  3. package/dist_ts/proxies/http-proxy/request-handler.js +104 -21
  4. package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +4 -0
  5. package/dist_ts/proxies/http-proxy/websocket-handler.js +78 -8
  6. package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +17 -1
  7. package/dist_ts/proxies/smart-proxy/certificate-manager.js +84 -10
  8. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -0
  9. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +19 -2
  10. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  11. package/dist_ts/proxies/smart-proxy/nftables-manager.js +14 -11
  12. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +4 -0
  13. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +112 -28
  14. package/dist_ts/proxies/smart-proxy/smart-proxy.js +9 -1
  15. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +23 -23
  16. package/dist_ts/proxies/smart-proxy/utils/route-patterns.js +13 -13
  17. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +4 -7
  18. package/dist_ts/proxies/smart-proxy/utils/route-validators.js +41 -25
  19. package/package.json +1 -1
  20. package/readme.hints.md +51 -1
  21. package/readme.md +105 -2
  22. package/readme.plan.md +154 -53
  23. package/ts/core/utils/shared-security-manager.ts +33 -4
  24. package/ts/proxies/http-proxy/request-handler.ts +124 -21
  25. package/ts/proxies/http-proxy/websocket-handler.ts +96 -8
  26. package/ts/proxies/smart-proxy/certificate-manager.ts +98 -13
  27. package/ts/proxies/smart-proxy/models/interfaces.ts +6 -0
  28. package/ts/proxies/smart-proxy/models/route-types.ts +34 -8
  29. package/ts/proxies/smart-proxy/nftables-manager.ts +14 -10
  30. package/ts/proxies/smart-proxy/route-connection-handler.ts +132 -28
  31. package/ts/proxies/smart-proxy/smart-proxy.ts +10 -0
  32. package/ts/proxies/smart-proxy/utils/route-helpers.ts +14 -14
  33. package/ts/proxies/smart-proxy/utils/route-patterns.ts +6 -6
  34. package/ts/proxies/smart-proxy/utils/route-utils.ts +3 -6
  35. package/ts/proxies/smart-proxy/utils/route-validators.ts +38 -21
@@ -94,32 +94,45 @@ export function validateRouteAction(action) {
94
94
  else if (!['forward', 'socket-handler'].includes(action.type)) {
95
95
  errors.push(`Invalid action type: ${action.type}`);
96
96
  }
97
- // Validate target for 'forward' action
97
+ // Validate targets for 'forward' action
98
98
  if (action.type === 'forward') {
99
- if (!action.target) {
100
- errors.push('Target is required for forward action');
99
+ if (!action.targets || !Array.isArray(action.targets) || action.targets.length === 0) {
100
+ errors.push('Targets array is required for forward action');
101
101
  }
102
102
  else {
103
- // Validate target host
104
- if (!action.target.host) {
105
- errors.push('Target host is required');
106
- }
107
- else if (typeof action.target.host !== 'string' &&
108
- !Array.isArray(action.target.host) &&
109
- typeof action.target.host !== 'function') {
110
- errors.push('Target host must be a string, array of strings, or function');
111
- }
112
- // Validate target port
113
- if (action.target.port === undefined) {
114
- errors.push('Target port is required');
115
- }
116
- else if (typeof action.target.port !== 'number' &&
117
- typeof action.target.port !== 'function') {
118
- errors.push('Target port must be a number or a function');
119
- }
120
- else if (typeof action.target.port === 'number' && !isValidPort(action.target.port)) {
121
- errors.push('Target port must be between 1 and 65535');
122
- }
103
+ // Validate each target
104
+ action.targets.forEach((target, index) => {
105
+ // Validate target host
106
+ if (!target.host) {
107
+ errors.push(`Target[${index}] host is required`);
108
+ }
109
+ else if (typeof target.host !== 'string' &&
110
+ !Array.isArray(target.host) &&
111
+ typeof target.host !== 'function') {
112
+ errors.push(`Target[${index}] host must be a string, array of strings, or function`);
113
+ }
114
+ // Validate target port
115
+ if (target.port === undefined) {
116
+ errors.push(`Target[${index}] port is required`);
117
+ }
118
+ else if (typeof target.port !== 'number' &&
119
+ typeof target.port !== 'function' &&
120
+ target.port !== 'preserve') {
121
+ errors.push(`Target[${index}] port must be a number, 'preserve', or a function`);
122
+ }
123
+ else if (typeof target.port === 'number' && !isValidPort(target.port)) {
124
+ errors.push(`Target[${index}] port must be between 1 and 65535`);
125
+ }
126
+ // Validate match criteria if present
127
+ if (target.match) {
128
+ if (target.match.ports && !Array.isArray(target.match.ports)) {
129
+ errors.push(`Target[${index}] match.ports must be an array`);
130
+ }
131
+ if (target.match.method && !Array.isArray(target.match.method)) {
132
+ errors.push(`Target[${index}] match.method must be an array`);
133
+ }
134
+ }
135
+ });
123
136
  }
124
137
  // Validate TLS options for forward actions
125
138
  if (action.tls) {
@@ -219,7 +232,10 @@ export function hasRequiredPropertiesForAction(route, actionType) {
219
232
  }
220
233
  switch (actionType) {
221
234
  case 'forward':
222
- return !!route.action.target && !!route.action.target.host && !!route.action.target.port;
235
+ return !!route.action.targets &&
236
+ Array.isArray(route.action.targets) &&
237
+ route.action.targets.length > 0 &&
238
+ route.action.targets.every(t => t.host && t.port !== undefined);
223
239
  case 'socket-handler':
224
240
  return !!route.action.socketHandler && typeof route.action.socketHandler === 'function';
225
241
  default:
@@ -240,4 +256,4 @@ export function assertValidRoute(route) {
240
256
  }
241
257
  return route;
242
258
  }
243
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtdmFsaWRhdG9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvdXRpbHMvcm91dGUtdmFsaWRhdG9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7R0FLRztBQUlIOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsV0FBVyxDQUFDLElBQVM7SUFDbkMsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUM3QixPQUFPLElBQUksR0FBRyxDQUFDLElBQUksSUFBSSxHQUFHLEtBQUssQ0FBQyxDQUFDLDhCQUE4QjtJQUNqRSxDQUFDO1NBQU0sSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDL0IsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQ3BCLENBQUMsT0FBTyxDQUFDLEtBQUssUUFBUSxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLEtBQUssQ0FBQztZQUM3QyxDQUFDLE9BQU8sQ0FBQyxLQUFLLFFBQVEsSUFBSSxNQUFNLElBQUksQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDO2dCQUNqRCxDQUFDLENBQUMsSUFBSSxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxHQUFHLEtBQUssSUFBSSxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsRUFBRSxHQUFHLEtBQUssQ0FBQyxDQUMzRCxDQUFDO0lBQ0osQ0FBQztTQUFNLElBQUksT0FBTyxJQUFJLEtBQUssVUFBVSxFQUFFLENBQUM7UUFDdEMsd0VBQXdFO1FBQ3hFLHdDQUF3QztRQUN4QyxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7U0FBTSxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsSUFBSSxNQUFNLElBQUksSUFBSSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUN0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
259
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtdmFsaWRhdG9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvdXRpbHMvcm91dGUtdmFsaWRhdG9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7R0FLRztBQUlIOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsV0FBVyxDQUFDLElBQVM7SUFDbkMsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUM3QixPQUFPLElBQUksR0FBRyxDQUFDLElBQUksSUFBSSxHQUFHLEtBQUssQ0FBQyxDQUFDLDhCQUE4QjtJQUNqRSxDQUFDO1NBQU0sSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDL0IsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQ3BCLENBQUMsT0FBTyxDQUFDLEtBQUssUUFBUSxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLEtBQUssQ0FBQztZQUM3QyxDQUFDLE9BQU8sQ0FBQyxLQUFLLFFBQVEsSUFBSSxNQUFNLElBQUksQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDO2dCQUNqRCxDQUFDLENBQUMsSUFBSSxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxHQUFHLEtBQUssSUFBSSxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsRUFBRSxHQUFHLEtBQUssQ0FBQyxDQUMzRCxDQUFDO0lBQ0osQ0FBQztTQUFNLElBQUksT0FBTyxJQUFJLEtBQUssVUFBVSxFQUFFLENBQUM7UUFDdEMsd0VBQXdFO1FBQ3hFLHdDQUF3QztRQUN4QyxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7U0FBTSxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsSUFBSSxNQUFNLElBQUksSUFBSSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUN0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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartproxy",
3
- "version": "19.6.16",
3
+ "version": "20.0.0",
4
4
  "private": false,
5
5
  "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.",
6
6
  "main": "dist_ts/index.js",
package/readme.hints.md CHANGED
@@ -295,4 +295,54 @@ You'll see:
295
295
  - During attacks or high-volume scenarios, logs are flushed more frequently
296
296
  - If 50+ events occur within 1 second, immediate flush is triggered
297
297
  - Prevents memory buildup during flooding attacks
298
- - Maintains real-time visibility during incidents
298
+ - Maintains real-time visibility during incidents
299
+
300
+ ## Custom Certificate Provision Function
301
+
302
+ The `certProvisionFunction` feature has been implemented to allow users to provide their own certificate generation logic.
303
+
304
+ ### Implementation Details
305
+
306
+ 1. **Type Definition**: The function must return `Promise<TSmartProxyCertProvisionObject>` where:
307
+ - `TSmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01'`
308
+ - Return `'http01'` to fallback to Let's Encrypt
309
+ - Return a certificate object for custom certificates
310
+
311
+ 2. **Certificate Manager Changes**:
312
+ - Added `certProvisionFunction` property to CertificateManager
313
+ - Modified `provisionAcmeCertificate()` to check custom function first
314
+ - Custom certificates are stored with source type 'custom'
315
+ - Expiry date extraction currently defaults to 90 days
316
+
317
+ 3. **Configuration Options**:
318
+ - `certProvisionFunction`: The custom provision function
319
+ - `certProvisionFallbackToAcme`: Whether to fallback to ACME on error (default: true)
320
+
321
+ 4. **Usage Example**:
322
+ ```typescript
323
+ new SmartProxy({
324
+ certProvisionFunction: async (domain: string) => {
325
+ if (domain === 'internal.example.com') {
326
+ return {
327
+ cert: customCert,
328
+ key: customKey,
329
+ ca: customCA
330
+ } as unknown as TSmartProxyCertProvisionObject;
331
+ }
332
+ return 'http01'; // Use Let's Encrypt
333
+ },
334
+ certProvisionFallbackToAcme: true
335
+ })
336
+ ```
337
+
338
+ 5. **Testing Notes**:
339
+ - Type assertions through `unknown` are needed in tests due to strict interface typing
340
+ - Mock certificate objects work for testing but need proper type casting
341
+ - The actual certificate parsing for expiry dates would need a proper X.509 parser
342
+
343
+ ### Future Improvements
344
+
345
+ 1. Implement proper certificate expiry date extraction using X.509 parsing
346
+ 2. Add support for returning expiry date with custom certificates
347
+ 3. Consider adding validation for custom certificate format
348
+ 4. Add events/hooks for certificate provisioning lifecycle
package/readme.md CHANGED
@@ -2336,14 +2336,117 @@ sequenceDiagram
2336
2336
  • Efficient SNI extraction
2337
2337
  • Minimal overhead routing
2338
2338
 
2339
- ## Certificate Hooks & Events
2339
+ ## Certificate Management
2340
+
2341
+ ### Custom Certificate Provision Function
2342
+
2343
+ SmartProxy supports a custom certificate provision function that allows you to provide your own certificate generation logic while maintaining compatibility with Let's Encrypt:
2344
+
2345
+ ```typescript
2346
+ const proxy = new SmartProxy({
2347
+ certProvisionFunction: async (domain: string): Promise<TSmartProxyCertProvisionObject> => {
2348
+ // Option 1: Return a custom certificate
2349
+ if (domain === 'internal.example.com') {
2350
+ return {
2351
+ cert: customCertPEM,
2352
+ key: customKeyPEM,
2353
+ ca: customCAPEM // Optional CA chain
2354
+ };
2355
+ }
2356
+
2357
+ // Option 2: Fallback to Let's Encrypt
2358
+ return 'http01';
2359
+ },
2360
+
2361
+ // Control fallback behavior when custom provision fails
2362
+ certProvisionFallbackToAcme: true, // Default: true
2363
+
2364
+ routes: [...]
2365
+ });
2366
+ ```
2367
+
2368
+ **Key Features:**
2369
+ - Called for any route with `certificate: 'auto'`
2370
+ - Return custom certificate object or `'http01'` to use Let's Encrypt
2371
+ - Participates in automatic renewal cycle (checked every 12 hours)
2372
+ - Custom certificates stored with source type 'custom' for tracking
2373
+
2374
+ **Configuration Options:**
2375
+ - `certProvisionFunction`: Async function that receives domain and returns certificate or 'http01'
2376
+ - `certProvisionFallbackToAcme`: Whether to fallback to Let's Encrypt if custom provision fails (default: true)
2377
+
2378
+ **Advanced Example with Certificate Manager:**
2379
+
2380
+ ```typescript
2381
+ const certManager = new MyCertificateManager();
2382
+
2383
+ const proxy = new SmartProxy({
2384
+ certProvisionFunction: async (domain: string) => {
2385
+ try {
2386
+ // Check if we have a custom certificate for this domain
2387
+ if (await certManager.hasCustomCert(domain)) {
2388
+ const cert = await certManager.getCertificate(domain);
2389
+ return {
2390
+ cert: cert.certificate,
2391
+ key: cert.privateKey,
2392
+ ca: cert.chain
2393
+ };
2394
+ }
2395
+
2396
+ // Use Let's Encrypt for public domains
2397
+ if (domain.endsWith('.example.com')) {
2398
+ return 'http01';
2399
+ }
2400
+
2401
+ // Generate self-signed for internal domains
2402
+ if (domain.endsWith('.internal')) {
2403
+ const selfSigned = await certManager.generateSelfSigned(domain);
2404
+ return {
2405
+ cert: selfSigned.cert,
2406
+ key: selfSigned.key,
2407
+ ca: ''
2408
+ };
2409
+ }
2410
+
2411
+ // Default to Let's Encrypt
2412
+ return 'http01';
2413
+ } catch (error) {
2414
+ console.error(`Certificate provision failed for ${domain}:`, error);
2415
+ // Will fallback to Let's Encrypt if certProvisionFallbackToAcme is true
2416
+ throw error;
2417
+ }
2418
+ },
2419
+
2420
+ certProvisionFallbackToAcme: true,
2421
+
2422
+ routes: [
2423
+ // Routes that use automatic certificates
2424
+ {
2425
+ match: { ports: 443, domains: ['app.example.com', '*.internal'] },
2426
+ action: {
2427
+ type: 'forward',
2428
+ target: { host: 'localhost', port: 8080 },
2429
+ tls: { mode: 'terminate', certificate: 'auto' }
2430
+ }
2431
+ }
2432
+ ]
2433
+ });
2434
+ ```
2435
+
2436
+ ### Certificate Events
2340
2437
 
2341
2438
  Listen for certificate events via EventEmitter:
2342
2439
  - **SmartProxy**:
2343
2440
  - `certificate` (domain, publicKey, privateKey, expiryDate, source, isRenewal)
2344
2441
  - Events from CertManager are propagated
2345
2442
 
2346
- Provide a `certProvisionFunction(domain)` in SmartProxy settings to supply static certs or return `'http01'`.
2443
+ ```typescript
2444
+ proxy.on('certificate', (domain, cert, key, expiryDate, source, isRenewal) => {
2445
+ console.log(`Certificate ${isRenewal ? 'renewed' : 'provisioned'} for ${domain}`);
2446
+ console.log(`Source: ${source}`); // 'acme', 'static', or 'custom'
2447
+ console.log(`Expires: ${expiryDate}`);
2448
+ });
2449
+ ```
2347
2450
 
2348
2451
  ## SmartProxy: Common Use Cases
2349
2452
 
package/readme.plan.md CHANGED
@@ -1,53 +1,154 @@
1
- # SmartProxy Connection Limiting Improvements Plan
2
-
3
- Command to re-read CLAUDE.md: `cat /home/philkunz/.claude/CLAUDE.md`
4
-
5
- ## Issues Identified
6
-
7
- 1. **HttpProxy Bypass**: Connections forwarded to HttpProxy for TLS termination only check global limits, not per-IP limits
8
- 2. **Missing Route-Level Connection Enforcement**: Routes can define `security.maxConnections` but it's never enforced
9
- 3. **Cleanup Queue Race Condition**: New connections can be added to cleanup queue while processing
10
- 4. **IP Tracking Memory Optimization**: IP entries remain in map even without active connections
11
-
12
- ## Implementation Steps
13
-
14
- ### 1. Fix HttpProxy Per-IP Validation ✓
15
- - [x] Pass IP information to HttpProxy when forwarding connections
16
- - [x] Add per-IP validation in HttpProxy connection handler
17
- - [x] Ensure connection tracking is consistent between SmartProxy and HttpProxy
18
-
19
- ### 2. Implement Route-Level Connection Limits ✓
20
- - [x] Add connection count tracking per route in ConnectionManager
21
- - [x] Update SharedSecurityManager.isAllowed() to check route-specific maxConnections
22
- - [x] Add route connection limit validation in route-connection-handler.ts
23
-
24
- ### 3. Fix Cleanup Queue Race Condition ✓
25
- - [x] Implement proper queue snapshotting before processing
26
- - [x] Ensure new connections added during processing aren't missed
27
- - [x] Add proper synchronization for cleanup operations
28
-
29
- ### 4. Optimize IP Tracking Memory Usage
30
- - [x] Add periodic cleanup for IPs with no active connections
31
- - [x] Implement expiry for rate limit timestamps
32
- - [x] Add memory-efficient data structures for IP tracking
33
-
34
- ### 5. Add Comprehensive Tests
35
- - [x] Test per-IP limits with HttpProxy forwarding
36
- - [x] Test route-level connection limits
37
- - [x] Test cleanup queue edge cases
38
- - [x] Test memory usage with many unique IPs
39
-
40
- ### 6. Log Deduplication for High-Volume Scenarios ✓
41
- - [x] Implement LogDeduplicator utility for batching similar events
42
- - [x] Add deduplication for connection rejections, terminations, and cleanups
43
- - [x] Include rejection reasons in IP rejection summaries
44
- - [x] Provide aggregated summaries with meaningful context
45
-
46
- ## Notes
47
-
48
- - All connection limiting is now consistent across SmartProxy and HttpProxy
49
- - Route-level limits provide additional granular control
50
- - Memory usage is optimized for high-traffic scenarios
51
- - Comprehensive test coverage ensures reliability
52
- - Log deduplication reduces spam during attacks or high-traffic periods
53
- - IP rejection summaries now include rejection reasons in main message
1
+ # SmartProxy Enhanced Routing Plan
2
+
3
+ ## Goal
4
+ Implement enhanced routing structure with multiple targets per route, sub-matching capabilities, and target-specific overrides to enable more elegant and DRY configurations.
5
+
6
+ ## Key Changes
7
+
8
+ ### 1. Update Route Target Interface
9
+ - Add `match` property to `IRouteTarget` for sub-matching within routes
10
+ - Add target-specific override properties (tls, websocket, loadBalancing, etc.)
11
+ - Add priority field for controlling match order
12
+
13
+ ### 2. Update Route Action Interface
14
+ - Remove singular `target` property
15
+ - Use only `targets` array (single target = array with one element)
16
+ - Maintain backwards compatibility during migration
17
+
18
+ ### 3. Implementation Steps
19
+
20
+ #### Phase 1: Type Updates
21
+ - [x] Update `IRouteTarget` interface in `route-types.ts`
22
+ - Add `match?: ITargetMatch` property
23
+ - Add override properties (tls, websocket, etc.)
24
+ - Add `priority?: number` field
25
+ - [x] Create `ITargetMatch` interface for sub-matching criteria
26
+ - [x] Update `IRouteAction` to use only `targets: IRouteTarget[]`
27
+
28
+ #### Phase 2: Route Resolution Logic
29
+ - [x] Update route matching logic to handle multiple targets
30
+ - [x] Implement target sub-matching algorithm:
31
+ 1. Sort targets by priority (highest first)
32
+ 2. For each target with a match property, check if request matches
33
+ 3. Use first matching target, or fallback to target without match
34
+ - [x] Ensure target-specific settings override route-level settings
35
+
36
+ #### Phase 3: Code Migration
37
+ - [x] Find all occurrences of `action.target` and update to use `action.targets`
38
+ - [x] Update route helpers and utilities
39
+ - [x] Update certificate manager to handle multiple targets
40
+ - [x] Update connection handlers
41
+
42
+ #### Phase 4: Testing
43
+ - [x] Update existing tests to use new format
44
+ - [ ] Add tests for multi-target scenarios
45
+ - [ ] Add tests for sub-matching logic
46
+ - [ ] Add tests for setting overrides
47
+
48
+ #### Phase 5: Documentation
49
+ - [ ] Update type documentation
50
+ - [ ] Add examples of new routing patterns
51
+ - [ ] Document migration path for existing configs
52
+
53
+ ## Example Configurations
54
+
55
+ ### Before (Current)
56
+ ```typescript
57
+ // Need separate routes for different ports/paths
58
+ [
59
+ {
60
+ match: { domains: ['api.example.com'], ports: [80] },
61
+ action: {
62
+ type: 'forward',
63
+ target: { host: 'backend', port: 8080 },
64
+ tls: { mode: 'terminate' }
65
+ }
66
+ },
67
+ {
68
+ match: { domains: ['api.example.com'], ports: [443] },
69
+ action: {
70
+ type: 'forward',
71
+ target: { host: 'backend', port: 8081 },
72
+ tls: { mode: 'passthrough' }
73
+ }
74
+ }
75
+ ]
76
+ ```
77
+
78
+ ### After (Enhanced)
79
+ ```typescript
80
+ // Single route with multiple targets
81
+ {
82
+ match: { domains: ['api.example.com'], ports: [80, 443] },
83
+ action: {
84
+ type: 'forward',
85
+ targets: [
86
+ {
87
+ match: { ports: [80] },
88
+ host: 'backend',
89
+ port: 8080,
90
+ tls: { mode: 'terminate' }
91
+ },
92
+ {
93
+ match: { ports: [443] },
94
+ host: 'backend',
95
+ port: 8081,
96
+ tls: { mode: 'passthrough' }
97
+ }
98
+ ]
99
+ }
100
+ }
101
+ ```
102
+
103
+ ### Advanced Example
104
+ ```typescript
105
+ {
106
+ match: { domains: ['app.example.com'], ports: [443] },
107
+ action: {
108
+ type: 'forward',
109
+ tls: { mode: 'terminate', certificate: 'auto' }, // Route-level default
110
+ websocket: { enabled: true }, // Route-level default
111
+ targets: [
112
+ {
113
+ match: { path: '/api/v2/*' },
114
+ host: 'api-v2',
115
+ port: 8082,
116
+ priority: 10
117
+ },
118
+ {
119
+ match: { path: '/api/*', headers: { 'X-Version': 'v1' } },
120
+ host: 'api-v1',
121
+ port: 8081,
122
+ priority: 5
123
+ },
124
+ {
125
+ match: { path: '/ws/*' },
126
+ host: 'websocket-server',
127
+ port: 8090,
128
+ websocket: {
129
+ enabled: true,
130
+ rewritePath: '/' // Strip /ws prefix
131
+ }
132
+ },
133
+ {
134
+ // Default target (no match property)
135
+ host: 'web-backend',
136
+ port: 8080
137
+ }
138
+ ]
139
+ }
140
+ }
141
+ ```
142
+
143
+ ## Benefits
144
+ 1. **DRY Configuration**: No need to duplicate common settings across routes
145
+ 2. **Flexibility**: Different backends for different ports/paths within same domain
146
+ 3. **Clarity**: All routing for a domain in one place
147
+ 4. **Performance**: Single route lookup instead of multiple
148
+ 5. **Backwards Compatible**: Can migrate gradually
149
+
150
+ ## Migration Strategy
151
+ 1. Keep support for `target` temporarily with deprecation warning
152
+ 2. Auto-convert `target` to `targets: [target]` internally
153
+ 3. Update documentation with migration examples
154
+ 4. Remove `target` support in next major version
@@ -13,7 +13,8 @@ import {
13
13
  trackConnection,
14
14
  removeConnection,
15
15
  cleanupExpiredRateLimits,
16
- parseBasicAuthHeader
16
+ parseBasicAuthHeader,
17
+ normalizeIP
17
18
  } from './security-utils.js';
18
19
 
19
20
  /**
@@ -78,7 +79,15 @@ export class SharedSecurityManager {
78
79
  * @returns Number of connections from this IP
79
80
  */
80
81
  public getConnectionCountByIP(ip: string): number {
81
- return this.connectionsByIP.get(ip)?.connections.size || 0;
82
+ // Check all normalized variants of the IP
83
+ const variants = normalizeIP(ip);
84
+ for (const variant of variants) {
85
+ const info = this.connectionsByIP.get(variant);
86
+ if (info) {
87
+ return info.connections.size;
88
+ }
89
+ }
90
+ return 0;
82
91
  }
83
92
 
84
93
  /**
@@ -88,7 +97,19 @@ export class SharedSecurityManager {
88
97
  * @param connectionId - The connection ID to associate
89
98
  */
90
99
  public trackConnectionByIP(ip: string, connectionId: string): void {
91
- trackConnection(ip, connectionId, this.connectionsByIP);
100
+ // Check if any variant already exists
101
+ const variants = normalizeIP(ip);
102
+ let existingKey: string | null = null;
103
+
104
+ for (const variant of variants) {
105
+ if (this.connectionsByIP.has(variant)) {
106
+ existingKey = variant;
107
+ break;
108
+ }
109
+ }
110
+
111
+ // Use existing key or the original IP
112
+ trackConnection(existingKey || ip, connectionId, this.connectionsByIP);
92
113
  }
93
114
 
94
115
  /**
@@ -98,7 +119,15 @@ export class SharedSecurityManager {
98
119
  * @param connectionId - The connection ID to remove
99
120
  */
100
121
  public removeConnectionByIP(ip: string, connectionId: string): void {
101
- removeConnection(ip, connectionId, this.connectionsByIP);
122
+ // Check all variants to find where the connection is tracked
123
+ const variants = normalizeIP(ip);
124
+
125
+ for (const variant of variants) {
126
+ if (this.connectionsByIP.has(variant)) {
127
+ removeConnection(variant, connectionId, this.connectionsByIP);
128
+ break;
129
+ }
130
+ }
102
131
  }
103
132
 
104
133
  /**