A simplified WireGuard-based reverse proxy system similar to FRP (Fast Reverse Proxy) that allows exposing local services through a WireGuard tunnel without complex port configurations on the server side.
The new architecture eliminates the need for manual port configuration on the server side:
- RPS (Server): Only needs WireGuard configuration, hosts a REST API within the WireGuard netstack on port 80
- RPC (Client): Connects to RPS and dynamically registers port mappings via REST API
- Dynamic Port Allocation: Client uses random internal ports, server opens external ports on demand
- Heartbeat Mechanism: Client sends periodic heartbeats to maintain connection, server automatically cleans up stale mappings
- Automatic Cleanup: When client disconnects, all associated port mappings are automatically removed
pkg/config/: WireGuard configuration parsingpkg/wireguard/: WireGuard device managementpkg/server/: Server-side proxy and API handlingpkg/client/: Client-side proxy and API communicationpkg/api/: Shared API types and structurespkg/bufferpool/: Efficient buffer pool for I/O operationspkg/utils/: Utility functions
rps: Server binary (WireGuard Reverse Proxy Server)rpc: Client binary (WireGuard Reverse Proxy Client)
# Start server with default config
./bin/rps
# Start server with custom config and verbose logging
./bin/rps -c wg-server.conf -v
# Start server with custom buffer size (128KB for high-traffic scenarios)
./bin/rps -c wg-server.conf -b 128
# Show version
./bin/rps -VThe server:
- Reads WireGuard configuration
- Creates WireGuard netstack device
- Starts REST API on port 80 within the WireGuard netstack
- Starts heartbeat-based health checker
- Waits for client connections and port mapping requests
- Automatically cleans up mappings for disconnected clients
# Expose local service localhost:8080 to server port 8080
./bin/rpc -r localhost:8080-8080
# Multiple port mappings
./bin/rpc -r localhost:8080-8080 -r localhost:3000-3000
# With custom config, verbose logging, and optimized buffer size
./bin/rpc -c wg-client.conf -v -b 128 -r localhost:8080-8080
# For high-throughput applications (file transfers, video streaming)
./bin/rpc -c wg-client.conf -b 256 -r localhost:8080-8080
# Show version
./bin/rpc -VThe client:
- Reads WireGuard configuration
- Creates WireGuard netstack device
- Checks server availability before proceeding
- Parses route mappings (format:
local_ip:local_port-remote_port) - Starts internal listeners on random ports
- Registers port mappings with server via REST API
- Starts heartbeat mechanism to maintain connection
- Forwards traffic from internal listeners to local services
- Automatically cleans up mappings on graceful shutdown
[Interface]
PrivateKey = <server_private_key>
Address = 10.0.0.1/24, fd00::1/64
ListenPort = 51820
MTU = 65280
[Peer]
PublicKey = <client_public_key>
AllowedIPs = 10.0.0.2/32, fd00::2/128[Interface]
PrivateKey = <client_private_key>
Address = 10.0.0.2/32, fd00::2/128
MTU = 65280
[Peer]
PublicKey = <server_public_key>
Endpoint = server.example.com:51820
AllowedIPs = 10.0.0.0/24, fd00::/64
PersistentKeepalive = 25The server exposes a REST API within the WireGuard netstack:
-
POST
/api/v1/port-mappings- Create a new port mapping
- Body:
{"local_addr": "127.0.0.1:8080", "remote_port": 8080, "client_ip": "10.0.0.2", "client_port": 12345}
-
DELETE
/api/v1/port-mappings?port=8080- Remove a port mapping
- POST
/api/v1/heartbeat- Send client heartbeat to maintain connection
- Body:
{"client_ip": "10.0.0.2"} - Server automatically removes mappings for clients that stop sending heartbeats (after 60 seconds)
External Client -> Server:8080 -> WireGuard Tunnel -> Client:random_port -> localhost:8080
^
Heartbeat every 20s
- External client connects to server on port 8080
- Server forwards to client's random internal port through WireGuard tunnel
- Client forwards to local service (localhost:8080)
- Client sends heartbeats every 20 seconds to maintain connection
- Server checks client health every 30 seconds and removes mappings if client stops sending heartbeats for 60+ seconds
- Simplified Configuration: Server doesn't need port mapping flags
- Dynamic Port Management: Ports are opened/closed on demand
- Automatic Cleanup: Disconnected clients have their mappings automatically removed
- Heartbeat Monitoring: Real-time detection of client disconnections
- Server Availability Check: Client validates server connectivity before setup
- Security: Only WireGuard port is exposed externally
- Scalability: Easy to add/remove services without server restart
- Clean Architecture: Separated concerns with dedicated packages
- Optimized I/O: Buffer pool implementation for efficient connection copying
- Configurable Performance: Adjustable buffer sizes for different use cases
- Memory Efficient: Reusable buffer pools reduce garbage collection pressure
- Graceful Shutdown: Proper cleanup on client termination
- Start the server:
# Default configuration
./bin/rps -c wg-server.conf
# For high-traffic scenarios, use larger buffer size
./bin/rps -c wg-server.conf -b 256- Start the client to expose a local web server:
# Default configuration
./bin/rpc -c wg-client.conf -r localhost:8080-8080
# With matching buffer size for optimal performance
./bin/rpc -c wg-client.conf -b 256 -r localhost:8080-8080- Access the service externally:
curl http://server-ip:8080The request will be tunneled through WireGuard to the client's localhost:8080 service.
Both the server and client support configurable buffer sizes for I/O operations using the -b flag:
# Default buffer size (32KB) - good for most applications
./bin/rps -c wg-server.conf
./bin/rpc -c wg-client.conf -r localhost:8080-8080
# Small buffer (32KB) - saves memory for low-traffic services
./bin/rps -c wg-server.conf -b 32
./bin/rpc -c wg-client.conf -b 32 -r localhost:8080-8080
# Large buffer (256KB) - better performance for high-throughput applications
./bin/rps -c wg-server.conf -b 256
./bin/rpc -c wg-client.conf -b 256 -r localhost:8080-8080| Use Case | Recommended Buffer Size | Reasoning |
|---|---|---|
| Web services (HTTP/HTTPS) | 32KB (default) | Balanced performance and memory usage |
| File transfers (FTP, SCP) | 256KB - 512KB | Large sequential reads/writes benefit from bigger buffers |
| Game servers | 32KB - 64KB | Small, frequent packets don't need large buffers |
| Database connections | 64KB - 128KB | Mixed workload with moderate data sizes |
| Video streaming | 256KB - 1MB | High bandwidth with large continuous data |
| IoT/sensor data | 16KB - 32KB | Small, infrequent data transmissions |
- Smaller buffers (16-64KB): Use less memory, suitable for memory-constrained environments or many concurrent connections
- Larger buffers (128KB+): Better throughput for high-bandwidth applications, but use more memory per connection
The system uses an efficient buffer pool that:
- Reuses buffers: Reduces garbage collection pressure
- Thread-safe: Safe for concurrent use across multiple connections
- Automatic cleanup: Buffers are automatically returned to the pool after use
This project is licensed under the MIT License.