@push.rocks/smartproxy 19.6.2 → 19.6.7

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 (52) hide show
  1. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +4 -7
  2. package/dist_ts/proxies/smart-proxy/connection-manager.js +22 -22
  3. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +4 -3
  4. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +9 -9
  5. package/dist_ts/proxies/smart-proxy/metrics-collector.d.ts +68 -56
  6. package/dist_ts/proxies/smart-proxy/metrics-collector.js +226 -176
  7. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -0
  8. package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +94 -48
  9. package/dist_ts/proxies/smart-proxy/nftables-manager.d.ts +4 -4
  10. package/dist_ts/proxies/smart-proxy/nftables-manager.js +6 -6
  11. package/dist_ts/proxies/smart-proxy/port-manager.d.ts +4 -7
  12. package/dist_ts/proxies/smart-proxy/port-manager.js +6 -9
  13. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +4 -15
  14. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +128 -128
  15. package/dist_ts/proxies/smart-proxy/security-manager.d.ts +3 -3
  16. package/dist_ts/proxies/smart-proxy/security-manager.js +9 -9
  17. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +20 -13
  18. package/dist_ts/proxies/smart-proxy/smart-proxy.js +16 -13
  19. package/dist_ts/proxies/smart-proxy/throughput-tracker.d.ts +36 -0
  20. package/dist_ts/proxies/smart-proxy/throughput-tracker.js +117 -0
  21. package/dist_ts/proxies/smart-proxy/timeout-manager.d.ts +4 -3
  22. package/dist_ts/proxies/smart-proxy/timeout-manager.js +16 -16
  23. package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +3 -3
  24. package/dist_ts/proxies/smart-proxy/tls-manager.js +12 -12
  25. package/package.json +8 -17
  26. package/readme.hints.md +0 -897
  27. package/readme.md +960 -54
  28. package/readme.plan.md +301 -562
  29. package/ts/proxies/smart-proxy/connection-manager.ts +23 -21
  30. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +9 -8
  31. package/ts/proxies/smart-proxy/metrics-collector.ts +277 -189
  32. package/ts/proxies/smart-proxy/models/interfaces.ts +7 -0
  33. package/ts/proxies/smart-proxy/models/metrics-types.ts +93 -41
  34. package/ts/proxies/smart-proxy/nftables-manager.ts +5 -5
  35. package/ts/proxies/smart-proxy/port-manager.ts +6 -14
  36. package/ts/proxies/smart-proxy/route-connection-handler.ts +136 -136
  37. package/ts/proxies/smart-proxy/security-manager.ts +8 -8
  38. package/ts/proxies/smart-proxy/smart-proxy.ts +26 -35
  39. package/ts/proxies/smart-proxy/throughput-tracker.ts +144 -0
  40. package/ts/proxies/smart-proxy/timeout-manager.ts +16 -15
  41. package/ts/proxies/smart-proxy/tls-manager.ts +11 -11
  42. package/readme.connections.md +0 -724
  43. package/readme.delete.md +0 -187
  44. package/readme.memory-leaks-fixed.md +0 -45
  45. package/readme.metrics.md +0 -591
  46. package/readme.monitoring.md +0 -202
  47. package/readme.proxy-chain-summary.md +0 -112
  48. package/readme.proxy-protocol-example.md +0 -462
  49. package/readme.proxy-protocol.md +0 -415
  50. package/readme.routing.md +0 -341
  51. package/readme.websocket-keepalive-config.md +0 -140
  52. package/readme.websocket-keepalive-fix.md +0 -63
@@ -1,462 +0,0 @@
1
- # SmartProxy PROXY Protocol Implementation Example
2
-
3
- This document shows how PROXY protocol parsing could be implemented in SmartProxy. Note that this is a conceptual implementation guide - the actual parsing is not yet implemented in the current version.
4
-
5
- ## Conceptual PROXY Protocol v1 Parser Implementation
6
-
7
- ### Parser Class
8
-
9
- ```typescript
10
- // This would go in ts/core/utils/proxy-protocol-parser.ts
11
- import { logger } from './logger.js';
12
-
13
- export interface IProxyProtocolInfo {
14
- version: 1 | 2;
15
- command: 'PROXY' | 'LOCAL';
16
- family: 'TCP4' | 'TCP6' | 'UNKNOWN';
17
- sourceIP: string;
18
- destIP: string;
19
- sourcePort: number;
20
- destPort: number;
21
- headerLength: number;
22
- }
23
-
24
- export class ProxyProtocolParser {
25
- private static readonly PROXY_V1_SIGNATURE = 'PROXY ';
26
- private static readonly MAX_V1_HEADER_LENGTH = 108; // Max possible v1 header
27
-
28
- /**
29
- * Parse PROXY protocol v1 header from buffer
30
- * Returns null if not a valid PROXY protocol header
31
- */
32
- static parseV1(buffer: Buffer): IProxyProtocolInfo | null {
33
- // Need at least 8 bytes for "PROXY " + newline
34
- if (buffer.length < 8) {
35
- return null;
36
- }
37
-
38
- // Check for v1 signature
39
- const possibleHeader = buffer.toString('ascii', 0, 6);
40
- if (possibleHeader !== this.PROXY_V1_SIGNATURE) {
41
- return null;
42
- }
43
-
44
- // Find the end of the header (CRLF)
45
- let headerEnd = -1;
46
- for (let i = 6; i < Math.min(buffer.length, this.MAX_V1_HEADER_LENGTH); i++) {
47
- if (buffer[i] === 0x0D && buffer[i + 1] === 0x0A) { // \r\n
48
- headerEnd = i + 2;
49
- break;
50
- }
51
- }
52
-
53
- if (headerEnd === -1) {
54
- // No complete header found
55
- return null;
56
- }
57
-
58
- // Parse the header line
59
- const headerLine = buffer.toString('ascii', 0, headerEnd - 2);
60
- const parts = headerLine.split(' ');
61
-
62
- if (parts.length !== 6) {
63
- logger.log('warn', 'Invalid PROXY v1 header format', {
64
- headerLine,
65
- partCount: parts.length
66
- });
67
- return null;
68
- }
69
-
70
- const [proxy, family, srcIP, dstIP, srcPort, dstPort] = parts;
71
-
72
- // Validate family
73
- if (!['TCP4', 'TCP6', 'UNKNOWN'].includes(family)) {
74
- logger.log('warn', 'Invalid PROXY protocol family', { family });
75
- return null;
76
- }
77
-
78
- // Validate ports
79
- const sourcePort = parseInt(srcPort);
80
- const destPort = parseInt(dstPort);
81
-
82
- if (isNaN(sourcePort) || sourcePort < 1 || sourcePort > 65535 ||
83
- isNaN(destPort) || destPort < 1 || destPort > 65535) {
84
- logger.log('warn', 'Invalid PROXY protocol ports', { srcPort, dstPort });
85
- return null;
86
- }
87
-
88
- return {
89
- version: 1,
90
- command: 'PROXY',
91
- family: family as 'TCP4' | 'TCP6' | 'UNKNOWN',
92
- sourceIP: srcIP,
93
- destIP: dstIP,
94
- sourcePort,
95
- destPort,
96
- headerLength: headerEnd
97
- };
98
- }
99
-
100
- /**
101
- * Check if buffer potentially contains PROXY protocol
102
- */
103
- static mightBeProxyProtocol(buffer: Buffer): boolean {
104
- if (buffer.length < 6) return false;
105
-
106
- // Check for v1 signature
107
- const start = buffer.toString('ascii', 0, 6);
108
- if (start === this.PROXY_V1_SIGNATURE) return true;
109
-
110
- // Check for v2 signature (12 bytes: \x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A)
111
- if (buffer.length >= 12) {
112
- const v2Sig = Buffer.from([0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A]);
113
- if (buffer.compare(v2Sig, 0, 12, 0, 12) === 0) return true;
114
- }
115
-
116
- return false;
117
- }
118
- }
119
- ```
120
-
121
- ### Integration with RouteConnectionHandler
122
-
123
- ```typescript
124
- // This shows how it would be integrated into route-connection-handler.ts
125
-
126
- private async handleProxyProtocol(
127
- socket: plugins.net.Socket,
128
- wrappedSocket: WrappedSocket,
129
- record: IConnectionRecord
130
- ): Promise<Buffer | null> {
131
- const remoteIP = socket.remoteAddress || '';
132
-
133
- // Only parse PROXY protocol from trusted IPs
134
- if (!this.settings.proxyIPs?.includes(remoteIP)) {
135
- return null;
136
- }
137
-
138
- return new Promise((resolve) => {
139
- let buffer = Buffer.alloc(0);
140
- let headerParsed = false;
141
-
142
- const parseHandler = (chunk: Buffer) => {
143
- // Accumulate data
144
- buffer = Buffer.concat([buffer, chunk]);
145
-
146
- // Try to parse PROXY protocol
147
- const proxyInfo = ProxyProtocolParser.parseV1(buffer);
148
-
149
- if (proxyInfo) {
150
- // Update wrapped socket with real client info
151
- wrappedSocket.setProxyInfo(proxyInfo.sourceIP, proxyInfo.sourcePort);
152
-
153
- // Update connection record
154
- record.remoteIP = proxyInfo.sourceIP;
155
-
156
- logger.log('info', 'PROXY protocol parsed', {
157
- connectionId: record.id,
158
- realIP: proxyInfo.sourceIP,
159
- realPort: proxyInfo.sourcePort,
160
- proxyIP: remoteIP
161
- });
162
-
163
- // Remove this handler
164
- socket.removeListener('data', parseHandler);
165
- headerParsed = true;
166
-
167
- // Return remaining data after header
168
- const remaining = buffer.slice(proxyInfo.headerLength);
169
- resolve(remaining.length > 0 ? remaining : null);
170
- } else if (buffer.length > 108) {
171
- // Max v1 header length exceeded, not PROXY protocol
172
- socket.removeListener('data', parseHandler);
173
- headerParsed = true;
174
- resolve(buffer);
175
- }
176
- };
177
-
178
- // Set timeout for PROXY protocol parsing
179
- const timeout = setTimeout(() => {
180
- if (!headerParsed) {
181
- socket.removeListener('data', parseHandler);
182
- logger.log('warn', 'PROXY protocol parsing timeout', {
183
- connectionId: record.id,
184
- bufferLength: buffer.length
185
- });
186
- resolve(buffer.length > 0 ? buffer : null);
187
- }
188
- }, 1000); // 1 second timeout
189
-
190
- socket.on('data', parseHandler);
191
-
192
- // Clean up on early close
193
- socket.once('close', () => {
194
- clearTimeout(timeout);
195
- if (!headerParsed) {
196
- socket.removeListener('data', parseHandler);
197
- resolve(null);
198
- }
199
- });
200
- });
201
- }
202
-
203
- // Modified handleConnection to include PROXY protocol parsing
204
- public async handleConnection(socket: plugins.net.Socket): void {
205
- const remoteIP = socket.remoteAddress || '';
206
- const localPort = socket.localPort || 0;
207
-
208
- // Always wrap the socket
209
- const wrappedSocket = new WrappedSocket(socket);
210
-
211
- // Create connection record
212
- const record = this.connectionManager.createConnection(wrappedSocket);
213
- if (!record) return;
214
-
215
- // If from trusted proxy, parse PROXY protocol
216
- if (this.settings.proxyIPs?.includes(remoteIP)) {
217
- const remainingData = await this.handleProxyProtocol(socket, wrappedSocket, record);
218
-
219
- if (remainingData) {
220
- // Process remaining data as normal
221
- this.handleInitialData(wrappedSocket, record, remainingData);
222
- } else {
223
- // Wait for more data
224
- this.handleInitialData(wrappedSocket, record);
225
- }
226
- } else {
227
- // Not from trusted proxy, handle normally
228
- this.handleInitialData(wrappedSocket, record);
229
- }
230
- }
231
- ```
232
-
233
- ### Sending PROXY Protocol When Forwarding
234
-
235
- ```typescript
236
- // This would be added to setupDirectConnection method
237
-
238
- private setupDirectConnection(
239
- socket: plugins.net.Socket | WrappedSocket,
240
- record: IConnectionRecord,
241
- serverName?: string,
242
- initialChunk?: Buffer,
243
- overridePort?: number,
244
- targetHost?: string,
245
- targetPort?: number
246
- ): void {
247
- // ... existing code ...
248
-
249
- // Create target socket
250
- const targetSocket = createSocketWithErrorHandler({
251
- port: finalTargetPort,
252
- host: finalTargetHost,
253
- onConnect: () => {
254
- // If sendProxyProtocol is enabled, send PROXY header first
255
- if (this.settings.sendProxyProtocol) {
256
- const proxyHeader = this.buildProxyProtocolHeader(wrappedSocket, targetSocket);
257
- targetSocket.write(proxyHeader);
258
- }
259
-
260
- // Then send any pending data
261
- if (record.pendingData.length > 0) {
262
- const combinedData = Buffer.concat(record.pendingData);
263
- targetSocket.write(combinedData);
264
- }
265
-
266
- // ... rest of connection setup ...
267
- }
268
- });
269
- }
270
-
271
- private buildProxyProtocolHeader(
272
- clientSocket: WrappedSocket,
273
- serverSocket: net.Socket
274
- ): Buffer {
275
- const family = clientSocket.remoteFamily === 'IPv6' ? 'TCP6' : 'TCP4';
276
- const srcIP = clientSocket.remoteAddress || '0.0.0.0';
277
- const srcPort = clientSocket.remotePort || 0;
278
- const dstIP = serverSocket.localAddress || '0.0.0.0';
279
- const dstPort = serverSocket.localPort || 0;
280
-
281
- const header = `PROXY ${family} ${srcIP} ${dstIP} ${srcPort} ${dstPort}\r\n`;
282
- return Buffer.from(header, 'ascii');
283
- }
284
- ```
285
-
286
- ## Complete Example: HAProxy Compatible Setup
287
-
288
- ```typescript
289
- // Example showing a complete HAProxy-compatible SmartProxy setup
290
-
291
- import { SmartProxy } from '@push.rocks/smartproxy';
292
-
293
- // Configuration matching HAProxy's proxy protocol behavior
294
- const proxy = new SmartProxy({
295
- // Accept PROXY protocol from these sources (like HAProxy's 'accept-proxy')
296
- proxyIPs: [
297
- '10.0.0.0/8', // Private network load balancers
298
- '172.16.0.0/12', // Docker networks
299
- '192.168.0.0/16' // Local networks
300
- ],
301
-
302
- // Send PROXY protocol to backends (like HAProxy's 'send-proxy')
303
- sendProxyProtocol: true,
304
-
305
- routes: [
306
- {
307
- name: 'web-app',
308
- match: {
309
- ports: 443,
310
- domains: ['app.example.com', 'www.example.com']
311
- },
312
- action: {
313
- type: 'forward',
314
- target: {
315
- host: 'backend-pool.internal',
316
- port: 8080
317
- },
318
- tls: {
319
- mode: 'terminate',
320
- certificate: 'auto',
321
- acme: {
322
- email: 'ssl@example.com'
323
- }
324
- }
325
- }
326
- }
327
- ]
328
- });
329
-
330
- // Start the proxy
331
- await proxy.start();
332
-
333
- // The proxy will now:
334
- // 1. Accept connections on port 443
335
- // 2. Parse PROXY protocol from trusted IPs
336
- // 3. Terminate TLS
337
- // 4. Forward to backend with PROXY protocol header
338
- // 5. Backend sees real client IP
339
- ```
340
-
341
- ## Testing PROXY Protocol
342
-
343
- ```typescript
344
- // Test client that sends PROXY protocol
345
- import * as net from 'net';
346
-
347
- function createProxyProtocolClient(
348
- realClientIP: string,
349
- realClientPort: number,
350
- proxyHost: string,
351
- proxyPort: number
352
- ): net.Socket {
353
- const client = net.connect(proxyPort, proxyHost);
354
-
355
- client.on('connect', () => {
356
- // Send PROXY protocol header
357
- const header = `PROXY TCP4 ${realClientIP} ${proxyHost} ${realClientPort} ${proxyPort}\r\n`;
358
- client.write(header);
359
-
360
- // Then send actual request
361
- client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n');
362
- });
363
-
364
- return client;
365
- }
366
-
367
- // Usage
368
- const client = createProxyProtocolClient(
369
- '203.0.113.45', // Real client IP
370
- 54321, // Real client port
371
- 'localhost', // Proxy host
372
- 8080 // Proxy port
373
- );
374
- ```
375
-
376
- ## AWS Network Load Balancer Example
377
-
378
- ```typescript
379
- // Configuration for AWS NLB with PROXY protocol v2
380
- const proxy = new SmartProxy({
381
- // AWS NLB IP ranges (get current list from AWS)
382
- proxyIPs: [
383
- '10.0.0.0/8', // VPC CIDR
384
- // Add specific NLB IPs or use AWS IP ranges
385
- ],
386
-
387
- // AWS NLB uses PROXY protocol v2 by default
388
- acceptProxyProtocolV2: true, // Future feature
389
-
390
- routes: [{
391
- name: 'aws-app',
392
- match: { ports: 443 },
393
- action: {
394
- type: 'forward',
395
- target: {
396
- host: 'app-cluster.internal',
397
- port: 8443
398
- },
399
- tls: { mode: 'passthrough' }
400
- }
401
- }]
402
- });
403
-
404
- // The proxy will:
405
- // 1. Accept PROXY protocol v2 from AWS NLB
406
- // 2. Preserve VPC endpoint IDs and other metadata
407
- // 3. Forward to backend with real client information
408
- ```
409
-
410
- ## Debugging PROXY Protocol
411
-
412
- ```typescript
413
- // Enable detailed logging to debug PROXY protocol parsing
414
- const proxy = new SmartProxy({
415
- enableDetailedLogging: true,
416
- proxyIPs: ['10.0.0.1'],
417
-
418
- // Add custom logging for debugging
419
- routes: [{
420
- name: 'debug-route',
421
- match: { ports: 8080 },
422
- action: {
423
- type: 'socket-handler',
424
- socketHandler: async (socket, context) => {
425
- console.log('Socket handler called with context:', {
426
- clientIp: context.clientIp, // Real IP from PROXY protocol
427
- port: context.port,
428
- connectionId: context.connectionId,
429
- timestamp: context.timestamp
430
- });
431
-
432
- // Handle the socket...
433
- }
434
- }
435
- }]
436
- });
437
- ```
438
-
439
- ## Security Considerations
440
-
441
- 1. **Always validate trusted proxy IPs** - Never accept PROXY protocol from untrusted sources
442
- 2. **Use specific IP ranges** - Avoid wildcards like `0.0.0.0/0`
443
- 3. **Implement rate limiting** - PROXY protocol parsing has a computational cost
444
- 4. **Validate header format** - Reject malformed headers immediately
445
- 5. **Set parsing timeouts** - Prevent slow loris attacks via PROXY headers
446
- 6. **Log parsing failures** - Monitor for potential attacks or misconfigurations
447
-
448
- ## Performance Considerations
449
-
450
- 1. **Header parsing overhead** - Minimal, one-time cost per connection
451
- 2. **Memory usage** - Small buffer for header accumulation (max 108 bytes for v1)
452
- 3. **Connection establishment** - Slight delay for PROXY protocol parsing
453
- 4. **Throughput impact** - None after initial header parsing
454
- 5. **CPU usage** - Negligible for well-formed headers
455
-
456
- ## Future Enhancements
457
-
458
- 1. **PROXY Protocol v2** - Binary format for better performance
459
- 2. **TLS information preservation** - Pass TLS version, cipher, SNI via PP2
460
- 3. **Custom type-length-value (TLV) fields** - Extended metadata support
461
- 4. **Connection pooling** - Reuse backend connections with different client IPs
462
- 5. **Health checks** - Skip PROXY protocol for health check connections