476 lines
16 KiB
Markdown
476 lines
16 KiB
Markdown
# @push.rocks/smartproxy
|
|
|
|
A high-performance proxy toolkit for Node.js, offering:
|
|
- HTTP/HTTPS reverse proxy with TLS termination and WebSocket support
|
|
- Automatic ACME certificate management (HTTP-01)
|
|
- Low-level port forwarding via nftables
|
|
- HTTP-to-HTTPS and custom URL redirects
|
|
- Advanced TCP/SNI-based proxying with IP filtering and rules
|
|
|
|
## Exports
|
|
The following classes and interfaces are provided:
|
|
|
|
- **NetworkProxy** (ts/networkproxy/classes.np.networkproxy.ts)
|
|
HTTP/HTTPS reverse proxy with TLS termination, WebSocket support,
|
|
connection pooling, and optional ACME integration.
|
|
- **Port80Handler** (ts/port80handler/classes.port80handler.ts)
|
|
ACME HTTP-01 challenge handler and certificate manager.
|
|
- **NfTablesProxy** (ts/nfttablesproxy/classes.nftablesproxy.ts)
|
|
Low-level port forwarding using nftables NAT rules.
|
|
- **Redirect**, **SslRedirect** (ts/redirect/classes.redirect.ts)
|
|
HTTP/HTTPS redirect server and shortcut for HTTP→HTTPS.
|
|
- **SmartProxy** (ts/smartproxy/classes.smartproxy.ts)
|
|
TCP/SNI-based proxy with dynamic routing, IP filtering, and unified certificates.
|
|
- **SniHandler** (ts/smartproxy/classes.pp.snihandler.ts)
|
|
Static utilities to extract SNI hostnames from TLS handshakes.
|
|
- **Interfaces**
|
|
- IPortProxySettings, IDomainConfig (ts/smartproxy/classes.pp.interfaces.ts)
|
|
- INetworkProxyOptions (ts/networkproxy/classes.np.types.ts)
|
|
- IAcmeOptions, IDomainOptions, IForwardConfig (ts/common/types.ts)
|
|
- INfTableProxySettings (ts/nfttablesproxy/classes.nftablesproxy.ts)
|
|
|
|
## Installation
|
|
Install via npm:
|
|
```bash
|
|
npm install @push.rocks/smartproxy
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
### 1. HTTP(S) Reverse Proxy (NetworkProxy)
|
|
```typescript
|
|
import { NetworkProxy } from '@push.rocks/smartproxy';
|
|
|
|
const proxy = new NetworkProxy({ port: 443 });
|
|
await proxy.start();
|
|
|
|
await proxy.updateProxyConfigs([
|
|
{
|
|
hostName: 'example.com',
|
|
destinationIps: ['127.0.0.1'],
|
|
destinationPorts: [3000],
|
|
publicKey: fs.readFileSync('cert.pem', 'utf8'),
|
|
privateKey: fs.readFileSync('key.pem', 'utf8'),
|
|
}
|
|
]);
|
|
|
|
// Add default headers to all responses
|
|
await proxy.addDefaultHeaders({
|
|
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains'
|
|
});
|
|
// ...
|
|
await proxy.stop();
|
|
```
|
|
|
|
### 2. HTTP→HTTPS Redirect (Redirect / SslRedirect)
|
|
```typescript
|
|
import { Redirect, SslRedirect } from '@push.rocks/smartproxy';
|
|
import * as fs from 'fs';
|
|
|
|
// Custom redirect rules
|
|
const redirect = new Redirect({
|
|
httpPort: 80,
|
|
httpsPort: 443,
|
|
sslOptions: {
|
|
key: fs.readFileSync('key.pem'),
|
|
cert: fs.readFileSync('cert.pem'),
|
|
},
|
|
rules: [
|
|
{
|
|
fromProtocol: 'http',
|
|
fromHost: '*',
|
|
toProtocol: 'https',
|
|
toHost: '$1',
|
|
statusCode: 301
|
|
}
|
|
]
|
|
});
|
|
await redirect.start();
|
|
|
|
// Quick HTTP→HTTPS helper on port 80
|
|
const quick = new SslRedirect(80);
|
|
await quick.start();
|
|
```
|
|
|
|
### 3. Automatic Certificates (ACME Port80Handler)
|
|
```typescript
|
|
import { Port80Handler } from '@push.rocks/smartproxy';
|
|
|
|
// Configure ACME on port 80 with contact email
|
|
const acme = new Port80Handler({
|
|
port: 80,
|
|
contactEmail: 'admin@example.com',
|
|
useProduction: true,
|
|
renewThresholdDays: 30
|
|
});
|
|
acme.on('certificate-issued', evt => {
|
|
console.log(`Certificate ready for ${evt.domain}, expires ${evt.expiryDate}`);
|
|
});
|
|
await acme.start();
|
|
acme.addDomain({
|
|
domainName: 'example.com',
|
|
sslRedirect: true,
|
|
acmeMaintenance: true
|
|
});
|
|
```
|
|
|
|
### 4. Low-Level Port Forwarding (NfTablesProxy)
|
|
```typescript
|
|
import { NfTablesProxy } from '@push.rocks/smartproxy';
|
|
|
|
// Forward port 80→8080 with source IP preservation
|
|
const nft = new NfTablesProxy({
|
|
fromPort: 80,
|
|
toPort: 8080,
|
|
toHost: 'localhost',
|
|
preserveSourceIP: true,
|
|
deleteOnExit: true
|
|
});
|
|
await nft.start();
|
|
// ...
|
|
await nft.stop();
|
|
```
|
|
|
|
### 5. TCP/SNI Proxy (SmartProxy)
|
|
```typescript
|
|
import { SmartProxy } from '@push.rocks/smartproxy';
|
|
|
|
const smart = new SmartProxy({
|
|
fromPort: 443,
|
|
toPort: 8443,
|
|
domainConfigs: [
|
|
{
|
|
domains: ['example.com', '*.example.com'],
|
|
allowedIPs: ['*'],
|
|
targetIPs: ['127.0.0.1'],
|
|
}
|
|
],
|
|
sniEnabled: true
|
|
});
|
|
smart.on('certificate', evt => console.log(evt));
|
|
await smart.start();
|
|
// Update domains later
|
|
await smart.updateDomainConfigs([/* new configs */]);
|
|
```
|
|
|
|
### 6. SNI Utilities (SniHandler)
|
|
```js
|
|
import { SniHandler } from '@push.rocks/smartproxy';
|
|
|
|
// Extract SNI from a TLS ClientHello buffer
|
|
const sni = SniHandler.extractSNI(buffer);
|
|
|
|
// Reassemble fragmented ClientHello
|
|
const complete = SniHandler.handleFragmentedClientHello(buf, connId);
|
|
```
|
|
|
|
## API Reference
|
|
For full configuration options and type definitions, see the TypeScript interfaces in the `ts/` directory:
|
|
- `INetworkProxyOptions` (ts/networkproxy/classes.np.types.ts)
|
|
- `IAcmeOptions`, `IDomainOptions`, `IForwardConfig` (ts/common/types.ts)
|
|
- `INfTableProxySettings` (ts/nfttablesproxy/classes.nftablesproxy.ts)
|
|
- `IPortProxySettings`, `IDomainConfig` (ts/smartproxy/classes.pp.interfaces.ts)
|
|
|
|
## Architecture & Flow Diagrams
|
|
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
Client([Client])
|
|
|
|
subgraph "SmartProxy Components"
|
|
direction TB
|
|
HTTP80["HTTP Port 80<br>Redirect / SslRedirect"]
|
|
HTTPS443["HTTPS Port 443<br>NetworkProxy"]
|
|
SmartProxy["SmartProxy<br>(TCP/SNI Proxy)"]
|
|
NfTables[NfTablesProxy]
|
|
Router[ProxyRouter]
|
|
ACME["Port80Handler<br>(ACME HTTP-01)"]
|
|
Certs[(SSL Certificates)]
|
|
end
|
|
|
|
subgraph "Backend Services"
|
|
Service1[Service 1]
|
|
Service2[Service 2]
|
|
Service3[Service 3]
|
|
end
|
|
|
|
Client -->|HTTP Request| HTTP80
|
|
HTTP80 -->|Redirect| Client
|
|
Client -->|HTTPS Request| HTTPS443
|
|
Client -->|TLS/TCP| SmartProxy
|
|
|
|
HTTPS443 -->|Route Request| Router
|
|
Router -->|Proxy Request| Service1
|
|
Router -->|Proxy Request| Service2
|
|
|
|
SmartProxy -->|Direct TCP| Service2
|
|
SmartProxy -->|Direct TCP| Service3
|
|
|
|
NfTables -.->|Low-level forwarding| SmartProxy
|
|
|
|
HTTP80 -.->|Challenge Response| ACME
|
|
ACME -.->|Generate/Manage| Certs
|
|
Certs -.->|Provide TLS Certs| HTTPS443
|
|
|
|
classDef component fill:#f9f,stroke:#333,stroke-width:2px;
|
|
classDef backend fill:#bbf,stroke:#333,stroke-width:1px;
|
|
classDef client fill:#dfd,stroke:#333,stroke-width:2px;
|
|
|
|
class Client client;
|
|
class HTTP80,HTTPS443,SmartProxy,NfTables,Router,ACME component;
|
|
class Service1,Service2,Service3 backend;
|
|
```
|
|
|
|
### HTTPS Reverse Proxy Flow
|
|
This diagram shows how HTTPS requests are handled and proxied to backend services:
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Client
|
|
participant NetworkProxy
|
|
participant ProxyRouter
|
|
participant Backend
|
|
|
|
Client->>NetworkProxy: HTTPS Request
|
|
|
|
Note over NetworkProxy: TLS Termination
|
|
|
|
NetworkProxy->>ProxyRouter: Route Request
|
|
ProxyRouter->>ProxyRouter: Match hostname to config
|
|
|
|
alt Authentication Required
|
|
NetworkProxy->>Client: Request Authentication
|
|
Client->>NetworkProxy: Send Credentials
|
|
NetworkProxy->>NetworkProxy: Validate Credentials
|
|
end
|
|
|
|
NetworkProxy->>Backend: Forward Request
|
|
Backend->>NetworkProxy: Response
|
|
|
|
Note over NetworkProxy: Add Default Headers
|
|
|
|
NetworkProxy->>Client: Forward Response
|
|
|
|
alt WebSocket Request
|
|
Client->>NetworkProxy: Upgrade to WebSocket
|
|
NetworkProxy->>Backend: Upgrade to WebSocket
|
|
loop WebSocket Active
|
|
Client->>NetworkProxy: WebSocket Message
|
|
NetworkProxy->>Backend: Forward Message
|
|
Backend->>NetworkProxy: WebSocket Message
|
|
NetworkProxy->>Client: Forward Message
|
|
NetworkProxy-->>NetworkProxy: Heartbeat Check
|
|
end
|
|
end
|
|
```
|
|
|
|
### SNI-based Connection Handling
|
|
This diagram illustrates how TCP connections with SNI (Server Name Indication) are processed and forwarded:
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Client
|
|
participant SmartProxy
|
|
participant Backend
|
|
|
|
Client->>SmartProxy: TLS Connection
|
|
|
|
alt SNI Enabled
|
|
SmartProxy->>Client: Accept Connection
|
|
Client->>SmartProxy: TLS ClientHello with SNI
|
|
SmartProxy->>SmartProxy: Extract SNI Hostname
|
|
SmartProxy->>SmartProxy: Match Domain Config
|
|
SmartProxy->>SmartProxy: Validate Client IP
|
|
|
|
alt IP Allowed
|
|
SmartProxy->>Backend: Forward Connection
|
|
Note over SmartProxy,Backend: Bidirectional Data Flow
|
|
else IP Rejected
|
|
SmartProxy->>Client: Close Connection
|
|
end
|
|
else Port-based Routing
|
|
SmartProxy->>SmartProxy: Match Port Range
|
|
SmartProxy->>SmartProxy: Find Domain Config
|
|
SmartProxy->>SmartProxy: Validate Client IP
|
|
|
|
alt IP Allowed
|
|
SmartProxy->>Backend: Forward Connection
|
|
Note over SmartProxy,Backend: Bidirectional Data Flow
|
|
else IP Rejected
|
|
SmartProxy->>Client: Close Connection
|
|
end
|
|
end
|
|
|
|
loop Connection Active
|
|
SmartProxy-->>SmartProxy: Monitor Activity
|
|
SmartProxy-->>SmartProxy: Check Max Lifetime
|
|
alt Inactivity or Max Lifetime Exceeded
|
|
SmartProxy->>Client: Close Connection
|
|
SmartProxy->>Backend: Close Connection
|
|
end
|
|
end
|
|
```
|
|
|
|
### Let's Encrypt Certificate Acquisition
|
|
This diagram shows how certificates are automatically acquired through the ACME protocol:
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Client
|
|
participant Port80Handler
|
|
participant ACME as Let's Encrypt ACME
|
|
participant NetworkProxy
|
|
|
|
Client->>Port80Handler: HTTP Request for domain
|
|
|
|
alt Certificate Exists
|
|
Port80Handler->>Client: Redirect to HTTPS
|
|
else No Certificate
|
|
Port80Handler->>Port80Handler: Mark domain as obtaining cert
|
|
Port80Handler->>ACME: Create account & new order
|
|
ACME->>Port80Handler: Challenge information
|
|
|
|
Port80Handler->>Port80Handler: Store challenge token & key authorization
|
|
|
|
ACME->>Port80Handler: HTTP-01 Challenge Request
|
|
Port80Handler->>ACME: Challenge Response
|
|
|
|
ACME->>ACME: Validate domain ownership
|
|
ACME->>Port80Handler: Challenge validated
|
|
|
|
Port80Handler->>Port80Handler: Generate CSR
|
|
Port80Handler->>ACME: Submit CSR
|
|
ACME->>Port80Handler: Issue Certificate
|
|
|
|
Port80Handler->>Port80Handler: Store certificate & private key
|
|
Port80Handler->>Port80Handler: Mark certificate as obtained
|
|
|
|
Note over Port80Handler,NetworkProxy: Certificate available for use
|
|
|
|
Client->>Port80Handler: Another HTTP Request
|
|
Port80Handler->>Client: Redirect to HTTPS
|
|
Client->>NetworkProxy: HTTPS Request
|
|
Note over NetworkProxy: Uses new certificate
|
|
end
|
|
```
|
|
|
|
## Features
|
|
|
|
- HTTP/HTTPS Reverse Proxy (NetworkProxy)
|
|
• TLS termination, virtual-host routing, HTTP/2 & WebSocket support, pooling & metrics
|
|
|
|
- Automatic ACME Certificates (Port80Handler)
|
|
• HTTP-01 challenge handling, certificate issuance/renewal, pluggable storage
|
|
|
|
- Low-Level Port Forwarding (NfTablesProxy)
|
|
• nftables NAT rules for ports/ranges, IPv4/IPv6, IP filtering, QoS & ipset support
|
|
|
|
- Custom Redirects (Redirect / SslRedirect)
|
|
• URL redirects with wildcard host/path, template variables & status codes
|
|
|
|
- TCP/SNI Proxy (SmartProxy)
|
|
• SNI-based routing, IP allow/block lists, port ranges, timeouts & graceful shutdown
|
|
|
|
- SNI Utilities (SniHandler)
|
|
• Robust ClientHello parsing, fragmentation & session resumption support
|
|
|
|
## Certificate Hooks & Events
|
|
|
|
Listen for certificate events via EventEmitter:
|
|
- **Port80Handler**:
|
|
- `certificate-issued`, `certificate-renewed`, `certificate-failed`
|
|
- `manager-started`, `manager-stopped`, `request-forwarded`
|
|
- **SmartProxy**:
|
|
- `certificate` (domain, publicKey, privateKey, expiryDate, source, isRenewal)
|
|
|
|
Provide a `certProvider(domain)` in SmartProxy settings to supply static certs or return `'http01'`.
|
|
|
|
## Configuration Options
|
|
|
|
### NetworkProxy (INetworkProxyOptions)
|
|
- `port` (number, required)
|
|
- `backendProtocol` ('http1'|'http2', default 'http1')
|
|
- `maxConnections` (number, default 10000)
|
|
- `keepAliveTimeout` (ms, default 120000)
|
|
- `headersTimeout` (ms, default 60000)
|
|
- `cors` (object)
|
|
- `connectionPoolSize` (number, default 50)
|
|
- `logLevel` ('error'|'warn'|'info'|'debug')
|
|
- `acme` (IAcmeOptions)
|
|
- `useExternalPort80Handler` (boolean)
|
|
- `portProxyIntegration` (boolean)
|
|
|
|
### Port80Handler (IAcmeOptions)
|
|
- `enabled` (boolean, default true)
|
|
- `port` (number, default 80)
|
|
- `contactEmail` (string)
|
|
- `useProduction` (boolean, default false)
|
|
- `renewThresholdDays` (number, default 30)
|
|
- `autoRenew` (boolean, default true)
|
|
- `certificateStore` (string)
|
|
- `skipConfiguredCerts` (boolean)
|
|
- `domainForwards` (IDomainForwardConfig[])
|
|
|
|
### NfTablesProxy (INfTableProxySettings)
|
|
- `fromPort` / `toPort` (number|range|array)
|
|
- `toHost` (string, default 'localhost')
|
|
- `preserveSourceIP`, `deleteOnExit`, `protocol`, `enableLogging`, `ipv6Support` (booleans)
|
|
- `allowedSourceIPs`, `bannedSourceIPs` (string[])
|
|
- `useIPSets` (boolean, default true)
|
|
- `qos`, `netProxyIntegration` (objects)
|
|
|
|
### Redirect / SslRedirect
|
|
- Constructor options: `httpPort`, `httpsPort`, `sslOptions`, `rules` (RedirectRule[])
|
|
|
|
### SmartProxy (IPortProxySettings)
|
|
- `fromPort`, `toPort` (number)
|
|
- `domainConfigs` (IDomainConfig[])
|
|
- `sniEnabled`, `defaultAllowedIPs`, `preserveSourceIP` (booleans)
|
|
- Timeouts: `initialDataTimeout`, `socketTimeout`, `inactivityTimeout`, etc.
|
|
- Socket opts: `noDelay`, `keepAlive`, `enableKeepAliveProbes`
|
|
- `acme` (IAcmeOptions), `certProvider` (callback)
|
|
- `useNetworkProxy` (number[]), `networkProxyPort` (number)
|
|
|
|
## Troubleshooting
|
|
|
|
### NetworkProxy
|
|
- Verify ports, certificates and `rejectUnauthorized` for TLS errors
|
|
- Configure CORS or use `addDefaultHeaders` for preflight issues
|
|
- Increase `maxConnections` or `connectionPoolSize` under load
|
|
|
|
### Port80Handler
|
|
- Run as root or grant CAP_NET_BIND_SERVICE for port 80
|
|
- Inspect `certificate-failed` events and switch staging/production
|
|
|
|
### NfTablesProxy
|
|
- Ensure `nft` is installed and run with sufficient privileges
|
|
- Use `forceCleanSlate:true` to clear conflicting rules
|
|
|
|
### Redirect / SslRedirect
|
|
- Check `fromHost`/`fromPath` patterns and Host headers
|
|
- Validate `sslOptions` key/cert correctness
|
|
|
|
### SmartProxy & SniHandler
|
|
- Increase `initialDataTimeout`/`maxPendingDataSize` for large ClientHello
|
|
- Enable `enableTlsDebugLogging` to trace handshake
|
|
- Ensure `allowSessionTicket` and fragmentation support for resumption
|
|
|
|
## License and Legal Information
|
|
|
|
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
|
|
**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.
|
|
|
|
### Trademarks
|
|
|
|
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
|
|
|
|
### Company Information
|
|
|
|
Task Venture Capital GmbH
|
|
Registered at District court Bremen HRB 35230 HB, Germany
|
|
|
|
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
|
|
|
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works. |