feat(server): add PROXY protocol v2 support for real client IP handling and connection ACLs

This commit is contained in:
2026-03-29 17:40:55 +00:00
parent e31086d0c2
commit 229db4be38
9 changed files with 592 additions and 404 deletions

View File

@@ -5,6 +5,7 @@ A high-performance VPN solution with a **TypeScript control plane** and a **Rust
🔐 **Noise IK** mutual authentication — per-client X25519 keypairs, server-side registry
🚀 **Triple transport**: WebSocket (Cloudflare-friendly), raw **QUIC** (datagrams), and **WireGuard** (standard protocol)
🛡️ **ACL engine** — deny-overrides-allow IP filtering, aligned with SmartProxy conventions
🔀 **PROXY protocol v2** — real client IPs behind reverse proxies (HAProxy, SmartProxy, Cloudflare Spectrum)
📊 **Adaptive QoS**: per-client rate limiting, priority queues, connection quality tracking
🔄 **Hub API**: one `createClient()` call generates keys, assigns IP, returns both SmartVPN + WireGuard configs
📡 **Real-time telemetry**: RTT, jitter, loss ratio, link health — all via typed APIs
@@ -125,6 +126,32 @@ security: {
Supports exact IPs, CIDR, wildcards (`192.168.1.*`), and ranges (`1.1.1.1-1.1.1.100`).
### 🔀 PROXY Protocol v2
When the VPN server sits behind a reverse proxy, enable PROXY protocol v2 to receive the **real client IP** instead of the proxy's address. This makes `ipAllowList` / `ipBlockList` ACLs work correctly through load balancers.
```typescript
await server.start({
// ... other config ...
proxyProtocol: true, // parse PP v2 headers on WS connections
connectionIpBlockList: ['198.51.100.0/24'], // server-wide block list (pre-handshake)
});
```
**Two-phase ACL with real IPs:**
| Phase | When | What Happens |
|-------|------|-------------|
| **Pre-handshake** | After TCP accept | Server-level `connectionIpBlockList` rejects known-bad IPs — zero crypto cost |
| **Post-handshake** | After Noise IK identifies client | Per-client `ipAllowList` / `ipBlockList` checked against real source IP |
- Parses the PP v2 binary header from raw TCP before WebSocket upgrade
- 5-second timeout protects against stalling attacks
- LOCAL command (proxy health checks) handled gracefully
- IPv4 and IPv6 addresses supported
- `remoteAddr` field on `IVpnClientInfo` exposes the real client IP for monitoring
- **Security**: must be `false` (default) when accepting direct connections — only enable behind a trusted proxy
### 📊 Telemetry & QoS
- **Connection quality**: Smoothed RTT, jitter, min/max RTT, loss ratio, link health (`healthy` / `degraded` / `critical`)
@@ -217,13 +244,13 @@ const unit = VpnInstaller.generateServiceUnit({
| Interface | Purpose |
|-----------|---------|
| `IVpnServerConfig` | Server configuration (listen addr, keys, subnet, transport mode, clients) |
| `IVpnServerConfig` | Server configuration (listen addr, keys, subnet, transport mode, clients, proxy protocol) |
| `IVpnClientConfig` | Client configuration (server URL, keys, transport, WG options) |
| `IClientEntry` | Server-side client definition (ID, keys, security, priority, tags, expiry) |
| `IClientSecurity` | Per-client ACLs and rate limits (SmartProxy-aligned naming) |
| `IClientRateLimit` | Rate limiting config (bytesPerSec, burstBytes) |
| `IClientConfigBundle` | Full config bundle returned by `createClient()` |
| `IVpnClientInfo` | Connected client info (IP, stats, authenticated key) |
| `IVpnClientInfo` | Connected client info (IP, stats, authenticated key, remote addr) |
| `IVpnConnectionQuality` | RTT, jitter, loss ratio, link health |
| `IVpnKeypair` | Base64-encoded public/private key pair |
@@ -314,7 +341,7 @@ pnpm install
# Build (TypeScript + Rust cross-compile)
pnpm build
# Run all tests (79 TS + 121 Rust = 200 tests)
# Run all tests (79 TS + 129 Rust = 208 tests)
pnpm test
# Run Rust tests directly
@@ -345,6 +372,7 @@ smartvpn/
│ ├── crypto.rs # Noise IK + XChaCha20
│ ├── client_registry.rs # Client database
│ ├── acl.rs # ACL engine
│ ├── proxy_protocol.rs # PROXY protocol v2 parser
│ ├── management.rs # JSON-lines IPC
│ ├── transport.rs # WebSocket transport
│ ├── quic_transport.rs # QUIC transport