@push.rocks/smartproxy 22.4.2 → 23.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.
- package/changelog.md +36 -0
- package/dist_rust/rustproxy +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +1 -6
- package/dist_ts/index.js +3 -11
- package/dist_ts/protocols/common/fragment-handler.js +5 -1
- package/dist_ts/proxies/index.d.ts +1 -6
- package/dist_ts/proxies/index.js +2 -8
- package/dist_ts/proxies/smart-proxy/index.d.ts +5 -10
- package/dist_ts/proxies/smart-proxy/index.js +7 -13
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -2
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/route-preprocessor.d.ts +37 -0
- package/dist_ts/proxies/smart-proxy/route-preprocessor.js +103 -0
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +23 -0
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +104 -0
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +74 -0
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +146 -0
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +49 -0
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +259 -0
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +39 -157
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +224 -621
- package/dist_ts/proxies/smart-proxy/socket-handler-server.d.ts +45 -0
- package/dist_ts/proxies/smart-proxy/socket-handler-server.js +253 -0
- package/dist_ts/routing/index.d.ts +1 -1
- package/dist_ts/routing/index.js +3 -3
- package/dist_ts/routing/models/http-types.d.ts +119 -4
- package/dist_ts/routing/models/http-types.js +93 -5
- package/package.json +1 -1
- package/readme.md +444 -219
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +4 -15
- package/ts/protocols/common/fragment-handler.ts +4 -0
- package/ts/proxies/index.ts +1 -12
- package/ts/proxies/smart-proxy/index.ts +6 -13
- package/ts/proxies/smart-proxy/models/interfaces.ts +6 -4
- package/ts/proxies/smart-proxy/models/route-types.ts +0 -2
- package/ts/proxies/smart-proxy/route-preprocessor.ts +122 -0
- package/ts/proxies/smart-proxy/rust-binary-locator.ts +112 -0
- package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +161 -0
- package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +310 -0
- package/ts/proxies/smart-proxy/smart-proxy.ts +282 -798
- package/ts/proxies/smart-proxy/socket-handler-server.ts +279 -0
- package/ts/routing/index.ts +2 -2
- package/ts/routing/models/http-types.ts +147 -4
- package/dist_ts/proxies/nftables-proxy/index.d.ts +0 -6
- package/dist_ts/proxies/nftables-proxy/index.js +0 -7
- package/dist_ts/proxies/nftables-proxy/models/errors.d.ts +0 -15
- package/dist_ts/proxies/nftables-proxy/models/errors.js +0 -28
- package/dist_ts/proxies/nftables-proxy/models/index.d.ts +0 -5
- package/dist_ts/proxies/nftables-proxy/models/index.js +0 -6
- package/dist_ts/proxies/nftables-proxy/models/interfaces.d.ts +0 -75
- package/dist_ts/proxies/nftables-proxy/models/interfaces.js +0 -5
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +0 -124
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +0 -1374
- package/dist_ts/proxies/nftables-proxy/utils/index.d.ts +0 -9
- package/dist_ts/proxies/nftables-proxy/utils/index.js +0 -12
- package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.d.ts +0 -66
- package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.js +0 -131
- package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.d.ts +0 -39
- package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.js +0 -112
- package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.d.ts +0 -59
- package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.js +0 -130
- package/ts/proxies/http-proxy/connection-pool.ts +0 -228
- package/ts/proxies/http-proxy/context-creator.ts +0 -145
- package/ts/proxies/http-proxy/default-certificates.ts +0 -150
- package/ts/proxies/http-proxy/function-cache.ts +0 -279
- package/ts/proxies/http-proxy/handlers/index.ts +0 -5
- package/ts/proxies/http-proxy/http-proxy.ts +0 -669
- package/ts/proxies/http-proxy/http-request-handler.ts +0 -331
- package/ts/proxies/http-proxy/http2-request-handler.ts +0 -255
- package/ts/proxies/http-proxy/index.ts +0 -18
- package/ts/proxies/http-proxy/models/http-types.ts +0 -148
- package/ts/proxies/http-proxy/models/index.ts +0 -5
- package/ts/proxies/http-proxy/models/types.ts +0 -125
- package/ts/proxies/http-proxy/request-handler.ts +0 -878
- package/ts/proxies/http-proxy/security-manager.ts +0 -413
- package/ts/proxies/http-proxy/websocket-handler.ts +0 -581
- package/ts/proxies/nftables-proxy/index.ts +0 -6
- package/ts/proxies/nftables-proxy/models/errors.ts +0 -30
- package/ts/proxies/nftables-proxy/models/index.ts +0 -5
- package/ts/proxies/nftables-proxy/models/interfaces.ts +0 -94
- package/ts/proxies/nftables-proxy/nftables-proxy.ts +0 -1754
- package/ts/proxies/nftables-proxy/utils/index.ts +0 -38
- package/ts/proxies/nftables-proxy/utils/nft-command-executor.ts +0 -162
- package/ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts +0 -125
- package/ts/proxies/nftables-proxy/utils/nft-rule-validator.ts +0 -156
- package/ts/proxies/smart-proxy/acme-state-manager.ts +0 -112
- package/ts/proxies/smart-proxy/cert-store.ts +0 -92
- package/ts/proxies/smart-proxy/certificate-manager.ts +0 -895
- package/ts/proxies/smart-proxy/connection-manager.ts +0 -809
- package/ts/proxies/smart-proxy/http-proxy-bridge.ts +0 -213
- package/ts/proxies/smart-proxy/metrics-collector.ts +0 -453
- package/ts/proxies/smart-proxy/nftables-manager.ts +0 -271
- package/ts/proxies/smart-proxy/port-manager.ts +0 -358
- package/ts/proxies/smart-proxy/route-connection-handler.ts +0 -1712
- package/ts/proxies/smart-proxy/route-orchestrator.ts +0 -297
- package/ts/proxies/smart-proxy/security-manager.ts +0 -269
- package/ts/proxies/smart-proxy/throughput-tracker.ts +0 -138
- package/ts/proxies/smart-proxy/timeout-manager.ts +0 -196
- package/ts/proxies/smart-proxy/tls-manager.ts +0 -171
|
@@ -1,746 +1,349 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import { logger } from '../../core/utils/logger.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import { PortManager } from './port-manager.js';
|
|
3
|
+
// Rust bridge and helpers
|
|
4
|
+
import { RustProxyBridge } from './rust-proxy-bridge.js';
|
|
5
|
+
import { RustBinaryLocator } from './rust-binary-locator.js';
|
|
6
|
+
import { RoutePreprocessor } from './route-preprocessor.js';
|
|
7
|
+
import { SocketHandlerServer } from './socket-handler-server.js';
|
|
8
|
+
import { RustMetricsAdapter } from './rust-metrics-adapter.js';
|
|
9
|
+
// Route management
|
|
11
10
|
import { SharedRouteManager as RouteManager } from '../../core/routing/route-manager.js';
|
|
12
|
-
import { RouteConnectionHandler } from './route-connection-handler.js';
|
|
13
|
-
import { NFTablesManager } from './nftables-manager.js';
|
|
14
|
-
// Certificate manager
|
|
15
|
-
import { SmartCertManager } from './certificate-manager.js';
|
|
16
|
-
// Import mutex for route update synchronization
|
|
17
|
-
import { Mutex } from './utils/mutex.js';
|
|
18
|
-
// Import route validator
|
|
19
11
|
import { RouteValidator } from './utils/route-validator.js';
|
|
20
|
-
|
|
21
|
-
import { RouteOrchestrator } from './route-orchestrator.js';
|
|
22
|
-
// Import ACME state manager
|
|
23
|
-
import { AcmeStateManager } from './acme-state-manager.js';
|
|
24
|
-
// Import metrics collector
|
|
25
|
-
import { MetricsCollector } from './metrics-collector.js';
|
|
12
|
+
import { Mutex } from './utils/mutex.js';
|
|
26
13
|
/**
|
|
27
|
-
* SmartProxy -
|
|
14
|
+
* SmartProxy - Rust-backed proxy engine with TypeScript configuration API.
|
|
28
15
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* -
|
|
34
|
-
* -
|
|
35
|
-
* - How to handle TLS (passthrough, terminate, terminate-and-reencrypt)
|
|
36
|
-
* - Security settings (IP restrictions, connection limits)
|
|
37
|
-
* - Advanced options (timeout, headers, etc.)
|
|
16
|
+
* All networking (TCP, TLS, HTTP reverse proxy, connection management, security,
|
|
17
|
+
* NFTables) is handled by the Rust binary. TypeScript is only:
|
|
18
|
+
* - The npm module interface (types, route helpers)
|
|
19
|
+
* - The thin IPC wrapper (this class)
|
|
20
|
+
* - Socket-handler callback relay (for JS-defined handlers)
|
|
21
|
+
* - Certificate provisioning callbacks (certProvisionFunction)
|
|
38
22
|
*/
|
|
39
23
|
export class SmartProxy extends plugins.EventEmitter {
|
|
40
|
-
/**
|
|
41
|
-
* Constructor for SmartProxy
|
|
42
|
-
*
|
|
43
|
-
* @param settingsArg Configuration options containing routes and other settings
|
|
44
|
-
* Routes define how traffic is matched and handled, with each route having:
|
|
45
|
-
* - match: criteria for matching traffic (ports, domains, paths, IPs)
|
|
46
|
-
* - action: what to do with matched traffic (forward, redirect, block)
|
|
47
|
-
*
|
|
48
|
-
* Example:
|
|
49
|
-
* ```ts
|
|
50
|
-
* const proxy = new SmartProxy({
|
|
51
|
-
* routes: [
|
|
52
|
-
* {
|
|
53
|
-
* match: {
|
|
54
|
-
* ports: 443,
|
|
55
|
-
* domains: ['example.com', '*.example.com']
|
|
56
|
-
* },
|
|
57
|
-
* action: {
|
|
58
|
-
* type: 'forward',
|
|
59
|
-
* target: { host: '10.0.0.1', port: 8443 },
|
|
60
|
-
* tls: { mode: 'passthrough' }
|
|
61
|
-
* }
|
|
62
|
-
* }
|
|
63
|
-
* ],
|
|
64
|
-
* defaults: {
|
|
65
|
-
* target: { host: 'localhost', port: 8080 },
|
|
66
|
-
* security: { ipAllowList: ['*'] }
|
|
67
|
-
* }
|
|
68
|
-
* });
|
|
69
|
-
* ```
|
|
70
|
-
*/
|
|
71
24
|
constructor(settingsArg) {
|
|
72
25
|
super();
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
75
|
-
//
|
|
76
|
-
this.certManager = null;
|
|
77
|
-
// Global challenge route tracking
|
|
78
|
-
this.globalChallengeRouteActive = false;
|
|
79
|
-
// Track port usage across route updates
|
|
80
|
-
this.portUsageMap = new Map();
|
|
81
|
-
// Set reasonable defaults for all settings
|
|
26
|
+
this.socketHandlerServer = null;
|
|
27
|
+
this.stopping = false;
|
|
28
|
+
// Apply defaults
|
|
82
29
|
this.settings = {
|
|
83
30
|
...settingsArg,
|
|
84
31
|
initialDataTimeout: settingsArg.initialDataTimeout || 120000,
|
|
85
32
|
socketTimeout: settingsArg.socketTimeout || 3600000,
|
|
86
|
-
inactivityCheckInterval: settingsArg.inactivityCheckInterval || 60000,
|
|
87
33
|
maxConnectionLifetime: settingsArg.maxConnectionLifetime || 86400000,
|
|
88
34
|
inactivityTimeout: settingsArg.inactivityTimeout || 14400000,
|
|
89
35
|
gracefulShutdownTimeout: settingsArg.gracefulShutdownTimeout || 30000,
|
|
90
|
-
noDelay: settingsArg.noDelay !== undefined ? settingsArg.noDelay : true,
|
|
91
|
-
keepAlive: settingsArg.keepAlive !== undefined ? settingsArg.keepAlive : true,
|
|
92
|
-
keepAliveInitialDelay: settingsArg.keepAliveInitialDelay || 10000,
|
|
93
|
-
maxPendingDataSize: settingsArg.maxPendingDataSize || 10 * 1024 * 1024,
|
|
94
|
-
disableInactivityCheck: settingsArg.disableInactivityCheck || false,
|
|
95
|
-
enableKeepAliveProbes: settingsArg.enableKeepAliveProbes !== undefined ? settingsArg.enableKeepAliveProbes : true,
|
|
96
|
-
enableDetailedLogging: settingsArg.enableDetailedLogging || false,
|
|
97
|
-
enableTlsDebugLogging: settingsArg.enableTlsDebugLogging || false,
|
|
98
|
-
enableRandomizedTimeouts: settingsArg.enableRandomizedTimeouts || false,
|
|
99
36
|
maxConnectionsPerIP: settingsArg.maxConnectionsPerIP || 100,
|
|
100
37
|
connectionRateLimitPerMinute: settingsArg.connectionRateLimitPerMinute || 300,
|
|
101
38
|
keepAliveTreatment: settingsArg.keepAliveTreatment || 'extended',
|
|
102
39
|
keepAliveInactivityMultiplier: settingsArg.keepAliveInactivityMultiplier || 6,
|
|
103
40
|
extendedKeepAliveLifetime: settingsArg.extendedKeepAliveLifetime || 7 * 24 * 60 * 60 * 1000,
|
|
104
|
-
httpProxyPort: settingsArg.httpProxyPort || 8443,
|
|
105
41
|
};
|
|
106
|
-
// Normalize ACME options
|
|
42
|
+
// Normalize ACME options
|
|
107
43
|
if (this.settings.acme) {
|
|
108
|
-
// Support both 'email' and 'accountEmail' fields
|
|
109
44
|
if (this.settings.acme.accountEmail && !this.settings.acme.email) {
|
|
110
45
|
this.settings.acme.email = this.settings.acme.accountEmail;
|
|
111
46
|
}
|
|
112
|
-
// Set reasonable defaults for commonly used fields
|
|
113
47
|
this.settings.acme = {
|
|
114
|
-
enabled: this.settings.acme.enabled !== false,
|
|
48
|
+
enabled: this.settings.acme.enabled !== false,
|
|
115
49
|
port: this.settings.acme.port || 80,
|
|
116
50
|
email: this.settings.acme.email,
|
|
117
51
|
useProduction: this.settings.acme.useProduction || false,
|
|
118
52
|
renewThresholdDays: this.settings.acme.renewThresholdDays || 30,
|
|
119
|
-
autoRenew: this.settings.acme.autoRenew !== false,
|
|
53
|
+
autoRenew: this.settings.acme.autoRenew !== false,
|
|
120
54
|
certificateStore: this.settings.acme.certificateStore || './certs',
|
|
121
55
|
skipConfiguredCerts: this.settings.acme.skipConfiguredCerts || false,
|
|
122
56
|
renewCheckIntervalHours: this.settings.acme.renewCheckIntervalHours || 24,
|
|
123
57
|
routeForwards: this.settings.acme.routeForwards || [],
|
|
124
|
-
...this.settings.acme
|
|
58
|
+
...this.settings.acme,
|
|
125
59
|
};
|
|
126
60
|
}
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
this.securityManager = new SecurityManager(this);
|
|
130
|
-
this.connectionManager = new ConnectionManager(this);
|
|
131
|
-
// Create the route manager with SharedRouteManager API
|
|
132
|
-
// Create a logger adapter to match ILogger interface
|
|
133
|
-
const loggerAdapter = {
|
|
134
|
-
debug: (message, data) => logger.log('debug', message, data),
|
|
135
|
-
info: (message, data) => logger.log('info', message, data),
|
|
136
|
-
warn: (message, data) => logger.log('warn', message, data),
|
|
137
|
-
error: (message, data) => logger.log('error', message, data)
|
|
138
|
-
};
|
|
139
|
-
// Validate initial routes
|
|
140
|
-
if (this.settings.routes && this.settings.routes.length > 0) {
|
|
61
|
+
// Validate routes
|
|
62
|
+
if (this.settings.routes?.length) {
|
|
141
63
|
const validation = RouteValidator.validateRoutes(this.settings.routes);
|
|
142
64
|
if (!validation.valid) {
|
|
143
65
|
RouteValidator.logValidationErrors(validation.errors);
|
|
144
66
|
throw new Error(`Initial route validation failed: ${validation.errors.size} route(s) have errors`);
|
|
145
67
|
}
|
|
146
68
|
}
|
|
69
|
+
// Create logger adapter
|
|
70
|
+
const loggerAdapter = {
|
|
71
|
+
debug: (message, data) => logger.log('debug', message, data),
|
|
72
|
+
info: (message, data) => logger.log('info', message, data),
|
|
73
|
+
warn: (message, data) => logger.log('warn', message, data),
|
|
74
|
+
error: (message, data) => logger.log('error', message, data),
|
|
75
|
+
};
|
|
76
|
+
// Initialize components
|
|
147
77
|
this.routeManager = new RouteManager({
|
|
148
78
|
logger: loggerAdapter,
|
|
149
79
|
enableDetailedLogging: this.settings.enableDetailedLogging,
|
|
150
|
-
routes: this.settings.routes
|
|
80
|
+
routes: this.settings.routes,
|
|
151
81
|
});
|
|
152
|
-
|
|
153
|
-
this.
|
|
154
|
-
this.
|
|
155
|
-
// Initialize connection handler with route support
|
|
156
|
-
this.routeConnectionHandler = new RouteConnectionHandler(this);
|
|
157
|
-
// Initialize port manager
|
|
158
|
-
this.portManager = new PortManager(this);
|
|
159
|
-
// Initialize NFTablesManager
|
|
160
|
-
this.nftablesManager = new NFTablesManager(this);
|
|
161
|
-
// Initialize route update mutex for synchronization
|
|
82
|
+
this.bridge = new RustProxyBridge();
|
|
83
|
+
this.preprocessor = new RoutePreprocessor();
|
|
84
|
+
this.metricsAdapter = new RustMetricsAdapter(this.bridge, this.settings.metrics?.sampleIntervalMs ?? 1000);
|
|
162
85
|
this.routeUpdateLock = new Mutex();
|
|
163
|
-
// Initialize ACME state manager
|
|
164
|
-
this.acmeStateManager = new AcmeStateManager();
|
|
165
|
-
// Initialize metrics collector with reference to this SmartProxy instance
|
|
166
|
-
this.metricsCollector = new MetricsCollector(this, {
|
|
167
|
-
sampleIntervalMs: this.settings.metrics?.sampleIntervalMs,
|
|
168
|
-
retentionSeconds: this.settings.metrics?.retentionSeconds
|
|
169
|
-
});
|
|
170
|
-
// Initialize route orchestrator for managing route updates
|
|
171
|
-
this.routeOrchestrator = new RouteOrchestrator(this.portManager, this.routeManager, this.httpProxyBridge, this.nftablesManager, null, // certManager will be set later
|
|
172
|
-
loggerAdapter);
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Helper method to create and configure certificate manager
|
|
176
|
-
* This ensures consistent setup including the required ACME callback
|
|
177
|
-
*/
|
|
178
|
-
async createCertificateManager(routes, certStore = './certs', acmeOptions, initialState) {
|
|
179
|
-
const certManager = new SmartCertManager(routes, certStore, acmeOptions, initialState);
|
|
180
|
-
// Always set up the route update callback for ACME challenges
|
|
181
|
-
certManager.setUpdateRoutesCallback(async (routes) => {
|
|
182
|
-
await this.updateRoutes(routes);
|
|
183
|
-
});
|
|
184
|
-
// Connect with HttpProxy if available
|
|
185
|
-
if (this.httpProxyBridge.getHttpProxy()) {
|
|
186
|
-
certManager.setHttpProxy(this.httpProxyBridge.getHttpProxy());
|
|
187
|
-
}
|
|
188
|
-
// Set the ACME state manager
|
|
189
|
-
certManager.setAcmeStateManager(this.acmeStateManager);
|
|
190
|
-
// Pass down the global ACME config if available
|
|
191
|
-
if (this.settings.acme) {
|
|
192
|
-
certManager.setGlobalAcmeDefaults(this.settings.acme);
|
|
193
|
-
}
|
|
194
|
-
// Pass down the custom certificate provision function if available
|
|
195
|
-
if (this.settings.certProvisionFunction) {
|
|
196
|
-
certManager.setCertProvisionFunction(this.settings.certProvisionFunction);
|
|
197
|
-
}
|
|
198
|
-
// Pass down the fallback to ACME setting
|
|
199
|
-
if (this.settings.certProvisionFallbackToAcme !== undefined) {
|
|
200
|
-
certManager.setCertProvisionFallbackToAcme(this.settings.certProvisionFallbackToAcme);
|
|
201
|
-
}
|
|
202
|
-
await certManager.initialize();
|
|
203
|
-
return certManager;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Initialize certificate manager
|
|
207
|
-
*/
|
|
208
|
-
async initializeCertificateManager() {
|
|
209
|
-
// Extract global ACME options if any routes use auto certificates
|
|
210
|
-
const autoRoutes = this.settings.routes.filter(r => r.action.tls?.certificate === 'auto');
|
|
211
|
-
if (autoRoutes.length === 0 && !this.hasStaticCertRoutes()) {
|
|
212
|
-
logger.log('info', 'No routes require certificate management', { component: 'certificate-manager' });
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
// Prepare ACME options with priority:
|
|
216
|
-
// 1. Use top-level ACME config if available
|
|
217
|
-
// 2. Fall back to first auto route's ACME config
|
|
218
|
-
// 3. Otherwise use undefined
|
|
219
|
-
let acmeOptions;
|
|
220
|
-
if (this.settings.acme?.email) {
|
|
221
|
-
// Use top-level ACME config
|
|
222
|
-
acmeOptions = {
|
|
223
|
-
email: this.settings.acme.email,
|
|
224
|
-
useProduction: this.settings.acme.useProduction || false,
|
|
225
|
-
port: this.settings.acme.port || 80
|
|
226
|
-
};
|
|
227
|
-
logger.log('info', `Using top-level ACME configuration with email: ${acmeOptions.email}`, { component: 'certificate-manager' });
|
|
228
|
-
}
|
|
229
|
-
else if (autoRoutes.length > 0) {
|
|
230
|
-
// Check for route-level ACME config
|
|
231
|
-
const routeWithAcme = autoRoutes.find(r => r.action.tls?.acme?.email);
|
|
232
|
-
if (routeWithAcme?.action.tls?.acme) {
|
|
233
|
-
const routeAcme = routeWithAcme.action.tls.acme;
|
|
234
|
-
acmeOptions = {
|
|
235
|
-
email: routeAcme.email,
|
|
236
|
-
useProduction: routeAcme.useProduction || false,
|
|
237
|
-
port: routeAcme.challengePort || 80
|
|
238
|
-
};
|
|
239
|
-
logger.log('info', `Using route-level ACME configuration from route '${routeWithAcme.name}' with email: ${acmeOptions.email}`, { component: 'certificate-manager' });
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
// Validate we have required configuration
|
|
243
|
-
if (autoRoutes.length > 0 && !acmeOptions?.email) {
|
|
244
|
-
throw new Error('ACME email is required for automatic certificate provisioning. ' +
|
|
245
|
-
'Please provide email in either:\n' +
|
|
246
|
-
'1. Top-level "acme" configuration\n' +
|
|
247
|
-
'2. Individual route\'s "tls.acme" configuration');
|
|
248
|
-
}
|
|
249
|
-
// Use the helper method to create and configure the certificate manager
|
|
250
|
-
this.certManager = await this.createCertificateManager(this.settings.routes, this.settings.acme?.certificateStore || './certs', acmeOptions);
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Check if we have routes with static certificates
|
|
254
|
-
*/
|
|
255
|
-
hasStaticCertRoutes() {
|
|
256
|
-
return this.settings.routes.some(r => r.action.tls?.certificate &&
|
|
257
|
-
r.action.tls.certificate !== 'auto');
|
|
258
86
|
}
|
|
259
87
|
/**
|
|
260
|
-
* Start the proxy
|
|
88
|
+
* Start the proxy.
|
|
89
|
+
* Spawns the Rust binary, configures socket relay if needed, sends routes, handles cert provisioning.
|
|
261
90
|
*/
|
|
262
91
|
async start() {
|
|
263
|
-
//
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const allWarnings = [...configWarnings, ...acmeWarnings];
|
|
273
|
-
if (allWarnings.length > 0) {
|
|
274
|
-
logger.log('warn', `${allWarnings.length} configuration warnings found`, { count: allWarnings.length });
|
|
275
|
-
for (const warning of allWarnings) {
|
|
276
|
-
logger.log('warn', `${warning}`);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
// Get listening ports from RouteManager
|
|
280
|
-
const listeningPorts = this.routeManager.getListeningPorts();
|
|
281
|
-
// Initialize port usage tracking using RouteOrchestrator
|
|
282
|
-
this.portUsageMap = this.routeOrchestrator.updatePortUsageMap(this.settings.routes);
|
|
283
|
-
// Log port usage for startup
|
|
284
|
-
logger.log('info', `SmartProxy starting with ${listeningPorts.length} ports: ${listeningPorts.join(', ')}`, {
|
|
285
|
-
portCount: listeningPorts.length,
|
|
286
|
-
ports: listeningPorts,
|
|
287
|
-
component: 'smart-proxy'
|
|
288
|
-
});
|
|
289
|
-
// Provision NFTables rules for routes that use NFTables
|
|
290
|
-
for (const route of this.settings.routes) {
|
|
291
|
-
if (route.action.forwardingEngine === 'nftables') {
|
|
292
|
-
await this.nftablesManager.provisionRoute(route);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
// Initialize and start HttpProxy if needed - before port binding
|
|
296
|
-
if (this.settings.useHttpProxy && this.settings.useHttpProxy.length > 0) {
|
|
297
|
-
await this.httpProxyBridge.initialize();
|
|
298
|
-
await this.httpProxyBridge.start();
|
|
299
|
-
}
|
|
300
|
-
// Start port listeners using the PortManager BEFORE initializing certificate manager
|
|
301
|
-
// This ensures all required ports are bound and ready when adding ACME challenge routes
|
|
302
|
-
await this.portManager.addPorts(listeningPorts);
|
|
303
|
-
// Initialize certificate manager AFTER port binding is complete
|
|
304
|
-
// This ensures the ACME challenge port is already bound and ready when needed
|
|
305
|
-
await this.initializeCertificateManager();
|
|
306
|
-
// Connect certificate manager with HttpProxy if both are available
|
|
307
|
-
if (this.certManager && this.httpProxyBridge.getHttpProxy()) {
|
|
308
|
-
this.certManager.setHttpProxy(this.httpProxyBridge.getHttpProxy());
|
|
309
|
-
}
|
|
310
|
-
// Now that ports are listening, provision any required certificates
|
|
311
|
-
if (this.certManager) {
|
|
312
|
-
logger.log('info', 'Starting certificate provisioning now that ports are ready', { component: 'certificate-manager' });
|
|
313
|
-
await this.certManager.provisionAllCertificates();
|
|
314
|
-
}
|
|
315
|
-
// Start the metrics collector now that all components are initialized
|
|
316
|
-
this.metricsCollector.start();
|
|
317
|
-
// Set up periodic connection logging and inactivity checks
|
|
318
|
-
this.connectionLogger = setInterval(() => {
|
|
319
|
-
// Immediately return if shutting down
|
|
320
|
-
if (this.isShuttingDown)
|
|
92
|
+
// Spawn Rust binary
|
|
93
|
+
const spawned = await this.bridge.spawn();
|
|
94
|
+
if (!spawned) {
|
|
95
|
+
throw new Error('RustProxy binary not found. Set SMARTPROXY_RUST_BINARY env var, install the platform package, ' +
|
|
96
|
+
'or build locally with: cd rust && cargo build --release');
|
|
97
|
+
}
|
|
98
|
+
// Handle unexpected exit (only emits error if not intentionally stopping)
|
|
99
|
+
this.bridge.on('exit', (code, signal) => {
|
|
100
|
+
if (this.stopping)
|
|
321
101
|
return;
|
|
322
|
-
|
|
323
|
-
this.
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
else {
|
|
349
|
-
nonTlsConnections++;
|
|
350
|
-
}
|
|
351
|
-
if (record.hasKeepAlive) {
|
|
352
|
-
keepAliveConnections++;
|
|
353
|
-
}
|
|
354
|
-
if (record.usingNetworkProxy) {
|
|
355
|
-
httpProxyConnections++;
|
|
356
|
-
}
|
|
357
|
-
maxIncoming = Math.max(maxIncoming, now - record.incomingStartTime);
|
|
358
|
-
if (record.outgoingStartTime) {
|
|
359
|
-
maxOutgoing = Math.max(maxOutgoing, now - record.outgoingStartTime);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
// Get termination stats
|
|
363
|
-
const terminationStats = this.connectionManager.getTerminationStats();
|
|
364
|
-
// Log detailed stats
|
|
365
|
-
logger.log('info', 'Connection statistics', {
|
|
366
|
-
activeConnections: connectionRecords.size,
|
|
367
|
-
tls: {
|
|
368
|
-
total: tlsConnections,
|
|
369
|
-
completed: completedTlsHandshakes,
|
|
370
|
-
pending: pendingTlsHandshakes
|
|
371
|
-
},
|
|
372
|
-
nonTls: nonTlsConnections,
|
|
373
|
-
keepAlive: keepAliveConnections,
|
|
374
|
-
httpProxy: httpProxyConnections,
|
|
375
|
-
longestRunning: {
|
|
376
|
-
incoming: plugins.prettyMs(maxIncoming),
|
|
377
|
-
outgoing: plugins.prettyMs(maxOutgoing)
|
|
378
|
-
},
|
|
379
|
-
terminationStats: {
|
|
380
|
-
incoming: terminationStats.incoming,
|
|
381
|
-
outgoing: terminationStats.outgoing
|
|
382
|
-
},
|
|
383
|
-
component: 'connection-manager'
|
|
384
|
-
});
|
|
385
|
-
}, this.settings.inactivityCheckInterval || 60000);
|
|
386
|
-
// Make sure the interval doesn't keep the process alive
|
|
387
|
-
if (this.connectionLogger.unref) {
|
|
388
|
-
this.connectionLogger.unref();
|
|
389
|
-
}
|
|
102
|
+
logger.log('error', `RustProxy exited unexpectedly (code=${code}, signal=${signal})`, { component: 'smart-proxy' });
|
|
103
|
+
this.emit('error', new Error(`RustProxy exited (code=${code}, signal=${signal})`));
|
|
104
|
+
});
|
|
105
|
+
// Check if any routes need TS-side handling (socket handlers, dynamic functions)
|
|
106
|
+
const hasHandlerRoutes = this.settings.routes.some((r) => (r.action.type === 'socket-handler' && r.action.socketHandler) ||
|
|
107
|
+
r.action.targets?.some((t) => typeof t.host === 'function' || typeof t.port === 'function'));
|
|
108
|
+
// Start socket handler relay server (but don't tell Rust yet - proxy not started)
|
|
109
|
+
if (hasHandlerRoutes) {
|
|
110
|
+
this.socketHandlerServer = new SocketHandlerServer(this.preprocessor);
|
|
111
|
+
await this.socketHandlerServer.start();
|
|
112
|
+
}
|
|
113
|
+
// Preprocess routes (strip JS functions, convert socket-handler routes)
|
|
114
|
+
const rustRoutes = this.preprocessor.preprocessForRust(this.settings.routes);
|
|
115
|
+
// Build Rust config
|
|
116
|
+
const config = this.buildRustConfig(rustRoutes);
|
|
117
|
+
// Start the Rust proxy
|
|
118
|
+
await this.bridge.startProxy(config);
|
|
119
|
+
// Now that Rust proxy is running, configure socket handler relay
|
|
120
|
+
if (this.socketHandlerServer) {
|
|
121
|
+
await this.bridge.setSocketHandlerRelay(this.socketHandlerServer.getSocketPath());
|
|
122
|
+
}
|
|
123
|
+
// Handle certProvisionFunction
|
|
124
|
+
await this.provisionCertificatesViaCallback();
|
|
125
|
+
// Start metrics polling
|
|
126
|
+
this.metricsAdapter.startPolling();
|
|
127
|
+
logger.log('info', 'SmartProxy started (Rust engine)', { component: 'smart-proxy' });
|
|
390
128
|
}
|
|
391
129
|
/**
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
* Note: This method has been removed as we now work directly with routes
|
|
395
|
-
*/
|
|
396
|
-
/**
|
|
397
|
-
* Stop the proxy server
|
|
130
|
+
* Stop the proxy.
|
|
398
131
|
*/
|
|
399
132
|
async stop() {
|
|
400
|
-
logger.log('info', 'SmartProxy shutting down...');
|
|
401
|
-
this.
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
// Stop NFTablesManager
|
|
409
|
-
await this.nftablesManager.stop();
|
|
410
|
-
logger.log('info', 'NFTablesManager stopped');
|
|
411
|
-
// Stop the connection logger
|
|
412
|
-
if (this.connectionLogger) {
|
|
413
|
-
clearInterval(this.connectionLogger);
|
|
414
|
-
this.connectionLogger = null;
|
|
415
|
-
}
|
|
416
|
-
// Stop all port listeners
|
|
417
|
-
await this.portManager.closeAll();
|
|
418
|
-
logger.log('info', 'All servers closed. Cleaning up active connections...');
|
|
419
|
-
// Clean up all active connections
|
|
420
|
-
await this.connectionManager.clearConnections();
|
|
421
|
-
// Stop HttpProxy
|
|
422
|
-
await this.httpProxyBridge.stop();
|
|
423
|
-
// Clear ACME state manager
|
|
424
|
-
this.acmeStateManager.clear();
|
|
425
|
-
// Stop metrics collector
|
|
426
|
-
this.metricsCollector.stop();
|
|
427
|
-
// Clean up ProtocolDetector singleton
|
|
428
|
-
const detection = await import('../../detection/index.js');
|
|
429
|
-
detection.ProtocolDetector.destroy();
|
|
430
|
-
// Flush any pending deduplicated logs
|
|
431
|
-
connectionLogDeduplicator.flushAll();
|
|
432
|
-
logger.log('info', 'SmartProxy shutdown complete.');
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Updates the domain configurations for the proxy
|
|
436
|
-
*
|
|
437
|
-
* Note: This legacy method has been removed. Use updateRoutes instead.
|
|
438
|
-
*/
|
|
439
|
-
async updateDomainConfigs() {
|
|
440
|
-
logger.log('warn', 'Method updateDomainConfigs() is deprecated. Use updateRoutes() instead.');
|
|
441
|
-
throw new Error('updateDomainConfigs() is deprecated - use updateRoutes() instead');
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Verify the challenge route has been properly removed from routes
|
|
445
|
-
*/
|
|
446
|
-
async verifyChallengeRouteRemoved() {
|
|
447
|
-
const maxRetries = 10;
|
|
448
|
-
const retryDelay = 100; // milliseconds
|
|
449
|
-
for (let i = 0; i < maxRetries; i++) {
|
|
450
|
-
// Check if the challenge route is still in the active routes
|
|
451
|
-
const challengeRouteExists = this.settings.routes.some(r => r.name === 'acme-challenge');
|
|
452
|
-
if (!challengeRouteExists) {
|
|
453
|
-
try {
|
|
454
|
-
logger.log('info', 'Challenge route successfully removed from routes');
|
|
455
|
-
}
|
|
456
|
-
catch (error) {
|
|
457
|
-
// Silently handle logging errors
|
|
458
|
-
console.log('[INFO] Challenge route successfully removed from routes');
|
|
459
|
-
}
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
// Wait before retrying
|
|
463
|
-
await plugins.smartdelay.delayFor(retryDelay);
|
|
464
|
-
}
|
|
465
|
-
const error = `Failed to verify challenge route removal after ${maxRetries} attempts`;
|
|
133
|
+
logger.log('info', 'SmartProxy shutting down...', { component: 'smart-proxy' });
|
|
134
|
+
this.stopping = true;
|
|
135
|
+
// Stop metrics polling
|
|
136
|
+
this.metricsAdapter.stopPolling();
|
|
137
|
+
// Remove exit listener before killing to avoid spurious error events
|
|
138
|
+
this.bridge.removeAllListeners('exit');
|
|
139
|
+
// Stop Rust proxy
|
|
466
140
|
try {
|
|
467
|
-
|
|
141
|
+
await this.bridge.stopProxy();
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Ignore if already stopped
|
|
468
145
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
146
|
+
this.bridge.kill();
|
|
147
|
+
// Stop socket handler relay
|
|
148
|
+
if (this.socketHandlerServer) {
|
|
149
|
+
await this.socketHandlerServer.stop();
|
|
150
|
+
this.socketHandlerServer = null;
|
|
472
151
|
}
|
|
473
|
-
|
|
152
|
+
logger.log('info', 'SmartProxy shutdown complete.', { component: 'smart-proxy' });
|
|
474
153
|
}
|
|
475
154
|
/**
|
|
476
|
-
* Update routes
|
|
477
|
-
*
|
|
478
|
-
* This method replaces the current route configuration with the provided routes.
|
|
479
|
-
* It also provisions certificates for routes that require TLS termination and have
|
|
480
|
-
* `certificate: 'auto'` set in their TLS configuration.
|
|
481
|
-
*
|
|
482
|
-
* @param newRoutes Array of route configurations to use
|
|
483
|
-
*
|
|
484
|
-
* Example:
|
|
485
|
-
* ```ts
|
|
486
|
-
* proxy.updateRoutes([
|
|
487
|
-
* {
|
|
488
|
-
* match: { ports: 443, domains: 'secure.example.com' },
|
|
489
|
-
* action: {
|
|
490
|
-
* type: 'forward',
|
|
491
|
-
* target: { host: '10.0.0.1', port: 8443 },
|
|
492
|
-
* tls: { mode: 'terminate', certificate: 'auto' }
|
|
493
|
-
* }
|
|
494
|
-
* }
|
|
495
|
-
* ]);
|
|
496
|
-
* ```
|
|
155
|
+
* Update routes atomically.
|
|
497
156
|
*/
|
|
498
157
|
async updateRoutes(newRoutes) {
|
|
499
158
|
return this.routeUpdateLock.runExclusive(async () => {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
});
|
|
159
|
+
// Validate
|
|
160
|
+
const validation = RouteValidator.validateRoutes(newRoutes);
|
|
161
|
+
if (!validation.valid) {
|
|
162
|
+
RouteValidator.logValidationErrors(validation.errors);
|
|
163
|
+
throw new Error(`Route validation failed: ${validation.errors.size} route(s) have errors`);
|
|
505
164
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
165
|
+
// Preprocess for Rust
|
|
166
|
+
const rustRoutes = this.preprocessor.preprocessForRust(newRoutes);
|
|
167
|
+
// Send to Rust
|
|
168
|
+
await this.bridge.updateRoutes(rustRoutes);
|
|
169
|
+
// Update local route manager
|
|
170
|
+
this.routeManager.updateRoutes(newRoutes);
|
|
171
|
+
// Update socket handler relay if handler routes changed
|
|
172
|
+
const hasHandlerRoutes = newRoutes.some((r) => (r.action.type === 'socket-handler' && r.action.socketHandler) ||
|
|
173
|
+
r.action.targets?.some((t) => typeof t.host === 'function' || typeof t.port === 'function'));
|
|
174
|
+
if (hasHandlerRoutes && !this.socketHandlerServer) {
|
|
175
|
+
this.socketHandlerServer = new SocketHandlerServer(this.preprocessor);
|
|
176
|
+
await this.socketHandlerServer.start();
|
|
177
|
+
await this.bridge.setSocketHandlerRelay(this.socketHandlerServer.getSocketPath());
|
|
509
178
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
this.
|
|
179
|
+
else if (!hasHandlerRoutes && this.socketHandlerServer) {
|
|
180
|
+
await this.socketHandlerServer.stop();
|
|
181
|
+
this.socketHandlerServer = null;
|
|
513
182
|
}
|
|
514
|
-
//
|
|
515
|
-
const updateResult = await this.routeOrchestrator.updateRoutes(this.settings.routes, newRoutes, {
|
|
516
|
-
acmePort: this.settings.acme?.port || 80,
|
|
517
|
-
acmeOptions: this.certManager?.getAcmeOptions(),
|
|
518
|
-
acmeState: this.certManager?.getState(),
|
|
519
|
-
globalChallengeRouteActive: this.globalChallengeRouteActive,
|
|
520
|
-
createCertificateManager: this.createCertificateManager.bind(this),
|
|
521
|
-
verifyChallengeRouteRemoved: this.verifyChallengeRouteRemoved.bind(this)
|
|
522
|
-
});
|
|
523
|
-
// Update settings with the new routes
|
|
183
|
+
// Update stored routes
|
|
524
184
|
this.settings.routes = newRoutes;
|
|
525
|
-
//
|
|
526
|
-
this.
|
|
527
|
-
|
|
528
|
-
this.portUsageMap = updateResult.portUsageMap;
|
|
529
|
-
// If certificate manager was recreated, update our reference
|
|
530
|
-
if (updateResult.newCertManager) {
|
|
531
|
-
this.certManager = updateResult.newCertManager;
|
|
532
|
-
// Update the orchestrator's reference too
|
|
533
|
-
this.routeOrchestrator.setCertManager(this.certManager);
|
|
534
|
-
}
|
|
185
|
+
// Handle cert provisioning for new routes
|
|
186
|
+
await this.provisionCertificatesViaCallback();
|
|
187
|
+
logger.log('info', `Routes updated (${newRoutes.length} routes)`, { component: 'smart-proxy' });
|
|
535
188
|
});
|
|
536
189
|
}
|
|
537
190
|
/**
|
|
538
|
-
*
|
|
191
|
+
* Provision a certificate for a named route.
|
|
539
192
|
*/
|
|
540
193
|
async provisionCertificate(routeName) {
|
|
541
|
-
|
|
542
|
-
throw new Error('Certificate manager not initialized');
|
|
543
|
-
}
|
|
544
|
-
const route = this.settings.routes.find(r => r.name === routeName);
|
|
545
|
-
if (!route) {
|
|
546
|
-
throw new Error(`Route ${routeName} not found`);
|
|
547
|
-
}
|
|
548
|
-
await this.certManager.provisionCertificate(route);
|
|
194
|
+
await this.bridge.provisionCertificate(routeName);
|
|
549
195
|
}
|
|
550
|
-
// Port usage tracking methods moved to RouteOrchestrator
|
|
551
196
|
/**
|
|
552
|
-
* Force renewal of a certificate
|
|
197
|
+
* Force renewal of a certificate.
|
|
553
198
|
*/
|
|
554
199
|
async renewCertificate(routeName) {
|
|
555
|
-
|
|
556
|
-
throw new Error('Certificate manager not initialized');
|
|
557
|
-
}
|
|
558
|
-
await this.certManager.renewCertificate(routeName);
|
|
200
|
+
await this.bridge.renewCertificate(routeName);
|
|
559
201
|
}
|
|
560
202
|
/**
|
|
561
|
-
* Get certificate status for a route
|
|
203
|
+
* Get certificate status for a route (async - calls Rust).
|
|
562
204
|
*/
|
|
563
|
-
getCertificateStatus(routeName) {
|
|
564
|
-
|
|
565
|
-
return undefined;
|
|
566
|
-
}
|
|
567
|
-
return this.certManager.getCertificateStatus(routeName);
|
|
205
|
+
async getCertificateStatus(routeName) {
|
|
206
|
+
return this.bridge.getCertificateStatus(routeName);
|
|
568
207
|
}
|
|
569
208
|
/**
|
|
570
|
-
* Get
|
|
571
|
-
*
|
|
572
|
-
* @returns IMetrics interface with grouped metrics methods
|
|
209
|
+
* Get the metrics interface.
|
|
573
210
|
*/
|
|
574
211
|
getMetrics() {
|
|
575
|
-
return this.
|
|
212
|
+
return this.metricsAdapter;
|
|
576
213
|
}
|
|
577
214
|
/**
|
|
578
|
-
*
|
|
215
|
+
* Get statistics (async - calls Rust).
|
|
579
216
|
*/
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
if (!domain || domain.length === 0) {
|
|
583
|
-
return false;
|
|
584
|
-
}
|
|
585
|
-
// Check for wildcard domains (they can't get ACME certs)
|
|
586
|
-
if (domain.includes('*')) {
|
|
587
|
-
logger.log('warn', `Wildcard domains like "${domain}" are not supported for automatic ACME certificates`, { domain, component: 'certificate-manager' });
|
|
588
|
-
return false;
|
|
589
|
-
}
|
|
590
|
-
// Check if domain has at least one dot and no invalid characters
|
|
591
|
-
const validDomainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
592
|
-
if (!validDomainRegex.test(domain)) {
|
|
593
|
-
logger.log('warn', `Domain "${domain}" has invalid format for certificate issuance`, { domain, component: 'certificate-manager' });
|
|
594
|
-
return false;
|
|
595
|
-
}
|
|
596
|
-
return true;
|
|
217
|
+
async getStatistics() {
|
|
218
|
+
return this.bridge.getStatistics();
|
|
597
219
|
}
|
|
598
220
|
/**
|
|
599
|
-
* Add a
|
|
600
|
-
*
|
|
601
|
-
* This allows you to add a port listener without updating routes.
|
|
602
|
-
* Useful for preparing to listen on a port before adding routes for it.
|
|
603
|
-
*
|
|
604
|
-
* @param port The port to start listening on
|
|
605
|
-
* @returns Promise that resolves when the port is listening
|
|
221
|
+
* Add a listening port at runtime.
|
|
606
222
|
*/
|
|
607
223
|
async addListeningPort(port) {
|
|
608
|
-
|
|
224
|
+
await this.bridge.addListeningPort(port);
|
|
609
225
|
}
|
|
610
226
|
/**
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
* This allows you to stop a port listener without updating routes.
|
|
614
|
-
* Useful for temporary maintenance or port changes.
|
|
615
|
-
*
|
|
616
|
-
* @param port The port to stop listening on
|
|
617
|
-
* @returns Promise that resolves when the port is closed
|
|
227
|
+
* Remove a listening port at runtime.
|
|
618
228
|
*/
|
|
619
229
|
async removeListeningPort(port) {
|
|
620
|
-
|
|
230
|
+
await this.bridge.removeListeningPort(port);
|
|
621
231
|
}
|
|
622
232
|
/**
|
|
623
|
-
* Get
|
|
624
|
-
*
|
|
625
|
-
* @returns Array of port numbers
|
|
233
|
+
* Get all currently listening ports (async - calls Rust).
|
|
626
234
|
*/
|
|
627
|
-
getListeningPorts() {
|
|
628
|
-
|
|
235
|
+
async getListeningPorts() {
|
|
236
|
+
if (!this.bridge.running)
|
|
237
|
+
return [];
|
|
238
|
+
return this.bridge.getListeningPorts();
|
|
629
239
|
}
|
|
630
240
|
/**
|
|
631
|
-
* Get
|
|
632
|
-
*/
|
|
633
|
-
getStatistics() {
|
|
634
|
-
const connectionRecords = this.connectionManager.getConnections();
|
|
635
|
-
const terminationStats = this.connectionManager.getTerminationStats();
|
|
636
|
-
let tlsConnections = 0;
|
|
637
|
-
let nonTlsConnections = 0;
|
|
638
|
-
let keepAliveConnections = 0;
|
|
639
|
-
let httpProxyConnections = 0;
|
|
640
|
-
// Analyze active connections
|
|
641
|
-
for (const record of connectionRecords.values()) {
|
|
642
|
-
if (record.isTLS)
|
|
643
|
-
tlsConnections++;
|
|
644
|
-
else
|
|
645
|
-
nonTlsConnections++;
|
|
646
|
-
if (record.hasKeepAlive)
|
|
647
|
-
keepAliveConnections++;
|
|
648
|
-
if (record.usingNetworkProxy)
|
|
649
|
-
httpProxyConnections++;
|
|
650
|
-
}
|
|
651
|
-
return {
|
|
652
|
-
activeConnections: connectionRecords.size,
|
|
653
|
-
tlsConnections,
|
|
654
|
-
nonTlsConnections,
|
|
655
|
-
keepAliveConnections,
|
|
656
|
-
httpProxyConnections,
|
|
657
|
-
terminationStats,
|
|
658
|
-
acmeEnabled: !!this.certManager,
|
|
659
|
-
port80HandlerPort: this.certManager ? 80 : null,
|
|
660
|
-
routeCount: this.settings.routes.length,
|
|
661
|
-
activePorts: this.portManager.getListeningPorts().length,
|
|
662
|
-
listeningPorts: this.portManager.getListeningPorts()
|
|
663
|
-
};
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* Get a list of eligible domains for ACME certificates
|
|
241
|
+
* Get eligible domains for ACME certificates (sync - reads local routes).
|
|
667
242
|
*/
|
|
668
243
|
getEligibleDomainsForCertificates() {
|
|
669
244
|
const domains = [];
|
|
670
|
-
|
|
671
|
-
const routes = this.settings.routes || [];
|
|
672
|
-
for (const route of routes) {
|
|
245
|
+
for (const route of this.settings.routes || []) {
|
|
673
246
|
if (!route.match.domains)
|
|
674
247
|
continue;
|
|
675
|
-
// Skip routes without TLS termination or auto certificates
|
|
676
248
|
if (route.action.type !== 'forward' ||
|
|
677
249
|
!route.action.tls ||
|
|
678
250
|
route.action.tls.mode === 'passthrough' ||
|
|
679
251
|
route.action.tls.certificate !== 'auto')
|
|
680
252
|
continue;
|
|
681
|
-
const routeDomains = Array.isArray(route.match.domains)
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
// Skip domains that can't be used with ACME
|
|
685
|
-
const eligibleDomains = routeDomains.filter(domain => !domain.includes('*') && this.isValidDomain(domain));
|
|
686
|
-
domains.push(...eligibleDomains);
|
|
253
|
+
const routeDomains = Array.isArray(route.match.domains) ? route.match.domains : [route.match.domains];
|
|
254
|
+
const eligible = routeDomains.filter((d) => !d.includes('*') && this.isValidDomain(d));
|
|
255
|
+
domains.push(...eligible);
|
|
687
256
|
}
|
|
688
|
-
// Legacy mode is no longer supported
|
|
689
257
|
return domains;
|
|
690
258
|
}
|
|
691
259
|
/**
|
|
692
|
-
* Get NFTables status
|
|
260
|
+
* Get NFTables status (async - calls Rust).
|
|
693
261
|
*/
|
|
694
262
|
async getNfTablesStatus() {
|
|
695
|
-
return this.
|
|
263
|
+
return this.bridge.getNftablesStatus();
|
|
696
264
|
}
|
|
265
|
+
// --- Private helpers ---
|
|
697
266
|
/**
|
|
698
|
-
*
|
|
267
|
+
* Build the Rust configuration object from TS settings.
|
|
699
268
|
*/
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
269
|
+
buildRustConfig(routes) {
|
|
270
|
+
return {
|
|
271
|
+
routes,
|
|
272
|
+
defaults: this.settings.defaults,
|
|
273
|
+
acme: this.settings.acme
|
|
274
|
+
? {
|
|
275
|
+
enabled: this.settings.acme.enabled,
|
|
276
|
+
email: this.settings.acme.email,
|
|
277
|
+
useProduction: this.settings.acme.useProduction,
|
|
278
|
+
port: this.settings.acme.port,
|
|
279
|
+
renewThresholdDays: this.settings.acme.renewThresholdDays,
|
|
280
|
+
autoRenew: this.settings.acme.autoRenew,
|
|
281
|
+
certificateStore: this.settings.acme.certificateStore,
|
|
282
|
+
renewCheckIntervalHours: this.settings.acme.renewCheckIntervalHours,
|
|
283
|
+
}
|
|
284
|
+
: undefined,
|
|
285
|
+
connectionTimeout: this.settings.connectionTimeout,
|
|
286
|
+
initialDataTimeout: this.settings.initialDataTimeout,
|
|
287
|
+
socketTimeout: this.settings.socketTimeout,
|
|
288
|
+
maxConnectionLifetime: this.settings.maxConnectionLifetime,
|
|
289
|
+
gracefulShutdownTimeout: this.settings.gracefulShutdownTimeout,
|
|
290
|
+
maxConnectionsPerIp: this.settings.maxConnectionsPerIP,
|
|
291
|
+
connectionRateLimitPerMinute: this.settings.connectionRateLimitPerMinute,
|
|
292
|
+
keepAliveTreatment: this.settings.keepAliveTreatment,
|
|
293
|
+
keepAliveInactivityMultiplier: this.settings.keepAliveInactivityMultiplier,
|
|
294
|
+
extendedKeepAliveLifetime: this.settings.extendedKeepAliveLifetime,
|
|
295
|
+
acceptProxyProtocol: this.settings.acceptProxyProtocol,
|
|
296
|
+
sendProxyProtocol: this.settings.sendProxyProtocol,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* For routes with certificate: 'auto', call certProvisionFunction if set.
|
|
301
|
+
* If the callback returns a cert object, load it into Rust.
|
|
302
|
+
* If it returns 'http01', let Rust handle ACME.
|
|
303
|
+
*/
|
|
304
|
+
async provisionCertificatesViaCallback() {
|
|
305
|
+
const provisionFn = this.settings.certProvisionFunction;
|
|
306
|
+
if (!provisionFn)
|
|
307
|
+
return;
|
|
308
|
+
for (const route of this.settings.routes) {
|
|
309
|
+
if (route.action.tls?.certificate !== 'auto')
|
|
310
|
+
continue;
|
|
311
|
+
if (!route.match.domains)
|
|
312
|
+
continue;
|
|
313
|
+
const domains = Array.isArray(route.match.domains) ? route.match.domains : [route.match.domains];
|
|
314
|
+
for (const domain of domains) {
|
|
315
|
+
if (domain.includes('*'))
|
|
316
|
+
continue;
|
|
317
|
+
try {
|
|
318
|
+
const result = await provisionFn(domain);
|
|
319
|
+
if (result === 'http01') {
|
|
320
|
+
// Rust handles ACME for this domain
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
// Got a static cert object - load it into Rust
|
|
324
|
+
if (result && typeof result === 'object') {
|
|
325
|
+
const certObj = result;
|
|
326
|
+
await this.bridge.loadCertificate(domain, certObj.publicKey, certObj.privateKey);
|
|
327
|
+
logger.log('info', `Certificate loaded via provision function for ${domain}`, { component: 'smart-proxy' });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
catch (err) {
|
|
331
|
+
logger.log('warn', `certProvisionFunction failed for ${domain}: ${err.message}`, { component: 'smart-proxy' });
|
|
332
|
+
// Fallback to ACME if enabled
|
|
333
|
+
if (this.settings.certProvisionFallbackToAcme !== false) {
|
|
334
|
+
logger.log('info', `Falling back to ACME for ${domain}`, { component: 'smart-proxy' });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
741
337
|
}
|
|
742
338
|
}
|
|
743
|
-
|
|
339
|
+
}
|
|
340
|
+
isValidDomain(domain) {
|
|
341
|
+
if (!domain || domain.length === 0)
|
|
342
|
+
return false;
|
|
343
|
+
if (domain.includes('*'))
|
|
344
|
+
return false;
|
|
345
|
+
const validDomainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
346
|
+
return validDomainRegex.test(domain);
|
|
744
347
|
}
|
|
745
348
|
}
|
|
746
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnQtcHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3NtYXJ0LXByb3h5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BELE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBRWpGLGdDQUFnQztBQUNoQyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDeEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN6RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxrQkFBa0IsSUFBSSxZQUFZLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUN6RixPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUN2RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFeEQsc0JBQXNCO0FBQ3RCLE9BQU8sRUFBRSxnQkFBZ0IsRUFBb0IsTUFBTSwwQkFBMEIsQ0FBQztBQVE5RSxnREFBZ0Q7QUFDaEQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRXpDLHlCQUF5QjtBQUN6QixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFNUQsaURBQWlEO0FBQ2pELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHlCQUF5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
|
|
349
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnQtcHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL3NtYXJ0LXByb3h5L3NtYXJ0LXByb3h5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXBELDBCQUEwQjtBQUMxQixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDekQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDN0QsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDakUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFL0QsbUJBQW1CO0FBQ25CLE9BQU8sRUFBRSxrQkFBa0IsSUFBSSxZQUFZLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUN6RixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDNUQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBT3pDOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sT0FBTyxVQUFXLFNBQVEsT0FBTyxDQUFDLFlBQVk7SUFXbEQsWUFBWSxXQUErQjtRQUN6QyxLQUFLLEVBQUUsQ0FBQztRQU5GLHdCQUFtQixHQUErQixJQUFJLENBQUM7UUFHdkQsYUFBUSxHQUFHLEtBQUssQ0FBQztRQUt2QixpQkFBaUI7UUFDakIsSUFBSSxDQUFDLFFBQVEsR0FBRztZQUNkLEdBQUcsV0FBVztZQUNkLGtCQUFrQixFQUFFLFdBQVcsQ0FBQyxrQkFBa0IsSUFBSSxNQUFNO1lBQzVELGFBQWEsRUFBRSxXQUFXLENBQUMsYUFBYSxJQUFJLE9BQU87WUFD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
|