Skip to content

Commit f220262

Browse files
committed
feat: implement clean routing architecture and enhanced UX
- Move website to /ui with smart root redirect for tunnel routing - Clean separation: /ui for website, / for tunnel subdomains - Simplified script with focus on proper cleanup - Enhanced docker-compose.yml with volume mounts and production config - Removed clutter from website UI (keyboard shortcuts, path notes) - Fixed tunnel proxy routing issues by following gorilla/mux best practices This resolves tunnel subdomain conflicts and provides a maintainable architecture.
1 parent 7ae3607 commit f220262

File tree

11 files changed

+364
-187
lines changed

11 files changed

+364
-187
lines changed

README.md

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,22 @@ Secure HTTP tunnels to localhost using WireGuard. Share your local development s
44

55
## Quick Start
66

7+
**Single command (recommended):**
78
```bash
8-
# Start your local server
9-
python3 -m http.server 3000
9+
# Replace 3000 with your local port
10+
curl -s https://arbok.mrkaran.dev/start/3000 | sudo bash
1011

11-
# Get tunnel config
12-
curl https://tunnel.mrkaran.dev/3000 > tunnel.conf
13-
14-
# Start tunnel
15-
sudo wg-quick up ./tunnel.conf
16-
17-
# Your app is now live at https://random-name-1234.tunnel.mrkaran.dev
12+
# Your app is now live at https://random-name-1234.arbok.mrkaran.dev
13+
# Press Ctrl+C to stop the tunnel
1814
```
1915

20-
Stop the tunnel:
16+
**Manual setup (advanced):**
2117
```bash
18+
# Get tunnel config and start manually
19+
curl https://arbok.mrkaran.dev/3000 > tunnel.conf
20+
sudo wg-quick up ./tunnel.conf
21+
22+
# Stop the tunnel
2223
sudo wg-quick down ./tunnel.conf
2324
```
2425

@@ -34,7 +35,7 @@ make build
3435
2. **Configure** (copy `config.sample.toml` to `config.toml`):
3536
```toml
3637
[app]
37-
domain = "tunnel.yourdomain.com"
38+
domain = "arbok.yourdomain.com"
3839

3940
[auth]
4041
# Optional: Add API keys for authentication
@@ -77,7 +78,7 @@ curl -H "Host: your-subdomain.localhost" http://localhost:8080
7778
## Features
7879

7980
**Simple & Secure**
80-
- One command setup - just `curl` to get started
81+
- One command setup with automatic lifecycle management
8182
- WireGuard encryption with modern cryptography
8283
- No account needed - anonymous tunnels by default
8384
- Self-hosted - complete control over your infrastructure
@@ -90,9 +91,14 @@ curl -H "Host: your-subdomain.localhost" http://localhost:8080
9091

9192
## API Usage
9293

93-
### Simple provisioning
94+
### Enhanced provisioning (single command)
95+
```bash
96+
curl -s https://arbok.mrkaran.dev/start/3000 | sudo bash
97+
```
98+
99+
### Simple provisioning (manual config)
94100
```bash
95-
curl https://tunnel.yourdomain.com/3000 > tunnel.conf
101+
curl https://arbok.mrkaran.dev/3000 > tunnel.conf
96102
```
97103

98104
### RESTful API

docker-compose.prod-env.yml

Lines changed: 0 additions & 31 deletions
This file was deleted.

docker-compose.prod.yml

Lines changed: 0 additions & 51 deletions
This file was deleted.

docker-compose.test.yml

Lines changed: 0 additions & 15 deletions
This file was deleted.

docker-compose.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
services:
2+
arbok:
3+
image: ghcr.io/mr-karan/arbok:latest
4+
container_name: arbok
5+
environment:
6+
TZ: Asia/Kolkata
7+
volumes:
8+
- ./config.toml:/app/config.toml # Mount your local config.toml file
9+
ports:
10+
- "8080:8080"
11+
- "54321:54321/udp" # WireGuard port
12+
cap_add:
13+
- NET_ADMIN
14+
- SYS_MODULE
15+
sysctls:
16+
- net.ipv4.ip_forward=1
17+
- net.ipv6.conf.all.forwarding=1
18+
devices:
19+
- /dev/net/tun:/dev/net/tun
20+
healthcheck:
21+
test: ["CMD-SHELL", "wget -q --tries=1 http://localhost:8080/health -O - || exit 1"]
22+
interval: 60s
23+
timeout: 10s
24+
retries: 3
25+
start_period: 40s
26+
restart: unless-stopped
27+
command: ["./arbok", "--config", "/app/config.toml"]
28+
networks:
29+
- arbok
30+
31+
networks:
32+
arbok:
33+
name: arbok

internal/api/handlers.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,39 @@ func (s *Server) handleProvisionSimple(w http.ResponseWriter, r *http.Request) {
225225
fmt.Fprint(w, instructions)
226226
}
227227

228+
// handleStartTunnel handles enhanced tunnel provisioning with self-executing script
229+
func (s *Server) handleStartTunnel(w http.ResponseWriter, r *http.Request) {
230+
vars := mux.Vars(r)
231+
port, err := strconv.ParseUint(vars["port"], 10, 16)
232+
if err != nil || port == 0 || port > 65535 {
233+
http.Error(w, "Invalid port number", http.StatusBadRequest)
234+
return
235+
}
236+
237+
// Create tunnel
238+
t, err := s.registry.CreateTunnel(uint16(port))
239+
if err != nil {
240+
s.logger.Error("failed to create tunnel", "error", err, "port", port)
241+
http.Error(w, "Failed to create tunnel", http.StatusInternalServerError)
242+
return
243+
}
244+
245+
// Add peer to WireGuard
246+
if err := s.tun.AddPeer(t.PublicKey, t.AllowedIP); err != nil {
247+
s.logger.Error("failed to add peer", "error", err, "tunnel_id", t.ID)
248+
_ = s.registry.DeleteTunnel(t.ID)
249+
http.Error(w, "Failed to configure tunnel", http.StatusInternalServerError)
250+
return
251+
}
252+
253+
// Generate self-executing script
254+
script := s.generateTunnelScript(t)
255+
256+
w.Header().Set("Content-Type", "text/plain")
257+
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
258+
fmt.Fprint(w, script)
259+
}
260+
228261
// generateWireGuardConfig generates a WireGuard configuration
229262
func (s *Server) generateWireGuardConfig(t *tunnel.Info) string {
230263
serverEndpoint := fmt.Sprintf("%s:%d", s.cfg.Domain, s.cfg.WireGuardPort)
@@ -260,17 +293,22 @@ func (s *Server) handleTunnelProxy(w http.ResponseWriter, r *http.Request) {
260293

261294
parts := strings.Split(host, ".")
262295
if len(parts) < 2 {
296+
s.logger.Debug("tunnel proxy: invalid host", "host", host, "parts", len(parts))
263297
writeError(w, http.StatusBadRequest, "INVALID_HOST", "Invalid host header")
264298
return
265299
}
266300

267301
subdomain := parts[0]
302+
s.logger.Debug("tunnel proxy: looking for tunnel", "host", host, "subdomain", subdomain)
268303
t := s.registry.GetTunnelBySubdomain(subdomain)
269304
if t == nil {
305+
s.logger.Debug("tunnel proxy: tunnel not found", "subdomain", subdomain)
270306
writeError(w, http.StatusNotFound, "TUNNEL_NOT_FOUND", "Tunnel not found")
271307
return
272308
}
273309

310+
s.logger.Debug("tunnel proxy: found tunnel", "subdomain", subdomain, "tunnel_id", t.ID)
311+
274312
// Update traffic stats
275313
defer func() {
276314
// This is a simplified version - in production you'd track actual bytes

0 commit comments

Comments
 (0)