@remotelinker/reverse-ws-tunnel
v1.0.4
Published
**A Node.js library for creating secure reverse tunnels over WebSocket connections.**
Readme
🚀 Reverse WebSocket Tunnel
A Node.js library for creating secure reverse tunnels over WebSocket connections.
📖 What is Reverse WebSocket Tunnel?
Reverse WebSocket Tunnel is a library that enables you to expose local services to the internet through a WebSocket tunnel, similar to tools like ngrok or localtunnel. It consists of two main components:
- Server: Runs on a publicly accessible server and accepts WebSocket connections from clients
- Client: Runs locally and creates a tunnel to expose local services through the server
Use Cases
- Development: Expose local development servers for testing webhooks, APIs, or sharing work
- IoT & Edge: Connect devices behind NAT/firewalls to cloud services
- Microservices: Enable secure communication between services across different networks
- CI/CD: Create temporary endpoints for automated testing
How It Works
- The server listens for WebSocket connections and HTTP requests
- The client connects to the server via WebSocket and registers a tunnel ID
- When the server receives HTTP requests for a specific tunnel, it forwards them through the WebSocket to the client
- The client proxies the requests to the local target service and sends responses back through the tunnel
📦 Installation
npm install @remotelinker/reverse-ws-tunnel🚀 Quick Start
Server Setup
const { startWebSocketServer } = require('@remotelinker/reverse-ws-tunnel/server');
// Start the WebSocket tunnel server
startWebSocketServer({
port: 443,
host: '0.0.0.0',
path: '/tunnel',
tunnelIdHeaderName: 'x-tunnel-id',
});Client Setup
const { startClient } = require('@remotelinker/reverse-ws-tunnel/client');
// Connect to the tunnel server and expose local service
const client = startClient({
tunnelId: '1cf2755f-c151-4281-b3f0-55c399035f87',
wsUrl: 'wss://yourdomain.com/tunnel',
targetUrl: 'http://localhost:3000',
tunnelEntryPort: 4443,
allowInsicureCerts: false,
});
// Listen for events
client.on('connected', () => {
console.log('Connected to tunnel');
});
client.on('disconnected', () => {
console.log('Disconnected from tunnel');
});
// Close connection
// client.close();⚙️ Configuration
You can configure the library using:
- Environment variables
- TOML configuration files (
config.toml) - Direct JavaScript parameters
Configuration priority: JavaScript parameters > config.toml > environment variables
🖥️ Server Configuration
JavaScript API
const { startWebSocketServer } = require('@remotelinker/reverse-ws-tunnel/server');
startWebSocketServer({
port: 443, // WebSocket server port
host: '0.0.0.0', // Host to bind (optional)
path: '/tunnel', // WebSocket path (optional)
tunnelIdHeaderName: 'x-tunnel-id', // Header name for tunnel identification
});Environment Variables
| Variable | Description | Default | Example |
| ----------------------- | ------------------------- | ------------- | ------------- |
| WS_PORT | WebSocket server port | 443 | 8080 |
| HOST | Host address to bind | undefined | 0.0.0.0 |
| PATH_URL | WebSocket endpoint path | undefined | /tunnel |
| TUNNEL_ID_HEADER_NAME | HTTP header for tunnel ID | x-tunnel-id | x-tunnel-id |
| LOG_LEVEL | Logging verbosity | info | debug |
TOML Configuration (config.toml)
# WebSocket server configuration
wsPort = 443
host = "0.0.0.0"
path = "/tunnel"
tunnelIdHeaderName = "x-tunnel-id"Example Server
# Run the example server
npm run example:server💻 Client Configuration
JavaScript API
const { startClient } = require('@remotelinker/reverse-ws-tunnel/client');
const client = startClient({
tunnelId: '1cf2755f-c151-4281-b3f0-55c399035f87', // Unique tunnel identifier (UUID)
wsUrl: 'wss://example.com/tunnel', // WebSocket server URL
targetUrl: 'http://localhost:3000', // Local service to expose
tunnelEntryUrl: 'http://localhost:4443', // Optional: tunnel entry URL
tunnelEntryPort: 4443, // TCP port for tunnel entry
allowInsicureCerts: false, // Allow insecure SSL certificates
headers: {
// Optional: custom headers
Authorization: 'Bearer token',
'X-Custom-Header': 'value',
},
autoReconnect: true, // Automatically reconnect on close (default: true)
});
// Event handling
client.on('connected', () => console.log('Tunnel connected'));
client.on('disconnected', () => console.log('Tunnel disconnected'));
// Close the tunnel
// client.close();Environment Variables
| Variable | Description | Required | Default | Example |
| ---------------------- | ---------------------------------- | -------- | ------- | -------------------------------------- |
| TUNNEL_ID | Unique tunnel identifier (UUID-v4) | ✅ | - | 1cf2755f-c151-4281-b3f0-55c399035f87 |
| WS_URL | WebSocket server URL | ✅ | - | wss://example.com/tunnel |
| TARGET_URL | Local service URL to expose | ✅ | - | http://localhost:3000 |
| TUNNEL_ENTRY_URL | Tunnel entry point URL | ❌ | - | http://localhost:4443 |
| TUNNEL_ENTRY_PORT | TCP port for tunnel entry | ❌ | - | 4443 |
| HEADERS | Custom headers (JSON string) | ❌ | - | {"Authorization":"Bearer token"} |
| ALLOW_INSICURE_CERTS | Allow insecure SSL certificates | ❌ | false | true |
| AUTO_RECONNECT | Automatically reconnect on close | ❌ | true | false |
| LOG_LEVEL | Logging level | ❌ | info | debug |
TOML Configuration (config.toml)
# Unique identifier of the tunnel (UUID-v4)
tunnelId = "1cf2755f-c151-4281-b3f0-55c399035f87"
# WebSocket server URL to connect to
wsUrl = "wss://example.com/tunnel"
# Target URL where the traffic will be forwarded
targetUrl = "http://localhost:3000"
# Optional URL for tunnel entry point
tunnelEntryUrl = "http://localhost:4443"
# TCP port to open for incoming tunnel connections
tunnelEntryPort = 4443
# Whether to allow insecure SSL certificates (dev/test only)
allowInsicureCerts = false
# Automatically reconnect on close
autoReconnect = true
# Log verbosity level: error, warn, info, debug, trace
logLevel = "info"
# Custom headers to send with requests
[headers]
Authorization = "Bearer your-token"
X-Custom-Header = "custom-value"Example Client
# Run the example client
npm run example:client🐳 Docker Deployment
Building the Docker Image
npm run docker:buildRunning with Docker Compose
npm run docker:deployThe service will start on port 4443 by default.
Docker Environment Variables
When using Docker, you can pass all the environment variables mentioned above:
docker run -e TUNNEL_ID=your-uuid -e WS_URL=wss://example.com -e TARGET_URL=http://localhost:3000 remotelinker/reverse-ws-tunnel📝 Logging
The library uses Winston for logging with configurable levels:
error: Only error messageswarn: Warnings and errorsinfo: General information (default)debug: Detailed debugging informationtrace: Very verbose output including message traces
Set the log level via:
- Environment variable:
LOG_LEVEL=debug - TOML config:
logLevel = "debug"
🔧 Advanced Usage
Custom Headers
You can send custom headers with tunnel requests:
// Environment variable (JSON string)
process.env.HEADERS = JSON.stringify({
'Authorization': 'Bearer your-token',
'X-API-Key': 'your-api-key'
});
// Or in config.toml
[headers]
Authorization = "Bearer your-token"
X-API-Key = "your-api-key"SSL/TLS Configuration
For development, you might need to allow insecure certificates:
startClient({
// ... other config
allowInsicureCerts: true, // Only for development!
});Multiple Tunnels
Each client needs a unique tunnelId (UUID-v4). You can run multiple clients with different tunnel IDs to expose multiple services.
🧪 Examples
The repository includes working examples:
- Server:
examples/server/- Shows how to set up a tunnel server. - Client:
examples/client/- Shows how to connect and expose a local service. - Web Server:
examples/webserver/- A minimal target web server.
Complete Reverse Tunnel Example
This example demonstrates how to set up a complete reverse tunnel to expose a local web server to the internet.
1. Start the Target Web Server
First, start the minimal web server that will be the destination of our tunnel. This server will respond with "Hello, World!".
# Terminal 1: Start the web server
node examples/webserver/webserver-example.js
# Server running on http://localhost:3000/2. Start the Tunnel Server
Next, start the tunnel server. This server runs on a publicly accessible machine and listens for WebSocket connections from the tunnel client.
The example server is located in examples/server/.
# Terminal 2: Start the tunnel server
npm run example:serverThis will start the server using the configuration from examples/server/config.toml. By default, it listens on port 3000 for WebSocket connections and port 4443 for public HTTP requests.
3. Start the Tunnel Client
Now, start the tunnel client. The client connects to the tunnel server and exposes the local web server.
We need to configure the client to connect to our tunnel server and point to our local web server. The example client configuration is in examples/client/config.toml. Let's modify it to match our setup.
examples/client/config.toml
# Unique identifier of the tunnel (UUID-v4)
tunnelId = "1cf2755f-c151-4281-b3f0-55c399035f87"
# WebSocket server URL to connect to
wsUrl = "ws://localhost:8080/tunnel"
# Target URL where the traffic will be forwarded
targetUrl = "http://localhost:8080"
# TCP port to open for incoming tunnel connections
tunnelEntryPort = 4443Now, run the client:
# Terminal 3: Start the tunnel client
npm run example:client4. Test the Tunnel
The tunnel is now active. The tunnel server is listening for requests on port 4443 and will forward them to your local web server running on port 8080.
You can test it by making a curl request to the tunnel server's public endpoint, including the x-tunnel-id header:
# Terminal 4: Test the tunnel
curl -X GET http://localhost:8083 -H "x-tunnel-id: 1cf2755f-c151-4281-b3f0-55c399035f87"You should see the "Hello, World!" response from your local web server.
Hello, World!⚠️ Security Considerations
- Production Use: Ready for production use
- SSL/TLS: Always use secure WebSocket connections (
wss://) in production - Authentication: Implement proper authentication mechanisms for your tunnels
- Rate Limiting: Consider implementing rate limiting on the server side
- Firewall: Ensure proper firewall rules are in place
📄 License
ISC License - see LICENSE file for details.
🤝 Contributing
This project is in active development. Contributions, issues, and feature requests are welcome!
📞 Support
For questions and support, please open an issue on the GitHub repository.
