@push.rocks/smartproxy 26.2.4 → 27.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 +16 -0
- package/dist_rust/rustproxy_linux_amd64 +0 -0
- package/dist_rust/rustproxy_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/core/models/wrapped-socket.js +4 -1
- package/dist_ts/core/routing/route-manager.js +9 -7
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/proxies/smart-proxy/datagram-handler-server.js +7 -5
- package/dist_ts/proxies/smart-proxy/route-preprocessor.js +6 -8
- package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +186 -184
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +0 -1
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +2 -4
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +9 -4
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +70 -10
- package/dist_ts/proxies/smart-proxy/socket-handler-server.js +5 -3
- package/dist_ts/proxies/smart-proxy/utils/concurrency-semaphore.js +4 -3
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +2 -3
- package/dist_ts/proxies/smart-proxy/utils/index.js +4 -6
- package/dist_ts/proxies/smart-proxy/utils/mutex.js +3 -5
- package/dist_ts/proxies/smart-proxy/utils/route-validator.js +7 -7
- package/dist_ts/proxies/smart-proxy/utils/{route-helpers/socket-handlers.d.ts → socket-handlers.d.ts} +2 -10
- package/dist_ts/proxies/smart-proxy/utils/socket-handlers.js +268 -0
- package/dist_ts/routing/models/http-types.js +5 -1
- package/package.json +2 -1
- package/readme.hints.md +42 -20
- package/readme.md +165 -163
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/plugins.ts +2 -0
- package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +0 -5
- package/ts/proxies/smart-proxy/smart-proxy.ts +67 -5
- package/ts/proxies/smart-proxy/utils/index.ts +3 -11
- package/ts/proxies/smart-proxy/utils/{route-helpers/socket-handlers.ts → socket-handlers.ts} +3 -31
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.d.ts +0 -49
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.js +0 -108
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.d.ts +0 -57
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.js +0 -90
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.d.ts +0 -17
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.js +0 -32
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.d.ts +0 -68
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.js +0 -117
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.d.ts +0 -17
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.js +0 -27
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.d.ts +0 -63
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.js +0 -105
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.d.ts +0 -83
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.js +0 -126
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.d.ts +0 -47
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.js +0 -66
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.js +0 -286
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.d.ts +0 -46
- package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.js +0 -67
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +0 -9
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +0 -11
- package/ts/proxies/smart-proxy/utils/route-helpers/api-helpers.ts +0 -144
- package/ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.ts +0 -125
- package/ts/proxies/smart-proxy/utils/route-helpers/http-helpers.ts +0 -40
- package/ts/proxies/smart-proxy/utils/route-helpers/https-helpers.ts +0 -163
- package/ts/proxies/smart-proxy/utils/route-helpers/index.ts +0 -62
- package/ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.ts +0 -154
- package/ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.ts +0 -202
- package/ts/proxies/smart-proxy/utils/route-helpers/security-helpers.ts +0 -96
- package/ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.ts +0 -98
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +0 -11
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @push.rocks/smartproxy 🚀
|
|
2
2
|
|
|
3
|
-
**A high-performance, Rust-powered proxy toolkit for Node.js** — unified route-based configuration for SSL/TLS termination, HTTP/HTTPS reverse proxying, WebSocket support, UDP/QUIC/HTTP3, load balancing, custom protocol handlers, and kernel-level NFTables forwarding.
|
|
3
|
+
**A high-performance, Rust-powered proxy toolkit for Node.js** — unified route-based configuration for SSL/TLS termination, HTTP/HTTPS reverse proxying, WebSocket support, UDP/QUIC/HTTP3, load balancing, custom protocol handlers, and kernel-level NFTables forwarding via [`@push.rocks/smartnftables`](https://code.foss.global/push.rocks/smartnftables).
|
|
4
4
|
|
|
5
5
|
## 📦 Installation
|
|
6
6
|
|
|
@@ -44,7 +44,7 @@ Whether you're building microservices, deploying edge infrastructure, proxying U
|
|
|
44
44
|
Get up and running in 30 seconds:
|
|
45
45
|
|
|
46
46
|
```typescript
|
|
47
|
-
import { SmartProxy,
|
|
47
|
+
import { SmartProxy, SocketHandlers } from '@push.rocks/smartproxy';
|
|
48
48
|
|
|
49
49
|
// Create a proxy with automatic HTTPS
|
|
50
50
|
const proxy = new SmartProxy({
|
|
@@ -53,13 +53,25 @@ const proxy = new SmartProxy({
|
|
|
53
53
|
useProduction: true
|
|
54
54
|
},
|
|
55
55
|
routes: [
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
// HTTPS route with automatic Let's Encrypt cert
|
|
57
|
+
{
|
|
58
|
+
name: 'https-app',
|
|
59
|
+
match: { ports: 443, domains: 'app.example.com' },
|
|
60
|
+
action: {
|
|
61
|
+
type: 'forward',
|
|
62
|
+
targets: [{ host: 'localhost', port: 3000 }],
|
|
63
|
+
tls: { mode: 'terminate', certificate: 'auto' }
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
// HTTP → HTTPS redirect
|
|
67
|
+
{
|
|
68
|
+
name: 'http-redirect',
|
|
69
|
+
match: { ports: 80, domains: 'app.example.com' },
|
|
70
|
+
action: {
|
|
71
|
+
type: 'socket-handler',
|
|
72
|
+
socketHandler: SocketHandlers.httpRedirect('https://{domain}:443{path}', 301)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
63
75
|
]
|
|
64
76
|
});
|
|
65
77
|
|
|
@@ -111,31 +123,38 @@ SmartProxy supports three TLS handling modes:
|
|
|
111
123
|
### 🌐 HTTP to HTTPS Redirect
|
|
112
124
|
|
|
113
125
|
```typescript
|
|
114
|
-
import { SmartProxy,
|
|
126
|
+
import { SmartProxy, SocketHandlers } from '@push.rocks/smartproxy';
|
|
115
127
|
|
|
116
128
|
const proxy = new SmartProxy({
|
|
117
|
-
routes: [
|
|
118
|
-
|
|
119
|
-
|
|
129
|
+
routes: [{
|
|
130
|
+
name: 'http-to-https',
|
|
131
|
+
match: { ports: 80, domains: ['example.com', '*.example.com'] },
|
|
132
|
+
action: {
|
|
133
|
+
type: 'socket-handler',
|
|
134
|
+
socketHandler: SocketHandlers.httpRedirect('https://{domain}:443{path}', 301)
|
|
135
|
+
}
|
|
136
|
+
}]
|
|
120
137
|
});
|
|
121
138
|
```
|
|
122
139
|
|
|
123
140
|
### ⚖️ Load Balancer with Health Checks
|
|
124
141
|
|
|
125
142
|
```typescript
|
|
126
|
-
import { SmartProxy
|
|
143
|
+
import { SmartProxy } from '@push.rocks/smartproxy';
|
|
127
144
|
|
|
128
145
|
const proxy = new SmartProxy({
|
|
129
|
-
routes: [
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
146
|
+
routes: [{
|
|
147
|
+
name: 'load-balancer',
|
|
148
|
+
match: { ports: 443, domains: 'app.example.com' },
|
|
149
|
+
action: {
|
|
150
|
+
type: 'forward',
|
|
151
|
+
targets: [
|
|
133
152
|
{ host: 'server1.internal', port: 8080 },
|
|
134
153
|
{ host: 'server2.internal', port: 8080 },
|
|
135
154
|
{ host: 'server3.internal', port: 8080 }
|
|
136
155
|
],
|
|
137
|
-
{
|
|
138
|
-
|
|
156
|
+
tls: { mode: 'terminate', certificate: 'auto' },
|
|
157
|
+
loadBalancing: {
|
|
139
158
|
algorithm: 'round-robin',
|
|
140
159
|
healthCheck: {
|
|
141
160
|
path: '/health',
|
|
@@ -145,57 +164,68 @@ const proxy = new SmartProxy({
|
|
|
145
164
|
healthyThreshold: 2
|
|
146
165
|
}
|
|
147
166
|
}
|
|
148
|
-
|
|
149
|
-
]
|
|
167
|
+
}
|
|
168
|
+
}]
|
|
150
169
|
});
|
|
151
170
|
```
|
|
152
171
|
|
|
153
172
|
### 🔌 WebSocket Proxy
|
|
154
173
|
|
|
155
174
|
```typescript
|
|
156
|
-
import { SmartProxy
|
|
175
|
+
import { SmartProxy } from '@push.rocks/smartproxy';
|
|
157
176
|
|
|
158
177
|
const proxy = new SmartProxy({
|
|
159
|
-
routes: [
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
178
|
+
routes: [{
|
|
179
|
+
name: 'websocket',
|
|
180
|
+
match: { ports: 443, domains: 'ws.example.com', path: '/socket' },
|
|
181
|
+
priority: 100,
|
|
182
|
+
action: {
|
|
183
|
+
type: 'forward',
|
|
184
|
+
targets: [{ host: 'websocket-server', port: 8080 }],
|
|
185
|
+
tls: { mode: 'terminate', certificate: 'auto' },
|
|
186
|
+
websocket: {
|
|
187
|
+
enabled: true,
|
|
167
188
|
pingInterval: 30000,
|
|
168
189
|
pingTimeout: 10000
|
|
169
190
|
}
|
|
170
|
-
|
|
171
|
-
]
|
|
191
|
+
}
|
|
192
|
+
}]
|
|
172
193
|
});
|
|
173
194
|
```
|
|
174
195
|
|
|
175
196
|
### 🚦 API Gateway with Rate Limiting
|
|
176
197
|
|
|
177
198
|
```typescript
|
|
178
|
-
import { SmartProxy
|
|
179
|
-
|
|
180
|
-
let apiRoute = createApiGatewayRoute(
|
|
181
|
-
'api.example.com',
|
|
182
|
-
'/api',
|
|
183
|
-
{ host: 'api-backend', port: 8080 },
|
|
184
|
-
{
|
|
185
|
-
useTls: true,
|
|
186
|
-
certificate: 'auto',
|
|
187
|
-
addCorsHeaders: true
|
|
188
|
-
}
|
|
189
|
-
);
|
|
199
|
+
import { SmartProxy } from '@push.rocks/smartproxy';
|
|
190
200
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
201
|
+
const proxy = new SmartProxy({
|
|
202
|
+
routes: [{
|
|
203
|
+
name: 'api-gateway',
|
|
204
|
+
match: { ports: 443, domains: 'api.example.com', path: '/api/*' },
|
|
205
|
+
priority: 100,
|
|
206
|
+
action: {
|
|
207
|
+
type: 'forward',
|
|
208
|
+
targets: [{ host: 'api-backend', port: 8080 }],
|
|
209
|
+
tls: { mode: 'terminate', certificate: 'auto' }
|
|
210
|
+
},
|
|
211
|
+
headers: {
|
|
212
|
+
response: {
|
|
213
|
+
'Access-Control-Allow-Origin': '*',
|
|
214
|
+
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
215
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
216
|
+
'Access-Control-Max-Age': '86400'
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
security: {
|
|
220
|
+
rateLimit: {
|
|
221
|
+
enabled: true,
|
|
222
|
+
maxRequests: 100,
|
|
223
|
+
window: 60,
|
|
224
|
+
keyBy: 'ip'
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}]
|
|
196
228
|
});
|
|
197
|
-
|
|
198
|
-
const proxy = new SmartProxy({ routes: [apiRoute] });
|
|
199
229
|
```
|
|
200
230
|
|
|
201
231
|
### 🎮 Custom Protocol Handler (TCP)
|
|
@@ -203,36 +233,40 @@ const proxy = new SmartProxy({ routes: [apiRoute] });
|
|
|
203
233
|
SmartProxy lets you implement any protocol with full socket control. Routes with JavaScript socket handlers are automatically relayed from the Rust engine back to your TypeScript code:
|
|
204
234
|
|
|
205
235
|
```typescript
|
|
206
|
-
import { SmartProxy,
|
|
207
|
-
|
|
208
|
-
// Use pre-built handlers
|
|
209
|
-
const echoRoute = createSocketHandlerRoute(
|
|
210
|
-
'echo.example.com',
|
|
211
|
-
7777,
|
|
212
|
-
SocketHandlers.echo
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
// Or create your own custom protocol
|
|
216
|
-
const customRoute = createSocketHandlerRoute(
|
|
217
|
-
'custom.example.com',
|
|
218
|
-
9999,
|
|
219
|
-
async (socket) => {
|
|
220
|
-
console.log(`New connection on custom protocol`);
|
|
221
|
-
socket.write('Welcome to my custom protocol!\n');
|
|
222
|
-
|
|
223
|
-
socket.on('data', (data) => {
|
|
224
|
-
const command = data.toString().trim();
|
|
225
|
-
switch (command) {
|
|
226
|
-
case 'PING': socket.write('PONG\n'); break;
|
|
227
|
-
case 'TIME': socket.write(`${new Date().toISOString()}\n`); break;
|
|
228
|
-
case 'QUIT': socket.end('Goodbye!\n'); break;
|
|
229
|
-
default: socket.write(`Unknown: ${command}\n`);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
);
|
|
236
|
+
import { SmartProxy, SocketHandlers } from '@push.rocks/smartproxy';
|
|
234
237
|
|
|
235
|
-
const proxy = new SmartProxy({
|
|
238
|
+
const proxy = new SmartProxy({
|
|
239
|
+
routes: [
|
|
240
|
+
// Use pre-built handlers
|
|
241
|
+
{
|
|
242
|
+
name: 'echo-server',
|
|
243
|
+
match: { ports: 7777, domains: 'echo.example.com' },
|
|
244
|
+
action: { type: 'socket-handler', socketHandler: SocketHandlers.echo }
|
|
245
|
+
},
|
|
246
|
+
// Or create your own custom protocol
|
|
247
|
+
{
|
|
248
|
+
name: 'custom-protocol',
|
|
249
|
+
match: { ports: 9999, domains: 'custom.example.com' },
|
|
250
|
+
action: {
|
|
251
|
+
type: 'socket-handler',
|
|
252
|
+
socketHandler: async (socket) => {
|
|
253
|
+
console.log(`New connection on custom protocol`);
|
|
254
|
+
socket.write('Welcome to my custom protocol!\n');
|
|
255
|
+
|
|
256
|
+
socket.on('data', (data) => {
|
|
257
|
+
const command = data.toString().trim();
|
|
258
|
+
switch (command) {
|
|
259
|
+
case 'PING': socket.write('PONG\n'); break;
|
|
260
|
+
case 'TIME': socket.write(`${new Date().toISOString()}\n`); break;
|
|
261
|
+
case 'QUIT': socket.end('Goodbye!\n'); break;
|
|
262
|
+
default: socket.write(`Unknown: ${command}\n`);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
});
|
|
236
270
|
```
|
|
237
271
|
|
|
238
272
|
**Pre-built Socket Handlers:**
|
|
@@ -384,23 +418,26 @@ const dualStackRoute: IRouteConfig = {
|
|
|
384
418
|
|
|
385
419
|
### ⚡ High-Performance NFTables Forwarding
|
|
386
420
|
|
|
387
|
-
For ultra-low latency on Linux, use kernel-level forwarding (requires root):
|
|
421
|
+
For ultra-low latency on Linux, use kernel-level forwarding via [`@push.rocks/smartnftables`](https://code.foss.global/push.rocks/smartnftables) (requires root):
|
|
388
422
|
|
|
389
423
|
```typescript
|
|
390
|
-
import { SmartProxy
|
|
424
|
+
import { SmartProxy } from '@push.rocks/smartproxy';
|
|
391
425
|
|
|
392
426
|
const proxy = new SmartProxy({
|
|
393
|
-
routes: [
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
427
|
+
routes: [{
|
|
428
|
+
name: 'nftables-fast',
|
|
429
|
+
match: { ports: 443, domains: 'fast.example.com' },
|
|
430
|
+
action: {
|
|
431
|
+
type: 'forward',
|
|
432
|
+
forwardingEngine: 'nftables',
|
|
433
|
+
targets: [{ host: 'backend', port: 8080 }],
|
|
434
|
+
tls: { mode: 'terminate', certificate: 'auto' },
|
|
435
|
+
nftables: {
|
|
436
|
+
protocol: 'tcp',
|
|
400
437
|
preserveSourceIP: true // Backend sees real client IP
|
|
401
438
|
}
|
|
402
|
-
|
|
403
|
-
]
|
|
439
|
+
}
|
|
440
|
+
}]
|
|
404
441
|
});
|
|
405
442
|
```
|
|
406
443
|
|
|
@@ -409,15 +446,18 @@ const proxy = new SmartProxy({
|
|
|
409
446
|
Forward encrypted traffic to backends without terminating TLS — the proxy routes based on the SNI hostname alone:
|
|
410
447
|
|
|
411
448
|
```typescript
|
|
412
|
-
import { SmartProxy
|
|
449
|
+
import { SmartProxy } from '@push.rocks/smartproxy';
|
|
413
450
|
|
|
414
451
|
const proxy = new SmartProxy({
|
|
415
|
-
routes: [
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
452
|
+
routes: [{
|
|
453
|
+
name: 'sni-passthrough',
|
|
454
|
+
match: { ports: 443, domains: 'secure.example.com' },
|
|
455
|
+
action: {
|
|
456
|
+
type: 'forward',
|
|
457
|
+
targets: [{ host: 'backend-that-handles-tls', port: 8443 }],
|
|
458
|
+
tls: { mode: 'passthrough' }
|
|
459
|
+
}
|
|
460
|
+
}]
|
|
421
461
|
});
|
|
422
462
|
```
|
|
423
463
|
|
|
@@ -524,15 +564,7 @@ Comprehensive per-route security options:
|
|
|
524
564
|
}
|
|
525
565
|
```
|
|
526
566
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
```typescript
|
|
530
|
-
import { addRateLimiting, addBasicAuth, addJwtAuth } from '@push.rocks/smartproxy';
|
|
531
|
-
|
|
532
|
-
let route = createHttpsTerminateRoute('api.example.com', { host: 'backend', port: 8080 });
|
|
533
|
-
route = addRateLimiting(route, { maxRequests: 100, window: 60, keyBy: 'ip' });
|
|
534
|
-
route = addBasicAuth(route, { users: [{ username: 'admin', password: 'secret' }] });
|
|
535
|
-
```
|
|
567
|
+
Security options are configured directly on each route's `security` property — no separate helpers needed.
|
|
536
568
|
|
|
537
569
|
### 📊 Runtime Management
|
|
538
570
|
|
|
@@ -694,22 +726,26 @@ SmartProxy uses a hybrid **Rust + TypeScript** architecture:
|
|
|
694
726
|
│ │ Listener│ │ Reverse │ │ Matcher │ │ Cert Mgr │ │
|
|
695
727
|
│ │ │ │ Proxy │ │ │ │ │ │
|
|
696
728
|
│ └─────────┘ └─────────┘ └─────────┘ └──────────┘ │
|
|
697
|
-
│ ┌─────────┐ ┌─────────┐ ┌─────────┐
|
|
698
|
-
│ │ UDP │ │ Security│ │ Metrics │
|
|
699
|
-
│ │ QUIC │ │ Enforce │ │ Collect │
|
|
700
|
-
│ │ HTTP/3 │ │ │ │ │
|
|
701
|
-
│ └─────────┘ └─────────┘ └─────────┘
|
|
729
|
+
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
|
730
|
+
│ │ UDP │ │ Security│ │ Metrics │ │
|
|
731
|
+
│ │ QUIC │ │ Enforce │ │ Collect │ │
|
|
732
|
+
│ │ HTTP/3 │ │ │ │ │ │
|
|
733
|
+
│ └─────────┘ └─────────┘ └─────────┘ │
|
|
702
734
|
└──────────────────┬──────────────────────────────────┘
|
|
703
735
|
│ Unix Socket Relay
|
|
704
736
|
┌──────────────────▼──────────────────────────────────┐
|
|
705
737
|
│ TypeScript Socket & Datagram Handler Servers │
|
|
706
738
|
│ (for JS socket handlers, datagram handlers, │
|
|
707
739
|
│ and dynamic routes) │
|
|
740
|
+
├─────────────────────────────────────────────────────┤
|
|
741
|
+
│ @push.rocks/smartnftables (kernel-level NFTables) │
|
|
742
|
+
│ (DNAT/SNAT, firewall, rate limiting via nft CLI) │
|
|
708
743
|
└─────────────────────────────────────────────────────┘
|
|
709
744
|
```
|
|
710
745
|
|
|
711
746
|
- **Rust Engine** handles all networking: TCP, UDP, TLS, QUIC, HTTP proxying, connection management, security, and metrics
|
|
712
|
-
- **TypeScript** provides the npm API, configuration types,
|
|
747
|
+
- **TypeScript** provides the npm API, configuration types, validation, and handler callbacks
|
|
748
|
+
- **NFTables** managed by [`@push.rocks/smartnftables`](https://code.foss.global/push.rocks/smartnftables) — kernel-level DNAT/SNAT forwarding, firewall rules, and rate limiting via the `nft` CLI
|
|
713
749
|
- **IPC** — The TypeScript wrapper uses JSON commands/events over stdin/stdout to communicate with the Rust binary
|
|
714
750
|
- **Socket/Datagram Relay** — Unix domain socket servers for routes requiring TypeScript-side handling (socket handlers, datagram handlers, dynamic host/port functions)
|
|
715
751
|
|
|
@@ -854,47 +890,13 @@ interface IRouteQuic {
|
|
|
854
890
|
}
|
|
855
891
|
```
|
|
856
892
|
|
|
857
|
-
## 🛠️
|
|
858
|
-
|
|
859
|
-
All helpers are fully typed and return `IRouteConfig` or `IRouteConfig[]`:
|
|
893
|
+
## 🛠️ Exports Reference
|
|
860
894
|
|
|
861
895
|
```typescript
|
|
862
896
|
import {
|
|
863
|
-
//
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
createHttpsPassthroughRoute, // SNI passthrough (no termination)
|
|
867
|
-
createHttpToHttpsRedirect, // HTTP → HTTPS redirect
|
|
868
|
-
createCompleteHttpsServer, // HTTPS + redirect combo (returns IRouteConfig[])
|
|
869
|
-
|
|
870
|
-
// Load Balancing
|
|
871
|
-
createLoadBalancerRoute, // Multi-backend with health checks
|
|
872
|
-
createSmartLoadBalancer, // Dynamic domain-based backend selection
|
|
873
|
-
|
|
874
|
-
// API & WebSocket
|
|
875
|
-
createApiRoute, // API route with path matching
|
|
876
|
-
createApiGatewayRoute, // API gateway with CORS
|
|
877
|
-
createWebSocketRoute, // WebSocket-enabled route
|
|
878
|
-
|
|
879
|
-
// Custom Protocols
|
|
880
|
-
createSocketHandlerRoute, // Custom TCP socket handler
|
|
881
|
-
SocketHandlers, // Pre-built handlers (echo, proxy, block, etc.)
|
|
882
|
-
|
|
883
|
-
// NFTables (Linux, requires root)
|
|
884
|
-
createNfTablesRoute, // Kernel-level packet forwarding
|
|
885
|
-
createNfTablesTerminateRoute, // NFTables + TLS termination
|
|
886
|
-
createCompleteNfTablesHttpsServer, // NFTables HTTPS + redirect combo
|
|
887
|
-
|
|
888
|
-
// Dynamic Routing
|
|
889
|
-
createPortMappingRoute, // Port mapping with context
|
|
890
|
-
createOffsetPortMappingRoute, // Simple port offset
|
|
891
|
-
createDynamicRoute, // Dynamic host/port via functions
|
|
892
|
-
createPortOffset, // Port offset factory
|
|
893
|
-
|
|
894
|
-
// Security Modifiers
|
|
895
|
-
addRateLimiting, // Add rate limiting to any route
|
|
896
|
-
addBasicAuth, // Add basic auth to any route
|
|
897
|
-
addJwtAuth, // Add JWT auth to any route
|
|
897
|
+
// Core
|
|
898
|
+
SmartProxy, // Main proxy class
|
|
899
|
+
SocketHandlers, // Pre-built socket handlers (echo, proxy, block, httpRedirect, httpServer, etc.)
|
|
898
900
|
|
|
899
901
|
// Route Utilities
|
|
900
902
|
mergeRouteConfigs, // Deep-merge two route configs
|
|
@@ -906,7 +908,7 @@ import {
|
|
|
906
908
|
} from '@push.rocks/smartproxy';
|
|
907
909
|
```
|
|
908
910
|
|
|
909
|
-
|
|
911
|
+
All routes are configured as plain `IRouteConfig` objects with `match` and `action` properties — see the examples throughout this document.
|
|
910
912
|
|
|
911
913
|
## 📖 API Documentation
|
|
912
914
|
|
|
@@ -938,8 +940,8 @@ class SmartProxy extends EventEmitter {
|
|
|
938
940
|
getCertificateStatus(routeName: string): Promise<any>;
|
|
939
941
|
getEligibleDomainsForCertificates(): string[];
|
|
940
942
|
|
|
941
|
-
// NFTables
|
|
942
|
-
getNfTablesStatus():
|
|
943
|
+
// NFTables (managed by @push.rocks/smartnftables)
|
|
944
|
+
getNfTablesStatus(): INftStatus | null;
|
|
943
945
|
|
|
944
946
|
// Events
|
|
945
947
|
on(event: 'error', handler: (err: Error) => void): this;
|
|
@@ -991,11 +993,11 @@ interface ISmartProxyOptions {
|
|
|
991
993
|
sendProxyProtocol?: boolean; // Send PROXY protocol to targets
|
|
992
994
|
|
|
993
995
|
// Timeouts
|
|
994
|
-
connectionTimeout?: number; // Backend connection timeout (default:
|
|
995
|
-
initialDataTimeout?: number; // Initial data/SNI timeout (default:
|
|
996
|
-
socketTimeout?: number; // Socket inactivity timeout (default:
|
|
997
|
-
maxConnectionLifetime?: number; // Max connection lifetime (default:
|
|
998
|
-
inactivityTimeout?: number; // Inactivity timeout (default:
|
|
996
|
+
connectionTimeout?: number; // Backend connection timeout (default: 60s)
|
|
997
|
+
initialDataTimeout?: number; // Initial data/SNI timeout (default: 60s)
|
|
998
|
+
socketTimeout?: number; // Socket inactivity timeout (default: 60s)
|
|
999
|
+
maxConnectionLifetime?: number; // Max connection lifetime (default: 1h)
|
|
1000
|
+
inactivityTimeout?: number; // Inactivity timeout (default: 75s)
|
|
999
1001
|
gracefulShutdownTimeout?: number; // Shutdown grace period (default: 30s)
|
|
1000
1002
|
|
|
1001
1003
|
// Connection limits
|
|
@@ -1004,8 +1006,8 @@ interface ISmartProxyOptions {
|
|
|
1004
1006
|
|
|
1005
1007
|
// Keep-alive
|
|
1006
1008
|
keepAliveTreatment?: 'standard' | 'extended' | 'immortal';
|
|
1007
|
-
keepAliveInactivityMultiplier?: number; // (default:
|
|
1008
|
-
extendedKeepAliveLifetime?: number; // (default:
|
|
1009
|
+
keepAliveInactivityMultiplier?: number; // (default: 4)
|
|
1010
|
+
extendedKeepAliveLifetime?: number; // (default: 1h)
|
|
1009
1011
|
|
|
1010
1012
|
// Metrics
|
|
1011
1013
|
metrics?: {
|
|
@@ -1137,7 +1139,7 @@ SmartProxy searches for the Rust binary in this order:
|
|
|
1137
1139
|
|
|
1138
1140
|
## License and Legal Information
|
|
1139
1141
|
|
|
1140
|
-
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [
|
|
1142
|
+
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license) file.
|
|
1141
1143
|
|
|
1142
1144
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
1143
1145
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '
|
|
6
|
+
version: '27.0.0',
|
|
7
7
|
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
|
|
8
8
|
}
|
package/ts/plugins.ts
CHANGED
|
@@ -19,12 +19,14 @@ export { tsclass };
|
|
|
19
19
|
import * as smartcrypto from '@push.rocks/smartcrypto';
|
|
20
20
|
import * as smartlog from '@push.rocks/smartlog';
|
|
21
21
|
import * as smartlogDestinationLocal from '@push.rocks/smartlog/destination-local';
|
|
22
|
+
import * as smartnftables from '@push.rocks/smartnftables';
|
|
22
23
|
import * as smartrust from '@push.rocks/smartrust';
|
|
23
24
|
|
|
24
25
|
export {
|
|
25
26
|
smartcrypto,
|
|
26
27
|
smartlog,
|
|
27
28
|
smartlogDestinationLocal,
|
|
29
|
+
smartnftables,
|
|
28
30
|
smartrust,
|
|
29
31
|
};
|
|
30
32
|
|
|
@@ -15,7 +15,6 @@ type TSmartProxyCommands = {
|
|
|
15
15
|
renewCertificate: { params: { routeName: string }; result: void };
|
|
16
16
|
getCertificateStatus: { params: { routeName: string }; result: any };
|
|
17
17
|
getListeningPorts: { params: Record<string, never>; result: { ports: number[] } };
|
|
18
|
-
getNftablesStatus: { params: Record<string, never>; result: any };
|
|
19
18
|
setSocketHandlerRelay: { params: { socketPath: string }; result: void };
|
|
20
19
|
addListeningPort: { params: { port: number }; result: void };
|
|
21
20
|
removeListeningPort: { params: { port: number }; result: void };
|
|
@@ -159,10 +158,6 @@ export class RustProxyBridge extends plugins.EventEmitter {
|
|
|
159
158
|
return result?.ports ?? [];
|
|
160
159
|
}
|
|
161
160
|
|
|
162
|
-
public async getNftablesStatus(): Promise<any> {
|
|
163
|
-
return this.bridge.sendCommand('getNftablesStatus', {} as Record<string, never>);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
161
|
public async setSocketHandlerRelay(socketPath: string): Promise<void> {
|
|
167
162
|
await this.bridge.sendCommand('setSocketHandlerRelay', { socketPath });
|
|
168
163
|
}
|
|
@@ -23,8 +23,8 @@ import type { IMetrics } from './models/metrics-types.js';
|
|
|
23
23
|
/**
|
|
24
24
|
* SmartProxy - Rust-backed proxy engine with TypeScript configuration API.
|
|
25
25
|
*
|
|
26
|
-
* All networking (TCP, TLS, HTTP reverse proxy, connection management, security
|
|
27
|
-
*
|
|
26
|
+
* All networking (TCP, TLS, HTTP reverse proxy, connection management, security)
|
|
27
|
+
* is handled by the Rust binary. TypeScript is only:
|
|
28
28
|
* - The npm module interface (types, route helpers)
|
|
29
29
|
* - The thin IPC wrapper (this class)
|
|
30
30
|
* - Socket-handler callback relay (for JS-defined handlers)
|
|
@@ -39,6 +39,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
39
39
|
private socketHandlerServer: SocketHandlerServer | null = null;
|
|
40
40
|
private datagramHandlerServer: DatagramHandlerServer | null = null;
|
|
41
41
|
private metricsAdapter: RustMetricsAdapter;
|
|
42
|
+
private nftablesManager: InstanceType<typeof plugins.smartnftables.SmartNftables> | null = null;
|
|
42
43
|
private routeUpdateLock: Mutex;
|
|
43
44
|
private stopping = false;
|
|
44
45
|
private certProvisionPromise: Promise<void> | null = null;
|
|
@@ -211,6 +212,9 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
211
212
|
}
|
|
212
213
|
}
|
|
213
214
|
|
|
215
|
+
// Apply NFTables rules for routes using nftables forwarding engine
|
|
216
|
+
await this.applyNftablesRules(this.settings.routes);
|
|
217
|
+
|
|
214
218
|
// Start metrics polling BEFORE cert provisioning — the Rust engine is already
|
|
215
219
|
// running and accepting connections, so metrics should be available immediately.
|
|
216
220
|
// Cert provisioning can hang indefinitely (e.g. DNS-01 ACME timeouts) and must
|
|
@@ -238,6 +242,12 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
238
242
|
this.certProvisionPromise = null;
|
|
239
243
|
}
|
|
240
244
|
|
|
245
|
+
// Clean up NFTables rules
|
|
246
|
+
if (this.nftablesManager) {
|
|
247
|
+
await this.nftablesManager.cleanup();
|
|
248
|
+
this.nftablesManager = null;
|
|
249
|
+
}
|
|
250
|
+
|
|
241
251
|
// Stop metrics polling
|
|
242
252
|
this.metricsAdapter.stopPolling();
|
|
243
253
|
|
|
@@ -319,6 +329,13 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
319
329
|
this.datagramHandlerServer = null;
|
|
320
330
|
}
|
|
321
331
|
|
|
332
|
+
// Update NFTables rules
|
|
333
|
+
if (this.nftablesManager) {
|
|
334
|
+
await this.nftablesManager.cleanup();
|
|
335
|
+
this.nftablesManager = null;
|
|
336
|
+
}
|
|
337
|
+
await this.applyNftablesRules(newRoutes);
|
|
338
|
+
|
|
322
339
|
// Update stored routes
|
|
323
340
|
this.settings.routes = newRoutes;
|
|
324
341
|
|
|
@@ -411,14 +428,59 @@ export class SmartProxy extends plugins.EventEmitter {
|
|
|
411
428
|
}
|
|
412
429
|
|
|
413
430
|
/**
|
|
414
|
-
* Get NFTables status
|
|
431
|
+
* Get NFTables status.
|
|
415
432
|
*/
|
|
416
|
-
public
|
|
417
|
-
return this.
|
|
433
|
+
public getNfTablesStatus(): plugins.smartnftables.INftStatus | null {
|
|
434
|
+
return this.nftablesManager?.status() ?? null;
|
|
418
435
|
}
|
|
419
436
|
|
|
420
437
|
// --- Private helpers ---
|
|
421
438
|
|
|
439
|
+
/**
|
|
440
|
+
* Apply NFTables rules for routes using the nftables forwarding engine.
|
|
441
|
+
*/
|
|
442
|
+
private async applyNftablesRules(routes: IRouteConfig[]): Promise<void> {
|
|
443
|
+
const nftRoutes = routes.filter(r => r.action.forwardingEngine === 'nftables');
|
|
444
|
+
if (nftRoutes.length === 0) return;
|
|
445
|
+
|
|
446
|
+
const tableName = nftRoutes.find(r => r.action.nftables?.tableName)?.action.nftables?.tableName ?? 'smartproxy';
|
|
447
|
+
const nft = new plugins.smartnftables.SmartNftables({ tableName });
|
|
448
|
+
await nft.initialize();
|
|
449
|
+
|
|
450
|
+
for (const route of nftRoutes) {
|
|
451
|
+
const routeId = route.name || 'unnamed';
|
|
452
|
+
const targets = route.action.targets;
|
|
453
|
+
if (!targets) continue;
|
|
454
|
+
|
|
455
|
+
const nftOpts = route.action.nftables;
|
|
456
|
+
const protocol: plugins.smartnftables.TNftProtocol = (nftOpts?.protocol as any) ?? 'tcp';
|
|
457
|
+
const preserveSourceIP = nftOpts?.preserveSourceIP ?? false;
|
|
458
|
+
|
|
459
|
+
const ports = Array.isArray(route.match.ports)
|
|
460
|
+
? route.match.ports.flatMap(p => typeof p === 'number' ? [p] : [])
|
|
461
|
+
: typeof route.match.ports === 'number' ? [route.match.ports] : [];
|
|
462
|
+
|
|
463
|
+
for (const target of targets) {
|
|
464
|
+
const targetHost = Array.isArray(target.host) ? target.host[0] : target.host;
|
|
465
|
+
if (typeof targetHost !== 'string') continue;
|
|
466
|
+
|
|
467
|
+
for (const sourcePort of ports) {
|
|
468
|
+
const targetPort = typeof target.port === 'number' ? target.port : sourcePort;
|
|
469
|
+
await nft.nat.addPortForwarding(`${routeId}-${sourcePort}-${targetPort}`, {
|
|
470
|
+
sourcePort,
|
|
471
|
+
targetHost,
|
|
472
|
+
targetPort,
|
|
473
|
+
protocol,
|
|
474
|
+
preserveSourceIP,
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
this.nftablesManager = nft;
|
|
481
|
+
logger.log('info', `Applied NFTables rules for ${nftRoutes.length} route(s)`, { component: 'smart-proxy' });
|
|
482
|
+
}
|
|
483
|
+
|
|
422
484
|
/**
|
|
423
485
|
* Build the Rust configuration object from TS settings.
|
|
424
486
|
*/
|