@push.rocks/smartproxy 18.0.0 → 18.0.2
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.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/core/utils/route-utils.d.ts +3 -3
- package/dist_ts/core/utils/route-utils.js +9 -9
- package/dist_ts/proxies/network-proxy/http-request-handler.js +3 -2
- package/dist_ts/proxies/nftables-proxy/models/interfaces.d.ts +2 -2
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +21 -21
- package/dist_ts/proxies/smart-proxy/index.d.ts +1 -0
- package/dist_ts/proxies/smart-proxy/index.js +2 -1
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +1 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +14 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/nftables-manager.d.ts +82 -0
- package/dist_ts/proxies/smart-proxy/nftables-manager.js +235 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +42 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +6 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +46 -2
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -0
- package/dist_ts/proxies/smart-proxy/utils/index.js +3 -2
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +77 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +119 -1
- package/package.json +4 -4
- package/readme.plan.md +618 -110
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/utils/route-utils.ts +9 -9
- package/ts/proxies/network-proxy/http-request-handler.ts +3 -2
- package/ts/proxies/nftables-proxy/models/interfaces.ts +2 -2
- package/ts/proxies/nftables-proxy/nftables-proxy.ts +20 -20
- package/ts/proxies/smart-proxy/index.ts +1 -0
- package/ts/proxies/smart-proxy/models/interfaces.ts +3 -0
- package/ts/proxies/smart-proxy/models/route-types.ts +20 -0
- package/ts/proxies/smart-proxy/nftables-manager.ts +268 -0
- package/ts/proxies/smart-proxy/route-connection-handler.ts +55 -0
- package/ts/proxies/smart-proxy/smart-proxy.ts +60 -1
- package/ts/proxies/smart-proxy/utils/index.ts +2 -1
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +192 -0
|
@@ -9,6 +9,7 @@ import { TimeoutManager } from './timeout-manager.js';
|
|
|
9
9
|
import { PortManager } from './port-manager.js';
|
|
10
10
|
import { RouteManager } from './route-manager.js';
|
|
11
11
|
import { RouteConnectionHandler } from './route-connection-handler.js';
|
|
12
|
+
import { NFTablesManager } from './nftables-manager.js';
|
|
12
13
|
|
|
13
14
|
// External dependencies
|
|
14
15
|
import { Port80Handler } from '../../http/port80/port80-handler.js';
|
|
@@ -50,6 +51,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
50
51
|
private timeoutManager: TimeoutManager;
|
|
51
52
|
public routeManager: RouteManager; // Made public for route management
|
|
52
53
|
private routeConnectionHandler: RouteConnectionHandler;
|
|
54
|
+
private nftablesManager: NFTablesManager;
|
|
53
55
|
|
|
54
56
|
// Port80Handler for ACME certificate management
|
|
55
57
|
private port80Handler: Port80Handler | null = null;
|
|
@@ -82,7 +84,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
82
84
|
* ],
|
|
83
85
|
* defaults: {
|
|
84
86
|
* target: { host: 'localhost', port: 8080 },
|
|
85
|
-
* security: {
|
|
87
|
+
* security: { ipAllowList: ['*'] }
|
|
86
88
|
* }
|
|
87
89
|
* });
|
|
88
90
|
* ```
|
|
@@ -167,6 +169,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
167
169
|
|
|
168
170
|
// Initialize port manager
|
|
169
171
|
this.portManager = new PortManager(this.settings, this.routeConnectionHandler);
|
|
172
|
+
|
|
173
|
+
// Initialize NFTablesManager
|
|
174
|
+
this.nftablesManager = new NFTablesManager(this.settings);
|
|
170
175
|
}
|
|
171
176
|
|
|
172
177
|
/**
|
|
@@ -270,6 +275,13 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
270
275
|
// Get listening ports from RouteManager
|
|
271
276
|
const listeningPorts = this.routeManager.getListeningPorts();
|
|
272
277
|
|
|
278
|
+
// Provision NFTables rules for routes that use NFTables
|
|
279
|
+
for (const route of this.settings.routes) {
|
|
280
|
+
if (route.action.forwardingEngine === 'nftables') {
|
|
281
|
+
await this.nftablesManager.provisionRoute(route);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
273
285
|
// Start port listeners using the PortManager
|
|
274
286
|
await this.portManager.addPorts(listeningPorts);
|
|
275
287
|
|
|
@@ -364,6 +376,10 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
364
376
|
await this.certProvisioner.stop();
|
|
365
377
|
console.log('CertProvisioner stopped');
|
|
366
378
|
}
|
|
379
|
+
|
|
380
|
+
// Stop NFTablesManager
|
|
381
|
+
await this.nftablesManager.stop();
|
|
382
|
+
console.log('NFTablesManager stopped');
|
|
367
383
|
|
|
368
384
|
// Stop the Port80Handler if running
|
|
369
385
|
if (this.port80Handler) {
|
|
@@ -432,6 +448,39 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
432
448
|
public async updateRoutes(newRoutes: IRouteConfig[]): Promise<void> {
|
|
433
449
|
console.log(`Updating routes (${newRoutes.length} routes)`);
|
|
434
450
|
|
|
451
|
+
// Get existing routes that use NFTables
|
|
452
|
+
const oldNfTablesRoutes = this.settings.routes.filter(
|
|
453
|
+
r => r.action.forwardingEngine === 'nftables'
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
// Get new routes that use NFTables
|
|
457
|
+
const newNfTablesRoutes = newRoutes.filter(
|
|
458
|
+
r => r.action.forwardingEngine === 'nftables'
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
// Find routes to remove, update, or add
|
|
462
|
+
for (const oldRoute of oldNfTablesRoutes) {
|
|
463
|
+
const newRoute = newNfTablesRoutes.find(r => r.name === oldRoute.name);
|
|
464
|
+
|
|
465
|
+
if (!newRoute) {
|
|
466
|
+
// Route was removed
|
|
467
|
+
await this.nftablesManager.deprovisionRoute(oldRoute);
|
|
468
|
+
} else {
|
|
469
|
+
// Route was updated
|
|
470
|
+
await this.nftablesManager.updateRoute(oldRoute, newRoute);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Find new routes to add
|
|
475
|
+
for (const newRoute of newNfTablesRoutes) {
|
|
476
|
+
const oldRoute = oldNfTablesRoutes.find(r => r.name === newRoute.name);
|
|
477
|
+
|
|
478
|
+
if (!oldRoute) {
|
|
479
|
+
// New route
|
|
480
|
+
await this.nftablesManager.provisionRoute(newRoute);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
435
484
|
// Update routes in RouteManager
|
|
436
485
|
this.routeManager.updateRoutes(newRoutes);
|
|
437
486
|
|
|
@@ -440,6 +489,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
440
489
|
|
|
441
490
|
// Update port listeners to match the new configuration
|
|
442
491
|
await this.portManager.updatePorts(requiredPorts);
|
|
492
|
+
|
|
493
|
+
// Update settings with the new routes
|
|
494
|
+
this.settings.routes = newRoutes;
|
|
443
495
|
|
|
444
496
|
// If NetworkProxy is initialized, resync the configurations
|
|
445
497
|
if (this.networkProxyBridge.getNetworkProxy()) {
|
|
@@ -676,6 +728,13 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
676
728
|
return domains;
|
|
677
729
|
}
|
|
678
730
|
|
|
731
|
+
/**
|
|
732
|
+
* Get NFTables status
|
|
733
|
+
*/
|
|
734
|
+
public async getNfTablesStatus(): Promise<Record<string, any>> {
|
|
735
|
+
return this.nftablesManager.getStatus();
|
|
736
|
+
}
|
|
737
|
+
|
|
679
738
|
/**
|
|
680
739
|
* Get status of certificates managed by Port80Handler
|
|
681
740
|
*/
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* including helpers, validators, utilities, and patterns for working with routes.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
//
|
|
8
|
+
// Export route helpers for creating route configurations
|
|
9
|
+
export * from './route-helpers.js';
|
|
9
10
|
|
|
10
11
|
// Export route validators for validating route configurations
|
|
11
12
|
export * from './route-validators.js';
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* - WebSocket routes (createWebSocketRoute)
|
|
17
17
|
* - Port mapping routes (createPortMappingRoute, createOffsetPortMappingRoute)
|
|
18
18
|
* - Dynamic routing (createDynamicRoute, createSmartLoadBalancer)
|
|
19
|
+
* - NFTables routes (createNfTablesRoute, createNfTablesTerminateRoute)
|
|
19
20
|
*/
|
|
20
21
|
|
|
21
22
|
import type { IRouteConfig, IRouteMatch, IRouteAction, IRouteTarget, TPortRange, IRouteContext } from '../models/route-types.js';
|
|
@@ -618,4 +619,195 @@ export function createSmartLoadBalancer(options: {
|
|
|
618
619
|
priority: options.priority,
|
|
619
620
|
...options
|
|
620
621
|
};
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Create an NFTables-based route for high-performance packet forwarding
|
|
626
|
+
* @param nameOrDomains Name or domain(s) to match
|
|
627
|
+
* @param target Target host and port
|
|
628
|
+
* @param options Additional route options
|
|
629
|
+
* @returns Route configuration object
|
|
630
|
+
*/
|
|
631
|
+
export function createNfTablesRoute(
|
|
632
|
+
nameOrDomains: string | string[],
|
|
633
|
+
target: { host: string; port: number | 'preserve' },
|
|
634
|
+
options: {
|
|
635
|
+
ports?: TPortRange;
|
|
636
|
+
protocol?: 'tcp' | 'udp' | 'all';
|
|
637
|
+
preserveSourceIP?: boolean;
|
|
638
|
+
ipAllowList?: string[];
|
|
639
|
+
ipBlockList?: string[];
|
|
640
|
+
maxRate?: string;
|
|
641
|
+
priority?: number;
|
|
642
|
+
useTls?: boolean;
|
|
643
|
+
tableName?: string;
|
|
644
|
+
useIPSets?: boolean;
|
|
645
|
+
useAdvancedNAT?: boolean;
|
|
646
|
+
} = {}
|
|
647
|
+
): IRouteConfig {
|
|
648
|
+
// Determine if this is a name or domain
|
|
649
|
+
let name: string;
|
|
650
|
+
let domains: string | string[] | undefined;
|
|
651
|
+
|
|
652
|
+
if (Array.isArray(nameOrDomains) || (typeof nameOrDomains === 'string' && nameOrDomains.includes('.'))) {
|
|
653
|
+
domains = nameOrDomains;
|
|
654
|
+
name = Array.isArray(nameOrDomains) ? nameOrDomains[0] : nameOrDomains;
|
|
655
|
+
} else {
|
|
656
|
+
name = nameOrDomains;
|
|
657
|
+
domains = undefined; // No domains
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Create route match
|
|
661
|
+
const match: IRouteMatch = {
|
|
662
|
+
domains,
|
|
663
|
+
ports: options.ports || 80
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
// Create route action
|
|
667
|
+
const action: IRouteAction = {
|
|
668
|
+
type: 'forward',
|
|
669
|
+
target: {
|
|
670
|
+
host: target.host,
|
|
671
|
+
port: target.port
|
|
672
|
+
},
|
|
673
|
+
forwardingEngine: 'nftables',
|
|
674
|
+
nftables: {
|
|
675
|
+
protocol: options.protocol || 'tcp',
|
|
676
|
+
preserveSourceIP: options.preserveSourceIP,
|
|
677
|
+
maxRate: options.maxRate,
|
|
678
|
+
priority: options.priority,
|
|
679
|
+
tableName: options.tableName,
|
|
680
|
+
useIPSets: options.useIPSets,
|
|
681
|
+
useAdvancedNAT: options.useAdvancedNAT
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
// Add security if allowed or blocked IPs are specified
|
|
686
|
+
if (options.ipAllowList?.length || options.ipBlockList?.length) {
|
|
687
|
+
action.security = {
|
|
688
|
+
ipAllowList: options.ipAllowList,
|
|
689
|
+
ipBlockList: options.ipBlockList
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// Add TLS options if needed
|
|
694
|
+
if (options.useTls) {
|
|
695
|
+
action.tls = {
|
|
696
|
+
mode: 'passthrough'
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Create the route config
|
|
701
|
+
return {
|
|
702
|
+
name,
|
|
703
|
+
match,
|
|
704
|
+
action
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Create an NFTables-based TLS termination route
|
|
710
|
+
* @param nameOrDomains Name or domain(s) to match
|
|
711
|
+
* @param target Target host and port
|
|
712
|
+
* @param options Additional route options
|
|
713
|
+
* @returns Route configuration object
|
|
714
|
+
*/
|
|
715
|
+
export function createNfTablesTerminateRoute(
|
|
716
|
+
nameOrDomains: string | string[],
|
|
717
|
+
target: { host: string; port: number | 'preserve' },
|
|
718
|
+
options: {
|
|
719
|
+
ports?: TPortRange;
|
|
720
|
+
protocol?: 'tcp' | 'udp' | 'all';
|
|
721
|
+
preserveSourceIP?: boolean;
|
|
722
|
+
ipAllowList?: string[];
|
|
723
|
+
ipBlockList?: string[];
|
|
724
|
+
maxRate?: string;
|
|
725
|
+
priority?: number;
|
|
726
|
+
tableName?: string;
|
|
727
|
+
useIPSets?: boolean;
|
|
728
|
+
useAdvancedNAT?: boolean;
|
|
729
|
+
certificate?: 'auto' | { key: string; cert: string };
|
|
730
|
+
} = {}
|
|
731
|
+
): IRouteConfig {
|
|
732
|
+
// Create basic NFTables route
|
|
733
|
+
const route = createNfTablesRoute(
|
|
734
|
+
nameOrDomains,
|
|
735
|
+
target,
|
|
736
|
+
{
|
|
737
|
+
...options,
|
|
738
|
+
ports: options.ports || 443,
|
|
739
|
+
useTls: false
|
|
740
|
+
}
|
|
741
|
+
);
|
|
742
|
+
|
|
743
|
+
// Set TLS termination
|
|
744
|
+
route.action.tls = {
|
|
745
|
+
mode: 'terminate',
|
|
746
|
+
certificate: options.certificate || 'auto'
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
return route;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Create a complete NFTables-based HTTPS setup with HTTP redirect
|
|
754
|
+
* @param nameOrDomains Name or domain(s) to match
|
|
755
|
+
* @param target Target host and port
|
|
756
|
+
* @param options Additional route options
|
|
757
|
+
* @returns Array of two route configurations (HTTPS and HTTP redirect)
|
|
758
|
+
*/
|
|
759
|
+
export function createCompleteNfTablesHttpsServer(
|
|
760
|
+
nameOrDomains: string | string[],
|
|
761
|
+
target: { host: string; port: number | 'preserve' },
|
|
762
|
+
options: {
|
|
763
|
+
httpPort?: TPortRange;
|
|
764
|
+
httpsPort?: TPortRange;
|
|
765
|
+
protocol?: 'tcp' | 'udp' | 'all';
|
|
766
|
+
preserveSourceIP?: boolean;
|
|
767
|
+
ipAllowList?: string[];
|
|
768
|
+
ipBlockList?: string[];
|
|
769
|
+
maxRate?: string;
|
|
770
|
+
priority?: number;
|
|
771
|
+
tableName?: string;
|
|
772
|
+
useIPSets?: boolean;
|
|
773
|
+
useAdvancedNAT?: boolean;
|
|
774
|
+
certificate?: 'auto' | { key: string; cert: string };
|
|
775
|
+
} = {}
|
|
776
|
+
): IRouteConfig[] {
|
|
777
|
+
// Create the HTTPS route using NFTables
|
|
778
|
+
const httpsRoute = createNfTablesTerminateRoute(
|
|
779
|
+
nameOrDomains,
|
|
780
|
+
target,
|
|
781
|
+
{
|
|
782
|
+
...options,
|
|
783
|
+
ports: options.httpsPort || 443
|
|
784
|
+
}
|
|
785
|
+
);
|
|
786
|
+
|
|
787
|
+
// Determine the domain(s) for HTTP redirect
|
|
788
|
+
const domains = typeof nameOrDomains === 'string' && !nameOrDomains.includes('.')
|
|
789
|
+
? undefined
|
|
790
|
+
: nameOrDomains;
|
|
791
|
+
|
|
792
|
+
// Extract the HTTPS port for the redirect destination
|
|
793
|
+
const httpsPort = typeof options.httpsPort === 'number'
|
|
794
|
+
? options.httpsPort
|
|
795
|
+
: Array.isArray(options.httpsPort) && typeof options.httpsPort[0] === 'number'
|
|
796
|
+
? options.httpsPort[0]
|
|
797
|
+
: 443;
|
|
798
|
+
|
|
799
|
+
// Create the HTTP redirect route (this uses standard forwarding, not NFTables)
|
|
800
|
+
const httpRedirectRoute = createHttpToHttpsRedirect(
|
|
801
|
+
domains as any, // Type cast needed since domains can be undefined now
|
|
802
|
+
httpsPort,
|
|
803
|
+
{
|
|
804
|
+
match: {
|
|
805
|
+
ports: options.httpPort || 80,
|
|
806
|
+
domains: domains as any // Type cast needed since domains can be undefined now
|
|
807
|
+
},
|
|
808
|
+
name: `HTTP to HTTPS Redirect for ${Array.isArray(domains) ? domains.join(', ') : domains || 'all domains'}`
|
|
809
|
+
}
|
|
810
|
+
);
|
|
811
|
+
|
|
812
|
+
return [httpsRoute, httpRedirectRoute];
|
|
621
813
|
}
|