feat(rustproxy): introduce a Rust-powered proxy engine and workspace with core crates for proxy functionality, ACME/TLS support, passthrough and HTTP proxies, metrics, nftables integration, routing/security, management IPC, tests, and README updates
This commit is contained in:
719
readme.md
719
readme.md
@@ -1,6 +1,6 @@
|
||||
# @push.rocks/smartproxy 🚀
|
||||
|
||||
**The Swiss Army Knife of Node.js Proxies** - A unified, high-performance proxy toolkit that handles everything from simple HTTP forwarding to complex enterprise routing scenarios.
|
||||
**A high-performance, Rust-powered proxy toolkit for Node.js** — unified route-based configuration for SSL/TLS termination, HTTP/HTTPS reverse proxying, WebSocket support, load balancing, custom protocol handlers, and kernel-level NFTables forwarding.
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
@@ -16,22 +16,26 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
||||
|
||||
## 🎯 What is SmartProxy?
|
||||
|
||||
SmartProxy is a modern, production-ready proxy solution that brings order to the chaos of traffic management. Whether you're building microservices, deploying edge infrastructure, or need a battle-tested reverse proxy, SmartProxy has you covered.
|
||||
SmartProxy is a production-ready proxy solution that takes the complexity out of traffic management. Under the hood, all networking — TCP, TLS, HTTP reverse proxy, connection tracking, security enforcement, and NFTables — is handled by a **Rust engine** for maximum performance, while you configure everything through a clean TypeScript API with full type safety.
|
||||
|
||||
Whether you're building microservices, deploying edge infrastructure, or need a battle-tested reverse proxy with automatic Let's Encrypt certificates, SmartProxy has you covered.
|
||||
|
||||
### ⚡ Key Features
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| 🦀 **Rust-Powered Engine** | All networking handled by a high-performance Rust binary via IPC |
|
||||
| 🔀 **Unified Route-Based Config** | Clean match/action patterns for intuitive traffic routing |
|
||||
| 🔒 **Automatic SSL/TLS** | Zero-config HTTPS with Let's Encrypt ACME integration |
|
||||
| 🎯 **Flexible Matching** | Route by port, domain, path, client IP, TLS version, or custom logic |
|
||||
| 🎯 **Flexible Matching** | Route by port, domain, path, client IP, TLS version, headers, or custom logic |
|
||||
| 🚄 **High-Performance** | Choose between user-space or kernel-level (NFTables) forwarding |
|
||||
| ⚖️ **Load Balancing** | Distribute traffic with health checks and multiple algorithms |
|
||||
| 🛡️ **Enterprise Security** | IP filtering, rate limiting, authentication, connection limits |
|
||||
| ⚖️ **Load Balancing** | Round-robin, least-connections, IP-hash with health checks |
|
||||
| 🛡️ **Enterprise Security** | IP filtering, rate limiting, basic auth, JWT auth, connection limits |
|
||||
| 🔌 **WebSocket Support** | First-class WebSocket proxying with ping/pong keep-alive |
|
||||
| 🎮 **Custom Protocols** | Socket handlers for implementing any protocol |
|
||||
| 🎮 **Custom Protocols** | Socket handlers for implementing any protocol in TypeScript |
|
||||
| 📊 **Live Metrics** | Real-time throughput, connection counts, and performance data |
|
||||
| 🔧 **Dynamic Management** | Add/remove ports and routes at runtime without restarts |
|
||||
| 🔄 **PROXY Protocol** | Full PROXY protocol v1/v2 support for preserving client information |
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
@@ -43,16 +47,16 @@ import { SmartProxy, createCompleteHttpsServer } from '@push.rocks/smartproxy';
|
||||
// Create a proxy with automatic HTTPS
|
||||
const proxy = new SmartProxy({
|
||||
acme: {
|
||||
email: 'ssl@yourdomain.com', // Your email for Let's Encrypt
|
||||
useProduction: true // Use production servers
|
||||
email: 'ssl@yourdomain.com',
|
||||
useProduction: true
|
||||
},
|
||||
routes: [
|
||||
// Complete HTTPS setup in one line! ✨
|
||||
// Complete HTTPS setup in one call! ✨
|
||||
...createCompleteHttpsServer('app.example.com', {
|
||||
host: 'localhost',
|
||||
port: 3000
|
||||
}, {
|
||||
certificate: 'auto' // Magic! 🎩
|
||||
certificate: 'auto' // Automatic Let's Encrypt cert 🎩
|
||||
})
|
||||
]
|
||||
});
|
||||
@@ -84,10 +88,11 @@ SmartProxy uses a powerful **match/action** pattern that makes routing predictab
|
||||
```
|
||||
|
||||
Every route consists of:
|
||||
- **Match** - What traffic to capture (ports, domains, paths, IPs)
|
||||
- **Action** - What to do with it (forward, redirect, block, socket-handler)
|
||||
- **Security** (optional) - Access controls, rate limits, authentication
|
||||
- **Name/Priority** (optional) - For identification and ordering
|
||||
- **Match** — What traffic to capture (ports, domains, paths, IPs, headers)
|
||||
- **Action** — What to do with it (`forward` or `socket-handler`)
|
||||
- **Security** (optional) — IP allow/block lists, rate limits, authentication
|
||||
- **Headers** (optional) — Request/response header manipulation with template variables
|
||||
- **Name/Priority** (optional) — For identification and ordering
|
||||
|
||||
### 🔄 TLS Modes
|
||||
|
||||
@@ -95,8 +100,8 @@ SmartProxy supports three TLS handling modes:
|
||||
|
||||
| Mode | Description | Use Case |
|
||||
|------|-------------|----------|
|
||||
| `passthrough` | Forward encrypted traffic as-is | Backend handles TLS |
|
||||
| `terminate` | Decrypt at proxy, forward plain | Standard reverse proxy |
|
||||
| `passthrough` | Forward encrypted traffic as-is (SNI-based routing) | Backend handles TLS |
|
||||
| `terminate` | Decrypt at proxy, forward plain HTTP to backend | Standard reverse proxy |
|
||||
| `terminate-and-reencrypt` | Decrypt, then re-encrypt to backend | Zero-trust environments |
|
||||
|
||||
## 💡 Common Use Cases
|
||||
@@ -116,53 +121,61 @@ const proxy = new SmartProxy({
|
||||
### ⚖️ Load Balancer with Health Checks
|
||||
|
||||
```typescript
|
||||
import { createLoadBalancerRoute } from '@push.rocks/smartproxy';
|
||||
import { SmartProxy, createLoadBalancerRoute } from '@push.rocks/smartproxy';
|
||||
|
||||
const route = createLoadBalancerRoute(
|
||||
'app.example.com',
|
||||
[
|
||||
{ host: 'server1.internal', port: 8080 },
|
||||
{ host: 'server2.internal', port: 8080 },
|
||||
{ host: 'server3.internal', port: 8080 }
|
||||
],
|
||||
{
|
||||
tls: { mode: 'terminate', certificate: 'auto' },
|
||||
loadBalancing: {
|
||||
algorithm: 'round-robin',
|
||||
healthCheck: {
|
||||
path: '/health',
|
||||
interval: 30000,
|
||||
timeout: 5000
|
||||
const proxy = new SmartProxy({
|
||||
routes: [
|
||||
createLoadBalancerRoute(
|
||||
'app.example.com',
|
||||
[
|
||||
{ host: 'server1.internal', port: 8080 },
|
||||
{ host: 'server2.internal', port: 8080 },
|
||||
{ host: 'server3.internal', port: 8080 }
|
||||
],
|
||||
{
|
||||
tls: { mode: 'terminate', certificate: 'auto' },
|
||||
loadBalancing: {
|
||||
algorithm: 'round-robin',
|
||||
healthCheck: {
|
||||
path: '/health',
|
||||
interval: 30000,
|
||||
timeout: 5000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
)
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 🔌 WebSocket Proxy
|
||||
|
||||
```typescript
|
||||
import { createWebSocketRoute } from '@push.rocks/smartproxy';
|
||||
import { SmartProxy, createWebSocketRoute } from '@push.rocks/smartproxy';
|
||||
|
||||
const route = createWebSocketRoute(
|
||||
'ws.example.com',
|
||||
{ host: 'websocket-server', port: 8080 },
|
||||
{
|
||||
path: '/socket',
|
||||
useTls: true,
|
||||
certificate: 'auto',
|
||||
pingInterval: 30000, // Keep connections alive
|
||||
pingTimeout: 10000
|
||||
}
|
||||
);
|
||||
const proxy = new SmartProxy({
|
||||
routes: [
|
||||
createWebSocketRoute(
|
||||
'ws.example.com',
|
||||
{ host: 'websocket-server', port: 8080 },
|
||||
{
|
||||
path: '/socket',
|
||||
useTls: true,
|
||||
certificate: 'auto',
|
||||
pingInterval: 30000,
|
||||
pingTimeout: 10000
|
||||
}
|
||||
)
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 🚦 API Gateway with Rate Limiting
|
||||
|
||||
```typescript
|
||||
import { createApiGatewayRoute, addRateLimiting } from '@push.rocks/smartproxy';
|
||||
import { SmartProxy, createApiGatewayRoute, addRateLimiting } from '@push.rocks/smartproxy';
|
||||
|
||||
let route = createApiGatewayRoute(
|
||||
let apiRoute = createApiGatewayRoute(
|
||||
'api.example.com',
|
||||
'/api',
|
||||
{ host: 'api-backend', port: 8080 },
|
||||
@@ -173,20 +186,22 @@ let route = createApiGatewayRoute(
|
||||
}
|
||||
);
|
||||
|
||||
// Add rate limiting - 100 requests per minute per IP
|
||||
route = addRateLimiting(route, {
|
||||
// Add rate limiting — 100 requests per minute per IP
|
||||
apiRoute = addRateLimiting(apiRoute, {
|
||||
maxRequests: 100,
|
||||
window: 60,
|
||||
keyBy: 'ip'
|
||||
});
|
||||
|
||||
const proxy = new SmartProxy({ routes: [apiRoute] });
|
||||
```
|
||||
|
||||
### 🎮 Custom Protocol Handler
|
||||
|
||||
SmartProxy lets you implement any protocol with full socket control:
|
||||
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:
|
||||
|
||||
```typescript
|
||||
import { createSocketHandlerRoute, SocketHandlers } from '@push.rocks/smartproxy';
|
||||
import { SmartProxy, createSocketHandlerRoute, SocketHandlers } from '@push.rocks/smartproxy';
|
||||
|
||||
// Use pre-built handlers
|
||||
const echoRoute = createSocketHandlerRoute(
|
||||
@@ -214,18 +229,21 @@ const customRoute = createSocketHandlerRoute(
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const proxy = new SmartProxy({ routes: [echoRoute, customRoute] });
|
||||
```
|
||||
|
||||
**Pre-built Socket Handlers:**
|
||||
|
||||
| Handler | Description |
|
||||
|---------|-------------|
|
||||
| `SocketHandlers.echo` | Echo server - returns everything sent |
|
||||
| `SocketHandlers.echo` | Echo server — returns everything sent |
|
||||
| `SocketHandlers.proxy(host, port)` | TCP proxy to another server |
|
||||
| `SocketHandlers.lineProtocol(handler)` | Line-based text protocol |
|
||||
| `SocketHandlers.httpResponse(code, body)` | Simple HTTP response |
|
||||
| `SocketHandlers.httpRedirect(url, code)` | HTTP redirect with templates |
|
||||
| `SocketHandlers.httpRedirect(url, code)` | HTTP redirect with template variables (`{domain}`, `{path}`, `{port}`, `{clientIp}`) |
|
||||
| `SocketHandlers.httpServer(handler)` | Full HTTP request/response handling |
|
||||
| `SocketHandlers.httpBlock(status, message)` | HTTP block response |
|
||||
| `SocketHandlers.block(message)` | Block with optional message |
|
||||
|
||||
### ⚡ High-Performance NFTables Forwarding
|
||||
@@ -233,48 +251,73 @@ const customRoute = createSocketHandlerRoute(
|
||||
For ultra-low latency on Linux, use kernel-level forwarding (requires root):
|
||||
|
||||
```typescript
|
||||
import { createNfTablesTerminateRoute } from '@push.rocks/smartproxy';
|
||||
import { SmartProxy, createNfTablesTerminateRoute } from '@push.rocks/smartproxy';
|
||||
|
||||
const route = createNfTablesTerminateRoute(
|
||||
'fast.example.com',
|
||||
{ host: 'backend', port: 8080 },
|
||||
{
|
||||
ports: 443,
|
||||
certificate: 'auto',
|
||||
preserveSourceIP: true, // Backend sees real client IP
|
||||
maxRate: '1gbps' // QoS rate limiting
|
||||
}
|
||||
);
|
||||
const proxy = new SmartProxy({
|
||||
routes: [
|
||||
createNfTablesTerminateRoute(
|
||||
'fast.example.com',
|
||||
{ host: 'backend', port: 8080 },
|
||||
{
|
||||
ports: 443,
|
||||
certificate: 'auto',
|
||||
preserveSourceIP: true, // Backend sees real client IP
|
||||
maxRate: '1gbps' // QoS rate limiting
|
||||
}
|
||||
)
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 🔒 SNI Passthrough (TLS Passthrough)
|
||||
|
||||
Forward encrypted traffic to backends without terminating TLS — the proxy routes based on the SNI hostname alone:
|
||||
|
||||
```typescript
|
||||
import { SmartProxy, createHttpsPassthroughRoute } from '@push.rocks/smartproxy';
|
||||
|
||||
const proxy = new SmartProxy({
|
||||
routes: [
|
||||
createHttpsPassthroughRoute('secure.example.com', {
|
||||
host: 'backend-that-handles-tls',
|
||||
port: 8443
|
||||
})
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
## 🔧 Advanced Features
|
||||
|
||||
### 🎯 Dynamic Routing
|
||||
|
||||
Route traffic based on runtime conditions:
|
||||
Route traffic based on runtime conditions using function-based host/port resolution:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: 'business-hours-only',
|
||||
match: {
|
||||
ports: 443,
|
||||
domains: 'internal.example.com'
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{
|
||||
host: (context) => {
|
||||
// Dynamic host selection based on path
|
||||
return context.path?.startsWith('/premium')
|
||||
? 'premium-backend'
|
||||
: 'standard-backend';
|
||||
},
|
||||
port: 8080
|
||||
}]
|
||||
}
|
||||
}
|
||||
const proxy = new SmartProxy({
|
||||
routes: [{
|
||||
name: 'dynamic-backend',
|
||||
match: {
|
||||
ports: 443,
|
||||
domains: 'app.example.com'
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{
|
||||
host: (context) => {
|
||||
return context.path?.startsWith('/premium')
|
||||
? 'premium-backend'
|
||||
: 'standard-backend';
|
||||
},
|
||||
port: 8080
|
||||
}],
|
||||
tls: { mode: 'terminate', certificate: 'auto' }
|
||||
}
|
||||
}]
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** Routes with dynamic functions (host/port callbacks) are automatically relayed through the TypeScript socket handler server, since JavaScript functions can't be serialized to Rust.
|
||||
|
||||
### 🔒 Security Controls
|
||||
|
||||
Comprehensive per-route security options:
|
||||
@@ -285,7 +328,8 @@ Comprehensive per-route security options:
|
||||
match: { ports: 443, domains: 'api.example.com' },
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{ host: 'api-backend', port: 8080 }]
|
||||
targets: [{ host: 'api-backend', port: 8080 }],
|
||||
tls: { mode: 'terminate', certificate: 'auto' }
|
||||
},
|
||||
security: {
|
||||
// IP-based access control
|
||||
@@ -294,17 +338,31 @@ Comprehensive per-route security options:
|
||||
|
||||
// Connection limits
|
||||
maxConnections: 1000,
|
||||
maxConnectionsPerIp: 10,
|
||||
|
||||
// Rate limiting
|
||||
rateLimit: {
|
||||
enabled: true,
|
||||
maxRequests: 100,
|
||||
windowMs: 60000
|
||||
}
|
||||
window: 60
|
||||
},
|
||||
|
||||
// Authentication
|
||||
basicAuth: { users: [{ username: 'admin', password: 'secret' }] },
|
||||
jwtAuth: { secret: 'your-jwt-secret', algorithm: 'HS256' }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Security modifier helpers** let you add security to any existing route:
|
||||
|
||||
```typescript
|
||||
import { addRateLimiting, addBasicAuth, addJwtAuth } from '@push.rocks/smartproxy';
|
||||
|
||||
let route = createHttpsTerminateRoute('api.example.com', { host: 'backend', port: 8080 });
|
||||
route = addRateLimiting(route, { maxRequests: 100, window: 60, keyBy: 'ip' });
|
||||
route = addBasicAuth(route, { users: [{ username: 'admin', password: 'secret' }] });
|
||||
```
|
||||
|
||||
### 📊 Runtime Management
|
||||
|
||||
Control your proxy without restarts:
|
||||
@@ -313,21 +371,26 @@ Control your proxy without restarts:
|
||||
// Dynamic port management
|
||||
await proxy.addListeningPort(8443);
|
||||
await proxy.removeListeningPort(8080);
|
||||
const ports = await proxy.getListeningPorts();
|
||||
|
||||
// Update routes on the fly
|
||||
// Update routes on the fly (atomic, mutex-locked)
|
||||
await proxy.updateRoutes([...newRoutes]);
|
||||
|
||||
// Monitor status
|
||||
const status = proxy.getStatus();
|
||||
console.log(`Active connections: ${status.activeConnections}`);
|
||||
|
||||
// Get detailed metrics
|
||||
// Get real-time metrics
|
||||
const metrics = proxy.getMetrics();
|
||||
console.log(`Throughput: ${metrics.throughput.bytesPerSecond} bytes/sec`);
|
||||
console.log(`Active connections: ${metrics.connections.active()}`);
|
||||
console.log(`Requests/sec: ${metrics.throughput.requestsPerSecond()}`);
|
||||
|
||||
// Get detailed statistics from the Rust engine
|
||||
const stats = await proxy.getStatistics();
|
||||
|
||||
// Certificate management
|
||||
const certInfo = proxy.getCertificateInfo('example.com');
|
||||
console.log(`Certificate expires: ${certInfo.expiresAt}`);
|
||||
await proxy.provisionCertificate('my-route-name');
|
||||
await proxy.renewCertificate('my-route-name');
|
||||
const certStatus = await proxy.getCertificateStatus('my-route-name');
|
||||
|
||||
// NFTables status
|
||||
const nftStatus = await proxy.getNfTablesStatus();
|
||||
```
|
||||
|
||||
### 🔄 Header Manipulation
|
||||
@@ -338,51 +401,107 @@ Transform requests and responses with template variables:
|
||||
{
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{ host: 'backend', port: 8080 }],
|
||||
headers: {
|
||||
request: {
|
||||
'X-Real-IP': '{clientIp}',
|
||||
'X-Request-ID': '{uuid}',
|
||||
'X-Forwarded-Proto': 'https'
|
||||
},
|
||||
response: {
|
||||
'X-Powered-By': 'SmartProxy',
|
||||
'Strict-Transport-Security': 'max-age=31536000',
|
||||
'X-Frame-Options': 'DENY'
|
||||
}
|
||||
targets: [{ host: 'backend', port: 8080 }]
|
||||
},
|
||||
headers: {
|
||||
request: {
|
||||
'X-Real-IP': '{clientIp}',
|
||||
'X-Request-ID': '{uuid}',
|
||||
'X-Forwarded-Proto': 'https'
|
||||
},
|
||||
response: {
|
||||
'Strict-Transport-Security': 'max-age=31536000',
|
||||
'X-Frame-Options': 'DENY'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 🔀 PROXY Protocol Support
|
||||
|
||||
Preserve original client information through proxy chains:
|
||||
|
||||
```typescript
|
||||
const proxy = new SmartProxy({
|
||||
// Accept PROXY protocol from trusted load balancers
|
||||
acceptProxyProtocol: true,
|
||||
proxyIPs: ['10.0.0.1', '10.0.0.2'],
|
||||
|
||||
// Forward PROXY protocol to backends
|
||||
sendProxyProtocol: true,
|
||||
|
||||
routes: [...]
|
||||
});
|
||||
```
|
||||
|
||||
### 🏗️ Custom Certificate Provisioning
|
||||
|
||||
Supply your own certificates or integrate with external certificate providers:
|
||||
|
||||
```typescript
|
||||
const proxy = new SmartProxy({
|
||||
certProvisionFunction: async (domain: string) => {
|
||||
// Return 'http01' to let the built-in ACME handle it
|
||||
if (domain.endsWith('.example.com')) return 'http01';
|
||||
|
||||
// Or return a static certificate object
|
||||
return {
|
||||
publicKey: myPemCert,
|
||||
privateKey: myPemKey,
|
||||
};
|
||||
},
|
||||
certProvisionFallbackToAcme: true, // Fall back to ACME if callback fails
|
||||
routes: [...]
|
||||
});
|
||||
```
|
||||
|
||||
## 🏛️ Architecture
|
||||
|
||||
SmartProxy is built with a modular, extensible architecture:
|
||||
SmartProxy uses a hybrid **Rust + TypeScript** architecture:
|
||||
|
||||
```
|
||||
SmartProxy
|
||||
├── 📋 RouteManager # Route matching and prioritization
|
||||
├── 🔌 PortManager # Dynamic port lifecycle management
|
||||
├── 🔒 SmartCertManager # ACME/Let's Encrypt automation
|
||||
├── 🚦 ConnectionManager # Connection pooling and tracking
|
||||
├── 📊 MetricsCollector # Real-time performance monitoring
|
||||
├── 🛡️ SecurityManager # Access control and rate limiting
|
||||
├── 🔧 ProtocolDetector # Smart HTTP/TLS/WebSocket detection
|
||||
├── ⚡ NFTablesManager # Kernel-level forwarding (Linux)
|
||||
└── 🌐 HttpProxyBridge # HTTP/HTTPS request handling
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Your Application │
|
||||
│ (TypeScript — routes, config, socket handlers) │
|
||||
└──────────────────┬──────────────────────────────────┘
|
||||
│ IPC (JSON over stdin/stdout)
|
||||
┌──────────────────▼──────────────────────────────────┐
|
||||
│ Rust Proxy Engine │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
|
||||
│ │ TCP/TLS │ │ HTTP │ │ Route │ │ ACME │ │
|
||||
│ │ Listener│ │ Reverse │ │ Matcher │ │ Cert Mgr │ │
|
||||
│ │ │ │ Proxy │ │ │ │ │ │
|
||||
│ └─────────┘ └─────────┘ └─────────┘ └──────────┘ │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
|
||||
│ │ Security│ │ Metrics │ │ Connec- │ │ NFTables │ │
|
||||
│ │ Enforce │ │ Collect │ │ tion │ │ Mgr │ │
|
||||
│ │ │ │ │ │ Tracker │ │ │ │
|
||||
│ └─────────┘ └─────────┘ └─────────┘ └──────────┘ │
|
||||
└──────────────────┬──────────────────────────────────┘
|
||||
│ Unix Socket Relay
|
||||
┌──────────────────▼──────────────────────────────────┐
|
||||
│ TypeScript Socket Handler Server │
|
||||
│ (for JS-defined socket handlers & dynamic routes) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **Rust Engine** handles all networking, TLS, HTTP proxying, connection management, security, and metrics
|
||||
- **TypeScript** provides the npm API, configuration types, route helpers, validation, and socket handler callbacks
|
||||
- **IPC** — JSON commands/events over stdin/stdout for seamless cross-language communication
|
||||
- **Socket Relay** — a Unix domain socket server for routes requiring TypeScript-side handling (socket handlers, dynamic host/port functions)
|
||||
|
||||
## 🎯 Route Configuration Reference
|
||||
|
||||
### Match Criteria
|
||||
|
||||
```typescript
|
||||
interface IRouteMatch {
|
||||
ports: number | number[] | string; // 80, [80, 443], '8000-8999'
|
||||
domains?: string | string[]; // 'example.com', '*.example.com'
|
||||
path?: string; // '/api/*', '/users/:id'
|
||||
clientIp?: string | string[]; // '10.0.0.0/8', ['192.168.*']
|
||||
tlsVersion?: string | string[]; // ['TLSv1.2', 'TLSv1.3']
|
||||
ports: number | number[] | Array<{ from: number; to: number }>; // Port(s) to listen on
|
||||
domains?: string | string[]; // 'example.com', '*.example.com'
|
||||
path?: string; // '/api/*', '/users/:id'
|
||||
clientIp?: string[]; // ['10.0.0.0/8', '192.168.*']
|
||||
tlsVersion?: string[]; // ['TLSv1.2', 'TLSv1.3']
|
||||
headers?: Record<string, string | RegExp>; // Match by HTTP headers
|
||||
}
|
||||
```
|
||||
|
||||
@@ -390,69 +509,251 @@ interface IRouteMatch {
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `forward` | Proxy to one or more backend targets |
|
||||
| `redirect` | HTTP redirect with status code |
|
||||
| `block` | Block the connection |
|
||||
| `socket-handler` | Custom socket handling function |
|
||||
| `forward` | Proxy to one or more backend targets (with optional TLS, WebSocket, load balancing) |
|
||||
| `socket-handler` | Custom socket handling function in TypeScript |
|
||||
|
||||
### Target Options
|
||||
|
||||
```typescript
|
||||
interface IRouteTarget {
|
||||
host: string | string[] | ((context: IRouteContext) => string);
|
||||
port: number | 'preserve' | ((context: IRouteContext) => number);
|
||||
tls?: { ... }; // Per-target TLS override
|
||||
priority?: number; // Target priority
|
||||
match?: ITargetMatch; // Sub-match within a route (by port, path, headers, method)
|
||||
}
|
||||
```
|
||||
|
||||
### TLS Options
|
||||
|
||||
```typescript
|
||||
interface IRouteTls {
|
||||
mode: 'passthrough' | 'terminate' | 'terminate-and-reencrypt';
|
||||
certificate: 'auto' | { key: string; cert: string };
|
||||
// For terminate-and-reencrypt:
|
||||
reencrypt?: {
|
||||
host: string;
|
||||
port: number;
|
||||
ca?: string; // Custom CA for backend
|
||||
certificate: 'auto' | {
|
||||
key: string;
|
||||
cert: string;
|
||||
ca?: string;
|
||||
keyFile?: string;
|
||||
certFile?: string;
|
||||
};
|
||||
acme?: {
|
||||
email: string;
|
||||
useProduction?: boolean;
|
||||
challengePort?: number;
|
||||
renewBeforeDays?: number;
|
||||
};
|
||||
versions?: string[];
|
||||
ciphers?: string[];
|
||||
honorCipherOrder?: boolean;
|
||||
sessionTimeout?: number;
|
||||
}
|
||||
```
|
||||
|
||||
### WebSocket Options
|
||||
|
||||
```typescript
|
||||
interface IRouteWebSocket {
|
||||
enabled: boolean;
|
||||
pingInterval?: number; // ms between pings
|
||||
pingTimeout?: number; // ms to wait for pong
|
||||
maxPayloadSize?: number; // Maximum frame payload
|
||||
subprotocols?: string[]; // Allowed subprotocols
|
||||
allowedOrigins?: string[]; // CORS origins
|
||||
}
|
||||
```
|
||||
|
||||
### Load Balancing Options
|
||||
|
||||
```typescript
|
||||
interface IRouteLoadBalancing {
|
||||
algorithm: 'round-robin' | 'least-connections' | 'ip-hash';
|
||||
healthCheck?: {
|
||||
path: string;
|
||||
interval: number; // ms
|
||||
timeout: number; // ms
|
||||
unhealthyThreshold?: number;
|
||||
healthyThreshold?: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 🛠️ Helper Functions Reference
|
||||
|
||||
All helpers are fully typed and documented:
|
||||
All helpers are fully typed and return `IRouteConfig` or `IRouteConfig[]`:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
// HTTP/HTTPS
|
||||
createHttpRoute,
|
||||
createHttpsTerminateRoute,
|
||||
createHttpsPassthroughRoute,
|
||||
createHttpToHttpsRedirect,
|
||||
createCompleteHttpsServer,
|
||||
createHttpRoute, // Plain HTTP route
|
||||
createHttpsTerminateRoute, // HTTPS with TLS termination
|
||||
createHttpsPassthroughRoute, // SNI passthrough (no termination)
|
||||
createHttpToHttpsRedirect, // HTTP → HTTPS redirect
|
||||
createCompleteHttpsServer, // HTTPS + redirect combo (returns IRouteConfig[])
|
||||
|
||||
// Load Balancing
|
||||
createLoadBalancerRoute,
|
||||
createSmartLoadBalancer,
|
||||
createLoadBalancerRoute, // Multi-backend with health checks
|
||||
createSmartLoadBalancer, // Dynamic domain-based backend selection
|
||||
|
||||
// API & WebSocket
|
||||
createApiRoute,
|
||||
createApiGatewayRoute,
|
||||
createWebSocketRoute,
|
||||
createApiRoute, // API route with path matching
|
||||
createApiGatewayRoute, // API gateway with CORS
|
||||
createWebSocketRoute, // WebSocket-enabled route
|
||||
|
||||
// Custom Protocols
|
||||
createSocketHandlerRoute,
|
||||
SocketHandlers,
|
||||
createSocketHandlerRoute, // Custom socket handler
|
||||
SocketHandlers, // Pre-built handlers (echo, proxy, block, etc.)
|
||||
|
||||
// NFTables (Linux)
|
||||
createNfTablesRoute,
|
||||
createNfTablesTerminateRoute,
|
||||
createCompleteNfTablesHttpsServer,
|
||||
// NFTables (Linux, requires root)
|
||||
createNfTablesRoute, // Kernel-level packet forwarding
|
||||
createNfTablesTerminateRoute, // NFTables + TLS termination
|
||||
createCompleteNfTablesHttpsServer, // NFTables HTTPS + redirect combo
|
||||
|
||||
// Dynamic Routing
|
||||
createPortMappingRoute,
|
||||
createOffsetPortMappingRoute,
|
||||
createDynamicRoute,
|
||||
createPortMappingRoute, // Port mapping with context
|
||||
createOffsetPortMappingRoute, // Simple port offset
|
||||
createDynamicRoute, // Dynamic host/port via functions
|
||||
|
||||
// Security Modifiers
|
||||
addRateLimiting,
|
||||
addBasicAuth,
|
||||
addJwtAuth
|
||||
addRateLimiting, // Add rate limiting to any route
|
||||
addBasicAuth, // Add basic auth to any route
|
||||
addJwtAuth, // Add JWT auth to any route
|
||||
|
||||
// Route Utilities
|
||||
mergeRouteConfigs, // Deep-merge two route configs
|
||||
findMatchingRoutes, // Find routes matching criteria
|
||||
findBestMatchingRoute, // Find best matching route
|
||||
cloneRoute, // Deep-clone a route
|
||||
generateRouteId, // Generate deterministic route ID
|
||||
RouteValidator, // Validate route configurations
|
||||
} from '@push.rocks/smartproxy';
|
||||
```
|
||||
|
||||
## 📖 API Documentation
|
||||
|
||||
### SmartProxy Class
|
||||
|
||||
```typescript
|
||||
class SmartProxy extends EventEmitter {
|
||||
constructor(options: ISmartProxyOptions);
|
||||
|
||||
// Lifecycle
|
||||
start(): Promise<void>;
|
||||
stop(): Promise<void>;
|
||||
|
||||
// Route Management (atomic, mutex-locked)
|
||||
updateRoutes(routes: IRouteConfig[]): Promise<void>;
|
||||
|
||||
// Port Management
|
||||
addListeningPort(port: number): Promise<void>;
|
||||
removeListeningPort(port: number): Promise<void>;
|
||||
getListeningPorts(): Promise<number[]>;
|
||||
|
||||
// Monitoring & Metrics
|
||||
getMetrics(): IMetrics; // Sync — returns cached metrics adapter
|
||||
getStatistics(): Promise<any>; // Async — queries Rust engine
|
||||
|
||||
// Certificate Management
|
||||
provisionCertificate(routeName: string): Promise<void>;
|
||||
renewCertificate(routeName: string): Promise<void>;
|
||||
getCertificateStatus(routeName: string): Promise<any>;
|
||||
getEligibleDomainsForCertificates(): string[];
|
||||
|
||||
// NFTables
|
||||
getNfTablesStatus(): Promise<Record<string, any>>;
|
||||
|
||||
// Events
|
||||
on(event: 'error', handler: (err: Error) => void): this;
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
|
||||
```typescript
|
||||
interface ISmartProxyOptions {
|
||||
routes: IRouteConfig[]; // Required: array of route configs
|
||||
|
||||
// ACME/Let's Encrypt
|
||||
acme?: {
|
||||
email: string; // Contact email for Let's Encrypt
|
||||
useProduction?: boolean; // Use production servers (default: false)
|
||||
port?: number; // HTTP-01 challenge port (default: 80)
|
||||
renewThresholdDays?: number; // Days before expiry to renew (default: 30)
|
||||
autoRenew?: boolean; // Enable auto-renewal (default: true)
|
||||
certificateStore?: string; // Directory to store certs (default: './certs')
|
||||
renewCheckIntervalHours?: number; // Renewal check interval (default: 24)
|
||||
};
|
||||
|
||||
// Custom certificate provisioning
|
||||
certProvisionFunction?: (domain: string) => Promise<ICert | 'http01'>;
|
||||
certProvisionFallbackToAcme?: boolean; // Fall back to ACME on failure (default: true)
|
||||
|
||||
// Global defaults
|
||||
defaults?: {
|
||||
target?: { host: string; port: number };
|
||||
security?: { ipAllowList?: string[]; ipBlockList?: string[]; maxConnections?: number };
|
||||
};
|
||||
|
||||
// PROXY protocol
|
||||
proxyIPs?: string[]; // Trusted proxy IPs
|
||||
acceptProxyProtocol?: boolean; // Accept PROXY protocol headers
|
||||
sendProxyProtocol?: boolean; // Send PROXY protocol to targets
|
||||
|
||||
// Timeouts
|
||||
connectionTimeout?: number; // Backend connection timeout (default: 30s)
|
||||
initialDataTimeout?: number; // Initial data/SNI timeout (default: 120s)
|
||||
socketTimeout?: number; // Socket inactivity timeout (default: 1h)
|
||||
maxConnectionLifetime?: number; // Max connection lifetime (default: 24h)
|
||||
inactivityTimeout?: number; // Inactivity timeout (default: 4h)
|
||||
gracefulShutdownTimeout?: number; // Shutdown grace period (default: 30s)
|
||||
|
||||
// Connection limits
|
||||
maxConnectionsPerIP?: number; // Per-IP connection limit (default: 100)
|
||||
connectionRateLimitPerMinute?: number; // Per-IP rate limit (default: 300/min)
|
||||
|
||||
// Keep-alive
|
||||
keepAliveTreatment?: 'standard' | 'extended' | 'immortal';
|
||||
keepAliveInactivityMultiplier?: number; // (default: 6)
|
||||
extendedKeepAliveLifetime?: number; // (default: 7 days)
|
||||
|
||||
// Metrics
|
||||
metrics?: {
|
||||
enabled?: boolean;
|
||||
sampleIntervalMs?: number;
|
||||
retentionSeconds?: number;
|
||||
};
|
||||
|
||||
// Behavior
|
||||
enableDetailedLogging?: boolean; // Verbose connection logging
|
||||
enableTlsDebugLogging?: boolean; // TLS handshake debug logging
|
||||
|
||||
// Rust binary
|
||||
rustBinaryPath?: string; // Custom path to the Rust binary
|
||||
}
|
||||
```
|
||||
|
||||
### NfTablesProxy Class
|
||||
|
||||
A standalone class for managing nftables NAT rules directly (Linux only, requires root):
|
||||
|
||||
```typescript
|
||||
import { NfTablesProxy } from '@push.rocks/smartproxy';
|
||||
|
||||
const nftProxy = new NfTablesProxy({
|
||||
fromPorts: [80, 443],
|
||||
toHost: 'backend-server',
|
||||
toPorts: [8080, 8443],
|
||||
protocol: 'tcp',
|
||||
preserveSourceIP: true,
|
||||
enableIPv6: true,
|
||||
maxRate: '1gbps',
|
||||
useIPSets: true
|
||||
});
|
||||
|
||||
await nftProxy.start(); // Apply nftables rules
|
||||
const status = nftProxy.getStatus();
|
||||
await nftProxy.stop(); // Remove rules
|
||||
```
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Certificate Issues
|
||||
@@ -460,93 +761,41 @@ import {
|
||||
- ✅ Port 80 must be accessible for ACME HTTP-01 challenges
|
||||
- ✅ Check DNS propagation with `dig` or `nslookup`
|
||||
- ✅ Verify the email in ACME configuration is valid
|
||||
- ✅ Use `getCertificateStatus('route-name')` to check cert state
|
||||
|
||||
### Connection Problems
|
||||
- ✅ Check route priorities (higher number = matched first)
|
||||
- ✅ Verify security rules aren't blocking legitimate traffic
|
||||
- ✅ Test with `curl -v` for detailed connection output
|
||||
- ✅ Enable debug logging for verbose output
|
||||
- ✅ Enable debug logging with `enableDetailedLogging: true`
|
||||
|
||||
### Rust Binary Not Found
|
||||
SmartProxy searches for the Rust binary in this order:
|
||||
1. `SMARTPROXY_RUST_BINARY` environment variable
|
||||
2. Platform-specific npm package (`@push.rocks/smartproxy-linux-x64`, etc.)
|
||||
3. Local dev build (`./rust/target/release/rustproxy`)
|
||||
4. System PATH (`rustproxy`)
|
||||
|
||||
Set `rustBinaryPath` in options to override.
|
||||
|
||||
### Performance Tuning
|
||||
- ✅ Use NFTables forwarding for high-traffic routes (Linux only)
|
||||
- ✅ Enable connection keep-alive where appropriate
|
||||
- ✅ Monitor metrics to identify bottlenecks
|
||||
- ✅ Adjust `maxConnections` based on your server resources
|
||||
|
||||
### Debug Mode
|
||||
|
||||
```typescript
|
||||
const proxy = new SmartProxy({
|
||||
enableDetailedLogging: true, // Verbose connection logging
|
||||
routes: [...]
|
||||
});
|
||||
```
|
||||
- ✅ Use `getMetrics()` and `getStatistics()` to identify bottlenecks
|
||||
- ✅ Adjust `maxConnectionsPerIP` and `connectionRateLimitPerMinute` based on your workload
|
||||
- ✅ Use `passthrough` TLS mode when backend can handle TLS directly
|
||||
|
||||
## 🏆 Best Practices
|
||||
|
||||
1. **📝 Use Helper Functions** - They provide sensible defaults and prevent common mistakes
|
||||
2. **🎯 Set Route Priorities** - More specific routes should have higher priority values
|
||||
3. **🔒 Enable Security** - Always use IP filtering and rate limiting for public services
|
||||
4. **📊 Monitor Metrics** - Use the built-in metrics to identify issues early
|
||||
5. **🔄 Certificate Monitoring** - Set up alerts for certificate expiration
|
||||
6. **🛑 Graceful Shutdown** - Always call `proxy.stop()` for clean connection termination
|
||||
7. **🔧 Test Routes** - Validate your route configurations before deploying to production
|
||||
|
||||
## 📖 API Documentation
|
||||
|
||||
### SmartProxy Class
|
||||
|
||||
```typescript
|
||||
class SmartProxy {
|
||||
constructor(options: ISmartProxyOptions);
|
||||
|
||||
// Lifecycle
|
||||
start(): Promise<void>;
|
||||
stop(): Promise<void>;
|
||||
|
||||
// Route Management
|
||||
updateRoutes(routes: IRouteConfig[]): Promise<void>;
|
||||
|
||||
// Port Management
|
||||
addListeningPort(port: number): Promise<void>;
|
||||
removeListeningPort(port: number): Promise<void>;
|
||||
getListeningPorts(): number[];
|
||||
|
||||
// Monitoring
|
||||
getStatus(): IProxyStatus;
|
||||
getMetrics(): IMetrics;
|
||||
|
||||
// Certificate Management
|
||||
getCertificateInfo(domain: string): ICertStatus | null;
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
|
||||
```typescript
|
||||
interface ISmartProxyOptions {
|
||||
routes: IRouteConfig[]; // Required: array of route configs
|
||||
|
||||
// ACME/Let's Encrypt
|
||||
acme?: {
|
||||
email: string; // Contact email
|
||||
useProduction?: boolean; // Use production servers (default: false)
|
||||
port?: number; // Challenge port (default: 80)
|
||||
renewThresholdDays?: number; // Days before expiry to renew (default: 30)
|
||||
};
|
||||
|
||||
// Defaults
|
||||
defaults?: {
|
||||
target?: { host: string; port: number };
|
||||
security?: IRouteSecurity;
|
||||
tls?: IRouteTls;
|
||||
};
|
||||
|
||||
// Behavior
|
||||
enableDetailedLogging?: boolean;
|
||||
gracefulShutdownTimeout?: number; // ms to wait for connections to close
|
||||
}
|
||||
```
|
||||
1. **📝 Use Helper Functions** — They provide sensible defaults and prevent common mistakes
|
||||
2. **🎯 Set Route Priorities** — More specific routes should have higher priority values
|
||||
3. **🔒 Enable Security** — Always use IP filtering and rate limiting for public-facing services
|
||||
4. **📊 Monitor Metrics** — Use the built-in metrics to catch issues early
|
||||
5. **🔄 Certificate Monitoring** — Set up alerts before certificates expire
|
||||
6. **🛑 Graceful Shutdown** — Always call `proxy.stop()` for clean connection termination
|
||||
7. **✅ Validate Routes** — Use `RouteValidator.validateRoutes()` to catch config errors before deployment
|
||||
8. **🔀 Atomic Updates** — Use `updateRoutes()` for hot-reloading routes (mutex-locked, no downtime)
|
||||
9. **🎮 Use Socket Handlers** — For protocols beyond HTTP, implement custom socket handlers instead of fighting the proxy model
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
|
||||
Reference in New Issue
Block a user