Standalone CLI client for creating secure tunnels through ForTunnels. Expose local HTTP/HTTPS, TCP, and UDP services to the internet in seconds.
ForTunnels Client is a command-line tool that connects to the ForTunnels server at https://fortunnels.ru and creates a public URL for your local service.
- HTTP/HTTPS tunnels - forward web services with automatic routing
- TCP tunnels - forward any TCP protocol (SSH, databases, etc.)
- UDP tunnels - support for UDP protocols (DNS, syslog, etc.)
- Multiple transports - WebSocket/smux, QUIC, DTLS
- Encryption - optional client-side stream encryption (PSK)
- Monitoring - tunnel status via WebSocket
- Cross-platform - Linux, macOS, Windows
- Go 1.22 or newer (for building from source)
make(optional, for convenience targets)
git clone https://github.com/fortunnels/client.git
cd client
go build -o ./bin/client ./cmd/client
# On Windows: go build -o ./bin/client.msi ./cmd/clientOr via make:
make build # binary in ./bin/client (includes checks)
make build-fast # without tests/coverageOn Windows (or when cross-compiling with GOOS=windows), the binary is produced as bin/client.msi instead of bin/client.
The BIN_DIR environment variable controls the output directory:
make build BIN_DIR=../binBinaries for common platforms are available in releases.
Create a tunnel to a local web server on port 8000:
./bin/client 8000After the tunnel is created you will receive a public URL like https://fortunnels.ru/t/{tunnel-id}/.
Host-based routing is also available, for example: https://{subdomain}.fortunnels.ru
(where {subdomain} is a random identifier, not the tunnel ID).
./bin/client -local 127.0.0.1:8443 -protocol httpsForward SSH through a tunnel:
./bin/client -protocol tcp -local 127.0.0.1:22 -dst localhost:3333 -listen :4000Now you can connect to SSH through the tunnel:
ssh -p 4000 user@localhost./bin/client -protocol udp -udp-listen :5353 -udp-dst 127.0.0.1:53-allow-insecure-http- allow insecure HTTP for non-local addresses (not recommended)-local- local service address to forward (e.g.127.0.0.1:8000)-protocol http|https|tcp|udp- tunnel protocol-user- user identifier (for audit/quotas, default:default)-dp ws|quic|dtls- data-plane transport (default:ws)
Default: all tunnels run in blocking mode and stay active until Ctrl+C.
-dst host:port- server-side target address for TCP forwarding/testing-parallel N- number of parallel smux streams for TCP tests/load-listen :PORT- local TCP listen address; incoming connections are proxied via data-plane to-dst-backoff-initial- initial reconnect delay in listen mode (sec, default: 1)-backoff-max- max reconnect delay in listen mode (sec, default: 30)
-udp-listen :PORT- local UDP listen address (e.g.:5353)-udp-dst host:port- server-side UDP destination (e.g.127.0.0.1:53)
-ping-interval- WebSocket ping interval (default:30s)-ping-timeout- ping write timeout (default:10s)-smux-keepalive-interval- smux keepalive interval (default:25s)-smux-keepalive-timeout- smux keepalive timeout (default:60s)-watch- tunnel monitoring mode (subscription/polling)-watch-interval- HTTP poll interval after WS subscription (default:10s)
-encrypt- enable client-side stream encryption (PSK) over data-plane-psk- pre-shared key (required with-encrypt)-psk-file- read PSK from a file-psk-stdin- read PSK from stdin
Note: When using -encrypt, you must provide a non-empty -psk.
-login- login for server authentication-pass- password for server authentication-pass-file- read password from a file-pass-stdin- read password from stdin-token- Bearer JWT token for API authorization-token-file- read token from a file-token-stdin- read token from stdin-dp-auth-token- ready data-plane auth token (hex)-dp-auth-token-file- read data-plane token from a file-dp-auth-token-stdin- read data-plane token from stdin-dp-auth-secret- secret for computing token (HMAC-SHA256 overtunnel_id)-dp-auth-secret-file- read data-plane secret from a file-dp-auth-secret-stdin- read data-plane secret from stdin
Short forms are supported:
client 8000- HTTP tunnel to127.0.0.1:8000client http 8000- explicit protocolclient tcp 22- TCP tunnel to127.0.0.1:22
- All connections to the server use HTTPS (TLS 1.2+)
- Server certificate validation is enabled by default
- Auto-configuration is used for local development (
localhost/127.0.0.1) - HTTP for non-local addresses is blocked without
-allow-insecure-http
The client supports optional encryption over the data-plane:
- XChaCha20-Poly1305 (AEAD)
- Key derived from PSK and tunnel ID:
SHA256(PSK || tunnel_id) - Enable:
-encrypt -psk "your-secret-key"
Recommendations:
- Use long random keys (at least 32 bytes)
- Do not reuse a PSK across tunnels
- Store PSK in environment variables, files, or secret managers
- Authentication via login/password or JWT token
- Tokens are passed via
Authorization: Bearer <token> - Data-plane supports additional HMAC-based authentication
- Prefer
*-fileor*-stdinfor sensitive data
Test cloud webhooks against a local server:
./bin/client -local 127.0.0.1:3000 -protocol httpUse the public URL to configure the webhook.
Show prototypes to customers:
./bin/client -local 127.0.0.1:8080 -protocol http./bin/client -protocol tcp -local 127.0.0.1:22 -dst localhost:3333 -listen :4000./bin/client -protocol udp -udp-listen :5353 -udp-dst 127.0.0.1:53./bin/client 8000 -watch -watch-interval 5s./bin/client 8000 -encrypt -psk "$(openssl rand -hex 32)"./bin/client -protocol tcp -dp quic -dst localhost:3333./bin/client -protocol udp -dp dtls -udp-listen :5353 -udp-dst 127.0.0.1:53Problem: Unable to connect to server
Fixes:
- Check that the ForTunnels server is reachable:
curl https://fortunnels.ru/api/health - Check network settings and firewall
Problem: Failed to create tunnel
Fixes:
- Check authentication: use
-login/-passor-token - Ensure the local service is reachable:
curl http://127.0.0.1:8000 - Check address format: must be
host:port(e.g.127.0.0.1:8000)
Problem: empty PSK
Fix: When using -encrypt, provide a non-empty -psk:
./bin/client 8000 -encrypt -psk "your-secret-key-here"Problem: connections drop
Fixes:
- Increase timeouts:
-ping-timeout 30s,-smux-keepalive-timeout 120s - Check internet stability
- Use
-watchto monitor tunnel state
Problem: UDP packets do not pass
Fixes:
- Ensure both flags are set:
-udp-listenand-udp-dst - Check that UDP is enabled on the server
- Try a different transport:
-dp quicor-dp dtls
client/
|-- cmd/client/ # CLI entrypoint
|-- internal/
| |-- auth/ # Authentication
| |-- config/ # Configuration and validation
| |-- control/ # Control-plane operations
| |-- dataplane/ # Data-plane transports (WS, QUIC, DTLS)
| |-- security/ # Encryption (PSK)
| `-- support/ # Utilities and error handling
|-- shared/
| `-- wsconn/ # WebSocket adapter for smux
|-- Makefile # Build/test/release targets
|-- go.mod # Go module
`-- .golangci.yml # Linter settings
Project uses custom linter configuration (.golangci.yml) tuned for CLI apps:
make check- full checks (gofmt, go vet, tests, golangci-lint, govulncheck, staticcheck, ineffassign, misspell, gocyclo)- All checks run automatically on
make build
Strictness highlights:
- Cyclomatic complexity
gocyclolimited to 12 funlenmax 80 lines and 60 statements per functionlllmax line length 200- Duplicate finder (
dupl) triggers at 50 tokens - Any
//nolintmust include an explanation and list of linters
- Linux (amd64, arm64)
- macOS (amd64, arm64)
- Windows (amd64, arm64, x86)
Proprietary License - see LICENSE for details. This software is proprietary and may not be reused in other projects.
- ForTunnels - official site