- Go 66.1%
- HTML 33.2%
- Dockerfile 0.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .github/workflows | ||
| client | ||
| web | ||
| wire | ||
| zoraxy_plugin | ||
| .gitignore | ||
| .introspect | ||
| .releaseurl | ||
| cert.go | ||
| CHANGELOG.md | ||
| config.go | ||
| control.go | ||
| go.mod | ||
| go.sum | ||
| handler.go | ||
| icon.png | ||
| ingress.go | ||
| main.go | ||
| README.md | ||
| tunnel.go | ||
| zoraxy_api.go | ||
zoraxy-tunnel
A self-hosted, Cloudflare-Tunnel-style reverse tunnel for Zoraxy. Run the plugin on your Zoraxy box, expose one TLS port, and reach services behind NAT/firewalls from anywhere — without an external account.
Browser ──▶ Zoraxy (TLS) ──▶ plugin ingress :9080
│ (yamux stream over TLS)
▼
tunnel-client ──▶ http://127.0.0.1:3000 (your service)
How it works
The plugin runs three listeners:
| Port | Kind | Purpose |
|---|---|---|
| dynamic | HTTP | Dashboard UI (/ui) + JSON API (/ui/api/*), proxied by Zoraxy |
9080 (static) |
HTTP | Ingress — public traffic Zoraxy routes here, dispatched by Host |
9443 (static) |
TLS | Control plane — tunnel clients dial this |
On first start the plugin mints a self-signed cert valid 99 years and shows its SHA256 fingerprint in the dashboard. Clients pin that fingerprint during the TLS handshake — if the cert doesn't match, the connection is killed before any data is exchanged. Authorization is a per-tunnel token (stored only as a hash).
Trust model
- Plugin generates cert → fingerprint shown in UI.
- Client connects to
:9443, computes SHA256 of the presented cert, compares to--fingerprint. Mismatch → connection dropped. - Client authenticates with
--token; plugin maps it to a tunnel by hash. - One live client per tunnel (a reconnecting client replaces the previous one).
Setup
1. Build
git clone https://github.com/sniffingsugar/zoraxy-tunnel
cd zoraxy-tunnel
go build -o zoraxy-tunnel . # the plugin
go build -o tunnel-client ./client # the client
Pre-built binaries for both are published under releases.
2. Install the plugin
Drop the binary into Zoraxy's plugin folder (folder name must equal binary name):
plugins/zoraxy-tunnel/zoraxy-tunnel
Restart Zoraxy. Open the plugin UI.
3. Configure the node
In the dashboard:
- Set Server address to where clients reach your control port, e.g.
tunnel.example.com:9443(port-forward/expose9443to the internet). - Note the fingerprint.
4. Create a tunnel + connect a client
Click Create tunnel, name it, and copy the command the modal shows. There are three ready-to-paste variants:
CLI
tunnel-client \
--server tunnel.example.com:9443 \
--token zt_… \
--fingerprint "AB:CD:EF:…"
Docker
docker run -d --name tunnel-client --restart unless-stopped \
--network host \
ghcr.io/sniffingsugar/tunnel-client:latest \
--server tunnel.example.com:9443 \
--token zt_… \
--fingerprint "AB:CD:EF:…"
docker-compose.yml
services:
tunnel-client:
image: ghcr.io/sniffingsugar/tunnel-client:latest
container_name: tunnel-client
restart: unless-stopped
network_mode: host # Linux: 127.0.0.1 targets work as-is
command:
- --server=tunnel.example.com:9443
- --token=zt_…
- --fingerprint=AB:CD:EF:…
The token is shown once. Only a hash is stored afterwards — regenerate it from the tunnel's menu if you lose it.
With Docker, the client reaches services on the host. On Linux
network_mode: hostmakes127.0.0.1:3000work directly. On macOS/Windows Docker Desktop, dropnetwork_mode: hostand targethost.docker.internalinstead in each service's target.
5. Register a service + install the route
Inside a tunnel, Register service:
- Public host — e.g.
app.example.com(the domain the world visits) - Path prefix — optional, e.g.
/api - Local target — what the client dials, e.g.
http://127.0.0.1:3000
Then click Install route. The plugin creates a Zoraxy proxy rule
app.example.com → 127.0.0.1:9080 for you. Deleting the service or the tunnel
removes that route automatically.
Public HTTP(S) and WebSockets are both supported and streamed.
Requirements
- Zoraxy 3.2.0+ (plugin system)
- One publicly reachable TCP port for
:9443(port-forward / expose) - Go 1.23+ to build from source
License
MIT