@push.rocks/smartproxy 18.0.2 → 18.2.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 (53) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/certificate/certificate-manager.d.ts +150 -0
  3. package/dist_ts/certificate/certificate-manager.js +505 -0
  4. package/dist_ts/certificate/events/simplified-events.d.ts +56 -0
  5. package/dist_ts/certificate/events/simplified-events.js +13 -0
  6. package/dist_ts/certificate/models/certificate-errors.d.ts +69 -0
  7. package/dist_ts/certificate/models/certificate-errors.js +141 -0
  8. package/dist_ts/certificate/models/certificate-strategy.d.ts +60 -0
  9. package/dist_ts/certificate/models/certificate-strategy.js +73 -0
  10. package/dist_ts/certificate/simplified-certificate-manager.d.ts +150 -0
  11. package/dist_ts/certificate/simplified-certificate-manager.js +501 -0
  12. package/dist_ts/http/index.d.ts +1 -9
  13. package/dist_ts/http/index.js +5 -11
  14. package/dist_ts/plugins.d.ts +3 -1
  15. package/dist_ts/plugins.js +4 -2
  16. package/dist_ts/proxies/network-proxy/network-proxy.js +3 -1
  17. package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.d.ts +48 -0
  18. package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.js +76 -0
  19. package/dist_ts/proxies/network-proxy/websocket-handler.js +41 -4
  20. package/dist_ts/proxies/smart-proxy/cert-store.d.ts +10 -0
  21. package/dist_ts/proxies/smart-proxy/cert-store.js +70 -0
  22. package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +116 -0
  23. package/dist_ts/proxies/smart-proxy/certificate-manager.js +401 -0
  24. package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.d.ts +168 -0
  25. package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.js +642 -0
  26. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +26 -0
  27. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  28. package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.d.ts +65 -0
  29. package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.js +31 -0
  30. package/dist_ts/proxies/smart-proxy/models/smartproxy-options.d.ts +102 -0
  31. package/dist_ts/proxies/smart-proxy/models/smartproxy-options.js +73 -0
  32. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +10 -44
  33. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +66 -202
  34. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +4 -0
  35. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +62 -2
  36. package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.d.ts +41 -0
  37. package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.js +132 -0
  38. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +18 -13
  39. package/dist_ts/proxies/smart-proxy/smart-proxy.js +79 -196
  40. package/package.json +7 -5
  41. package/readme.md +224 -10
  42. package/readme.plan.md +1405 -617
  43. package/ts/00_commitinfo_data.ts +1 -1
  44. package/ts/http/index.ts +5 -12
  45. package/ts/plugins.ts +4 -1
  46. package/ts/proxies/network-proxy/network-proxy.ts +3 -0
  47. package/ts/proxies/network-proxy/websocket-handler.ts +38 -3
  48. package/ts/proxies/smart-proxy/cert-store.ts +86 -0
  49. package/ts/proxies/smart-proxy/certificate-manager.ts +506 -0
  50. package/ts/proxies/smart-proxy/models/route-types.ts +33 -3
  51. package/ts/proxies/smart-proxy/network-proxy-bridge.ts +86 -239
  52. package/ts/proxies/smart-proxy/route-connection-handler.ts +74 -1
  53. package/ts/proxies/smart-proxy/smart-proxy.ts +105 -222
@@ -1,46 +1,18 @@
1
1
  import * as plugins from '../../plugins.js';
2
2
  import { NetworkProxy } from '../network-proxy/index.js';
3
- import { Port80Handler } from '../../http/port80/port80-handler.js';
4
- import { subscribeToPort80Handler } from '../../core/utils/event-utils.js';
5
- import type { ICertificateData } from '../../certificate/models/certificate-types.js';
6
3
  import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
7
4
  import type { IRouteConfig } from './models/route-types.js';
8
5
 
9
- /**
10
- * Manages NetworkProxy integration for TLS termination
11
- *
12
- * NetworkProxyBridge connects SmartProxy with NetworkProxy to handle TLS termination.
13
- * It directly passes route configurations to NetworkProxy and manages the physical
14
- * connection piping between SmartProxy and NetworkProxy for TLS termination.
15
- *
16
- * It is used by SmartProxy for routes that have:
17
- * - TLS mode of 'terminate' or 'terminate-and-reencrypt'
18
- * - Certificate set to 'auto' or custom certificate
19
- */
20
6
  export class NetworkProxyBridge {
21
7
  private networkProxy: NetworkProxy | null = null;
22
- private port80Handler: Port80Handler | null = null;
23
8
 
24
9
  constructor(private settings: ISmartProxyOptions) {}
25
10
 
26
11
  /**
27
- * Set the Port80Handler to use for certificate management
12
+ * Get the NetworkProxy instance
28
13
  */
29
- public setPort80Handler(handler: Port80Handler): void {
30
- this.port80Handler = handler;
31
-
32
- // Subscribe to certificate events
33
- subscribeToPort80Handler(handler, {
34
- onCertificateIssued: this.handleCertificateEvent.bind(this),
35
- onCertificateRenewed: this.handleCertificateEvent.bind(this)
36
- });
37
-
38
- // If NetworkProxy is already initialized, connect it with Port80Handler
39
- if (this.networkProxy) {
40
- this.networkProxy.setExternalPort80Handler(handler);
41
- }
42
-
43
- console.log('Port80Handler connected to NetworkProxyBridge');
14
+ public getNetworkProxy(): NetworkProxy | null {
15
+ return this.networkProxy;
44
16
  }
45
17
 
46
18
  /**
@@ -48,22 +20,14 @@ export class NetworkProxyBridge {
48
20
  */
49
21
  public async initialize(): Promise<void> {
50
22
  if (!this.networkProxy && this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0) {
51
- // Configure NetworkProxy options based on SmartProxy settings
52
23
  const networkProxyOptions: any = {
53
24
  port: this.settings.networkProxyPort!,
54
25
  portProxyIntegration: true,
55
- logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info',
56
- useExternalPort80Handler: !!this.port80Handler // Use Port80Handler if available
26
+ logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info'
57
27
  };
58
28
 
59
29
  this.networkProxy = new NetworkProxy(networkProxyOptions);
60
-
61
30
  console.log(`Initialized NetworkProxy on port ${this.settings.networkProxyPort}`);
62
-
63
- // Connect Port80Handler if available
64
- if (this.port80Handler) {
65
- this.networkProxy.setExternalPort80Handler(this.port80Handler);
66
- }
67
31
 
68
32
  // Apply route configurations to NetworkProxy
69
33
  await this.syncRoutesToNetworkProxy(this.settings.routes || []);
@@ -71,235 +35,118 @@ export class NetworkProxyBridge {
71
35
  }
72
36
 
73
37
  /**
74
- * Handle certificate issuance or renewal events
38
+ * Sync routes to NetworkProxy
75
39
  */
76
- private handleCertificateEvent(data: ICertificateData): void {
40
+ public async syncRoutesToNetworkProxy(routes: IRouteConfig[]): Promise<void> {
77
41
  if (!this.networkProxy) return;
78
-
79
- console.log(`Received certificate for ${data.domain} from Port80Handler, updating NetworkProxy`);
80
-
81
- // Apply certificate directly to NetworkProxy
82
- this.networkProxy.updateCertificate(data.domain, data.certificate, data.privateKey);
83
- }
84
-
85
- /**
86
- * Apply an external (static) certificate into NetworkProxy
87
- */
88
- public applyExternalCertificate(data: ICertificateData): void {
89
- if (!this.networkProxy) {
90
- console.log(`NetworkProxy not initialized: cannot apply external certificate for ${data.domain}`);
91
- return;
92
- }
93
42
 
94
- // Apply certificate directly to NetworkProxy
95
- this.networkProxy.updateCertificate(data.domain, data.certificate, data.privateKey);
96
- }
97
-
98
- /**
99
- * Get the NetworkProxy instance
100
- */
101
- public getNetworkProxy(): NetworkProxy | null {
102
- return this.networkProxy;
103
- }
104
-
105
- /**
106
- * Get the NetworkProxy port
107
- */
108
- public getNetworkProxyPort(): number {
109
- return this.networkProxy ? this.networkProxy.getListeningPort() : this.settings.networkProxyPort || 8443;
43
+ // Convert routes to NetworkProxy format
44
+ const networkProxyConfigs = routes
45
+ .filter(route => {
46
+ // Check if this route matches any of the specified network proxy ports
47
+ const routePorts = Array.isArray(route.match.ports)
48
+ ? route.match.ports
49
+ : [route.match.ports];
50
+
51
+ return routePorts.some(port =>
52
+ this.settings.useNetworkProxy?.includes(port)
53
+ );
54
+ })
55
+ .map(route => this.routeToNetworkProxyConfig(route));
56
+
57
+ // Apply configurations to NetworkProxy
58
+ await this.networkProxy.updateRouteConfigs(networkProxyConfigs);
110
59
  }
111
60
 
112
61
  /**
113
- * Start NetworkProxy
62
+ * Convert route to NetworkProxy configuration
114
63
  */
115
- public async start(): Promise<void> {
116
- if (this.networkProxy) {
117
- await this.networkProxy.start();
118
- console.log(`NetworkProxy started on port ${this.settings.networkProxyPort}`);
119
- }
64
+ private routeToNetworkProxyConfig(route: IRouteConfig): any {
65
+ // Convert route to NetworkProxy domain config format
66
+ return {
67
+ domain: route.match.domains?.[0] || '*',
68
+ target: route.action.target,
69
+ tls: route.action.tls,
70
+ security: route.action.security
71
+ };
120
72
  }
121
73
 
122
74
  /**
123
- * Stop NetworkProxy
75
+ * Check if connection should use NetworkProxy
124
76
  */
125
- public async stop(): Promise<void> {
126
- if (this.networkProxy) {
127
- try {
128
- console.log('Stopping NetworkProxy...');
129
- await this.networkProxy.stop();
130
- console.log('NetworkProxy stopped successfully');
131
- } catch (err) {
132
- console.log(`Error stopping NetworkProxy: ${err}`);
133
- }
134
- }
77
+ public shouldUseNetworkProxy(connection: IConnectionRecord, routeMatch: any): boolean {
78
+ // Only use NetworkProxy for TLS termination
79
+ return (
80
+ routeMatch.route.action.tls?.mode === 'terminate' ||
81
+ routeMatch.route.action.tls?.mode === 'terminate-and-reencrypt'
82
+ ) && this.networkProxy !== null;
135
83
  }
136
84
 
137
85
  /**
138
- * Forwards a TLS connection to a NetworkProxy for handling
86
+ * Forward connection to NetworkProxy
139
87
  */
140
- public forwardToNetworkProxy(
88
+ public async forwardToNetworkProxy(
141
89
  connectionId: string,
142
90
  socket: plugins.net.Socket,
143
91
  record: IConnectionRecord,
144
- initialData: Buffer,
145
- customProxyPort?: number,
146
- onError?: (reason: string) => void
147
- ): void {
148
- // Ensure NetworkProxy is initialized
92
+ initialChunk: Buffer,
93
+ networkProxyPort: number,
94
+ cleanupCallback: (reason: string) => void
95
+ ): Promise<void> {
149
96
  if (!this.networkProxy) {
150
- console.log(
151
- `[${connectionId}] NetworkProxy not initialized. Cannot forward connection.`
152
- );
153
- if (onError) {
154
- onError('network_proxy_not_initialized');
155
- }
156
- return;
157
- }
158
-
159
- // Use the custom port if provided, otherwise use the default NetworkProxy port
160
- const proxyPort = customProxyPort || this.networkProxy.getListeningPort();
161
- const proxyHost = 'localhost'; // Assuming NetworkProxy runs locally
162
-
163
- if (this.settings.enableDetailedLogging) {
164
- console.log(
165
- `[${connectionId}] Forwarding TLS connection to NetworkProxy at ${proxyHost}:${proxyPort}`
166
- );
97
+ throw new Error('NetworkProxy not initialized');
167
98
  }
168
-
169
- // Create a connection to the NetworkProxy
170
- const proxySocket = plugins.net.connect({
171
- host: proxyHost,
172
- port: proxyPort,
173
- });
174
-
175
- // Store the outgoing socket in the record
176
- record.outgoing = proxySocket;
177
- record.outgoingStartTime = Date.now();
178
- record.usingNetworkProxy = true;
179
-
180
- // Set up error handlers
181
- proxySocket.on('error', (err) => {
182
- console.log(`[${connectionId}] Error connecting to NetworkProxy: ${err.message}`);
183
- if (onError) {
184
- onError('network_proxy_connect_error');
185
- }
186
- });
187
-
188
- // Handle connection to NetworkProxy
189
- proxySocket.on('connect', () => {
190
- if (this.settings.enableDetailedLogging) {
191
- console.log(`[${connectionId}] Connected to NetworkProxy at ${proxyHost}:${proxyPort}`);
192
- }
193
-
194
- // First send the initial data that contains the TLS ClientHello
195
- proxySocket.write(initialData);
196
-
197
- // Now set up bidirectional piping between client and NetworkProxy
198
- socket.pipe(proxySocket);
199
- proxySocket.pipe(socket);
200
-
201
- if (this.settings.enableDetailedLogging) {
202
- console.log(`[${connectionId}] TLS connection successfully forwarded to NetworkProxy`);
203
- }
99
+
100
+ const proxySocket = new plugins.net.Socket();
101
+
102
+ await new Promise<void>((resolve, reject) => {
103
+ proxySocket.connect(networkProxyPort, 'localhost', () => {
104
+ console.log(`[${connectionId}] Connected to NetworkProxy for termination`);
105
+ resolve();
106
+ });
107
+
108
+ proxySocket.on('error', reject);
204
109
  });
110
+
111
+ // Send initial chunk if present
112
+ if (initialChunk) {
113
+ proxySocket.write(initialChunk);
114
+ }
115
+
116
+ // Pipe the sockets together
117
+ socket.pipe(proxySocket);
118
+ proxySocket.pipe(socket);
119
+
120
+ // Handle cleanup
121
+ const cleanup = (reason: string) => {
122
+ socket.unpipe(proxySocket);
123
+ proxySocket.unpipe(socket);
124
+ proxySocket.destroy();
125
+ cleanupCallback(reason);
126
+ };
127
+
128
+ socket.on('end', () => cleanup('socket_end'));
129
+ socket.on('error', () => cleanup('socket_error'));
130
+ proxySocket.on('end', () => cleanup('proxy_end'));
131
+ proxySocket.on('error', () => cleanup('proxy_error'));
205
132
  }
206
133
 
207
134
  /**
208
- * Synchronizes routes to NetworkProxy
209
- *
210
- * This method directly passes route configurations to NetworkProxy without any
211
- * intermediate conversion. NetworkProxy natively understands route configurations.
212
- *
213
- * @param routes The route configurations to sync to NetworkProxy
135
+ * Start NetworkProxy
214
136
  */
215
- public async syncRoutesToNetworkProxy(routes: IRouteConfig[]): Promise<void> {
216
- if (!this.networkProxy) {
217
- console.log('Cannot sync configurations - NetworkProxy not initialized');
218
- return;
219
- }
220
-
221
- try {
222
- // Filter only routes that are applicable to NetworkProxy (TLS termination)
223
- const networkProxyRoutes = routes.filter(route => {
224
- return (
225
- route.action.type === 'forward' &&
226
- route.action.tls &&
227
- (route.action.tls.mode === 'terminate' || route.action.tls.mode === 'terminate-and-reencrypt')
228
- );
229
- });
230
-
231
- // Pass routes directly to NetworkProxy
232
- await this.networkProxy.updateRouteConfigs(networkProxyRoutes);
233
- console.log(`Synced ${networkProxyRoutes.length} routes directly to NetworkProxy`);
234
- } catch (err) {
235
- console.log(`Error syncing routes to NetworkProxy: ${err}`);
137
+ public async start(): Promise<void> {
138
+ if (this.networkProxy) {
139
+ await this.networkProxy.start();
236
140
  }
237
141
  }
238
142
 
239
143
  /**
240
- * Request a certificate for a specific domain
241
- *
242
- * @param domain The domain to request a certificate for
243
- * @param routeName Optional route name to associate with this certificate
144
+ * Stop NetworkProxy
244
145
  */
245
- public async requestCertificate(domain: string, routeName?: string): Promise<boolean> {
246
- // Delegate to Port80Handler if available
247
- if (this.port80Handler) {
248
- try {
249
- // Check if the domain is already registered
250
- const cert = this.port80Handler.getCertificate(domain);
251
- if (cert) {
252
- console.log(`Certificate already exists for ${domain}`);
253
- return true;
254
- }
255
-
256
- // Build the domain options
257
- const domainOptions: any = {
258
- domainName: domain,
259
- sslRedirect: true,
260
- acmeMaintenance: true,
261
- };
262
-
263
- // Add route reference if available
264
- if (routeName) {
265
- domainOptions.routeReference = {
266
- routeName
267
- };
268
- }
269
-
270
- // Register the domain for certificate issuance
271
- this.port80Handler.addDomain(domainOptions);
272
-
273
- console.log(`Domain ${domain} registered for certificate issuance`);
274
- return true;
275
- } catch (err) {
276
- console.log(`Error requesting certificate: ${err}`);
277
- return false;
278
- }
279
- }
280
-
281
- // Fall back to NetworkProxy if Port80Handler is not available
282
- if (!this.networkProxy) {
283
- console.log('Cannot request certificate - NetworkProxy not initialized');
284
- return false;
285
- }
286
-
287
- if (!this.settings.acme?.enabled) {
288
- console.log('Cannot request certificate - ACME is not enabled');
289
- return false;
290
- }
291
-
292
- try {
293
- const result = await this.networkProxy.requestCertificate(domain);
294
- if (result) {
295
- console.log(`Certificate request for ${domain} submitted successfully`);
296
- } else {
297
- console.log(`Certificate request for ${domain} failed`);
298
- }
299
- return result;
300
- } catch (err) {
301
- console.log(`Error requesting certificate: ${err}`);
302
- return false;
146
+ public async stop(): Promise<void> {
147
+ if (this.networkProxy) {
148
+ await this.networkProxy.stop();
149
+ this.networkProxy = null;
303
150
  }
304
151
  }
305
152
  }
@@ -365,6 +365,10 @@ export class RouteConnectionHandler {
365
365
  case 'block':
366
366
  return this.handleBlockAction(socket, record, route);
367
367
 
368
+ case 'static':
369
+ this.handleStaticAction(socket, record, route);
370
+ return;
371
+
368
372
  default:
369
373
  console.log(`[${connectionId}] Unknown action type: ${(route.action as any).type}`);
370
374
  socket.end();
@@ -528,7 +532,7 @@ export class RouteConnectionHandler {
528
532
 
529
533
  // If we have an initial chunk with TLS data, start processing it
530
534
  if (initialChunk && record.isTLS) {
531
- return this.networkProxyBridge.forwardToNetworkProxy(
535
+ this.networkProxyBridge.forwardToNetworkProxy(
532
536
  connectionId,
533
537
  socket,
534
538
  record,
@@ -536,6 +540,7 @@ export class RouteConnectionHandler {
536
540
  this.settings.networkProxyPort,
537
541
  (reason) => this.connectionManager.initiateCleanupOnce(record, reason)
538
542
  );
543
+ return;
539
544
  }
540
545
 
541
546
  // This shouldn't normally happen - we should have TLS data at this point
@@ -706,6 +711,64 @@ export class RouteConnectionHandler {
706
711
  this.connectionManager.initiateCleanupOnce(record, 'route_blocked');
707
712
  }
708
713
 
714
+ /**
715
+ * Handle a static action for a route
716
+ */
717
+ private async handleStaticAction(
718
+ socket: plugins.net.Socket,
719
+ record: IConnectionRecord,
720
+ route: IRouteConfig
721
+ ): Promise<void> {
722
+ const connectionId = record.id;
723
+
724
+ if (!route.action.handler) {
725
+ console.error(`[${connectionId}] Static route '${route.name}' has no handler`);
726
+ socket.end();
727
+ this.connectionManager.cleanupConnection(record, 'no_handler');
728
+ return;
729
+ }
730
+
731
+ try {
732
+ // Build route context
733
+ const context: IRouteContext = {
734
+ port: record.localPort,
735
+ domain: record.lockedDomain,
736
+ clientIp: record.remoteIP,
737
+ serverIp: socket.localAddress!,
738
+ path: undefined, // Will need to be extracted from HTTP request
739
+ isTls: record.isTLS,
740
+ tlsVersion: record.tlsVersion,
741
+ routeName: route.name,
742
+ routeId: route.name,
743
+ timestamp: Date.now(),
744
+ connectionId
745
+ };
746
+
747
+ // Call the handler
748
+ const response = await route.action.handler(context);
749
+
750
+ // Send HTTP response
751
+ const headers = response.headers || {};
752
+ headers['Content-Length'] = Buffer.byteLength(response.body).toString();
753
+
754
+ let httpResponse = `HTTP/1.1 ${response.status} ${getStatusText(response.status)}\r\n`;
755
+ for (const [key, value] of Object.entries(headers)) {
756
+ httpResponse += `${key}: ${value}\r\n`;
757
+ }
758
+ httpResponse += '\r\n';
759
+
760
+ socket.write(httpResponse);
761
+ socket.write(response.body);
762
+ socket.end();
763
+
764
+ this.connectionManager.cleanupConnection(record, 'completed');
765
+ } catch (error) {
766
+ console.error(`[${connectionId}] Error in static handler: ${error}`);
767
+ socket.end();
768
+ this.connectionManager.cleanupConnection(record, 'handler_error');
769
+ }
770
+ }
771
+
709
772
  /**
710
773
  * Sets up a direct connection to the target
711
774
  */
@@ -1131,4 +1194,14 @@ export class RouteConnectionHandler {
1131
1194
  }
1132
1195
  });
1133
1196
  }
1197
+ }
1198
+
1199
+ // Helper function for status text
1200
+ function getStatusText(status: number): string {
1201
+ const statusTexts: Record<number, string> = {
1202
+ 200: 'OK',
1203
+ 404: 'Not Found',
1204
+ 500: 'Internal Server Error'
1205
+ };
1206
+ return statusTexts[status] || 'Unknown';
1134
1207
  }