Skip to content

Repository files navigation

ForTunnels Client

Go Version License

Standalone CLI client for creating secure tunnels through ForTunnels. Expose local HTTP/HTTPS, TCP, and UDP services to the internet in seconds.

Overview

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.

Features

  • 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

Requirements

  • Go 1.25.7 or newer (toolchain go1.26.5; see go.mod)
  • make (optional, for convenience targets)

Installation

Build from source

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/client

The released CLI product name is fortunnels (see make release artifacts). A local make build writes ./bin/client (or ./bin/client.msi on Windows).

Or via make:

make build          # binary in ./bin/client (includes checks)
make build-fast     # without tests/coverage
make format         # rewrite sources (gofumpt + goimports)

On 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=../bin

Download binaries

Binaries for common platforms are available on the ForTunnels downloads page.

On Windows, you can install from the Chocolatey Community Repository when the package is available:

choco install fortunnels

On macOS and Linux, a Homebrew tap is published separately; see the main ForTunnels site.

Quick start

HTTP tunnel

Create a tunnel to a local web server on port 8000:

./bin/client 8000

After 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).

HTTPS tunnel

./bin/client -local 127.0.0.1:8443 -protocol https

TCP tunnel (expose-local, default)

Expose a local TCP service (e.g. PostgreSQL, SSH) to a public endpoint:

./bin/client tcp 5433

Or with explicit flags:

./bin/client -protocol tcp -local 127.0.0.1:5433

The server allocates a public TCP port. Connect to the displayed tcp://host:port URL from anywhere.

UDP tunnel (expose-local)

Expose a local UDP service (e.g. DNS, game server):

./bin/client udp 9000

Advanced UDP (reverse mode)

./bin/client -protocol udp -udp-listen :5353 -udp-dst 127.0.0.1:53

CLI options reference

General options

  • -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)

Execution mode

Default: all tunnels run in blocking mode and stay active until Ctrl+C.

TCP mode

  • Default (expose-local): Server accepts external TCP, forwards to your local backend.
  • -backoff-initial - reconnect backoff (sec, default: 1)
  • -backoff-max - max reconnect backoff (sec, default: 30)

UDP mode

Default (expose-local): fortunnels udp <port> forwards public UDP to your local service.

Advanced reverse mode (both flags required):

  • -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)

Reliability and monitoring

  • -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)

Encryption

  • -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.

Lifecycle and auth

  • When using -login/-pass (session auth), the fallback lifecycle poller uses the same authenticated client (cookie jar) for tunnel status checks. Bearer token auth is also supported.
  • -watch mode uses the same auth for both WebSocket subscription and HTTP fallback polling.

Authentication notes

  • -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 over tunnel_id)
  • -dp-auth-secret-file - read data-plane secret from a file
  • -dp-auth-secret-stdin - read data-plane secret from stdin

Positional arguments

Short forms are supported:

  • client 8000 - HTTP tunnel to 127.0.0.1:8000
  • client http 8000 - explicit protocol
  • client tcp 22 - TCP tunnel to 127.0.0.1:22

Security

TLS/HTTPS

  • 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

Stream encryption

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

  • Authentication via login/password or JWT token
  • Tokens are passed via Authorization: Bearer <token>
  • Data-plane supports additional HMAC-based authentication
  • Prefer *-file or *-stdin for sensitive data

Usage examples

Webhooks

Test cloud webhooks against a local server:

./bin/client -local 127.0.0.1:3000 -protocol http

Use the public URL to configure the webhook.

SSH access

./bin/client tcp 22

UDP (DNS)

./bin/client udp 53

Advanced reverse mode:

./bin/client -protocol udp -udp-listen :5353 -udp-dst 127.0.0.1:53

With monitoring

./bin/client 8000 -watch -watch-interval 5s

With encryption

./bin/client 8000 -encrypt -psk "$(openssl rand -hex 32)"

QUIC transport

./bin/client tcp 22 -dp quic

DTLS transport (UDP)

./bin/client -protocol udp -dp dtls -udp-listen :5353 -udp-dst 127.0.0.1:53

Troubleshooting

Unable to connect to the server

Problem: Unable to connect to server

Fixes:

  • Check that the ForTunnels server is reachable: curl https://fortunnels.ru/api/health
  • Check network settings and firewall

Tunnel is not created

Problem: Failed to create tunnel

Fixes:

  • Check authentication: use -login/-pass or -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)

Empty PSK during encryption

Problem: empty PSK

Fix: When using -encrypt, provide a non-empty -psk:

./bin/client 8000 -encrypt -psk "your-secret-key-here"

Connection timeouts

Problem: connections drop

Fixes:

  • Increase timeouts: -ping-timeout 30s, -smux-keepalive-timeout 120s
  • Check internet stability
  • Use -watch to monitor tunnel state

UDP does not work

Problem: UDP packets do not pass

Fixes:

  • Ensure both flags are set: -udp-listen and -udp-dst
  • Check that UDP is enabled on the server
  • Try a different transport: -dp quic or -dp dtls

Project structure

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

Code checks

Project uses custom linter configuration (.golangci.yml) tuned for CLI apps:

  • make format - rewrite sources (gofumpt -w, goimports -w)
  • make format-check - read-only format gate (fails if sources need formatting)
  • make check - full checks (format-check, go vet, tests, golangci-lint, govulncheck, staticcheck, ineffassign, misspell, gocyclo)
  • All checks run automatically on make build (checks do not rewrite sources)

Strictness highlights:

  • Cyclomatic complexity gocyclo limited to 15 (gocyclo -over 15)
  • funlen max 80 lines and 60 statements per function
  • lll max line length 200
  • Duplicate finder (dupl) triggers at 50 tokens
  • Any //nolint must include an explanation and list of linters

Supported platforms

  • Linux (amd64, arm64)
  • macOS (amd64, arm64)
  • Windows (amd64, arm64, x86)

License

Proprietary License - see LICENSE for details. This software is proprietary and may not be reused in other projects.

Links

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages