Raytunnel 🚀
Raytunnel là một giải pháp reverse tunnel (HTTP và SSH forwarding) siêu nhẹ, bảo mật và được thiết kế tối giản nhằm thay thế cho Gradio/Ngrok. Thích hợp nhất cho việc tương tác giữa máy cá nhân và các remote instance (Kaggle, Colab, Cloud Server) kết nối trực tiếp về hệ thống Homelab.
🌟 Tính năng nổi bật
- Tối giản & Tự chủ: Hoạt động hoàn toàn trong user-space, không cần cấu hình tài khoản Unix hay SSH keys phức tạp trên Host OS của server.
- Tự động cấu hình SSH Daemon: Client tự phát hiện, cấu hình và khởi chạy SSH Daemon (
sshd) ở cổng 2222, tạo cặp khóa SSH động giúp bạn SSH trực tiếp vào remote instance (ví dụ: Kaggle) cực kỳ tiện lợi. - Dynamic HTTP Host Routing: Tự động phân tích trường
Hosttrong HTTP request để điều hướng tới đúng client đăng ký (ví dụ:my-worker.s.yourdomain.com). - Thân thiện với Notebook: Có sẵn API bất đồng bộ chạy ngầm (
background=True) để bạn gọi trực tiếp trong cell của Jupyter/Kaggle notebook mà không làm nghẽn luồng xử lý chính. - Bảo mật tuyệt đối:
- Xác thực kết nối điều khiển bằng Token bí mật.
- SSH daemon được cấu hình chỉ nhận khóa public key, cấm đăng nhập bằng mật khẩu (
PasswordAuthentication no).
📐 Kiến trúc hoạt động
sequenceDiagram
participant Client as Kaggle / Remote Worker
participant Server as Raytunnel Server (s.yourdomain.com)
participant Proxy as Reverse Proxy (Traefik / NPM)
participant User as Terminal / Trình duyệt
Client->>Server: Kết nối wss://s.yourdomain.com/ws/control + Auth Token
Server-->>Client: Trả về Subdomain: client-abc, SSH Port: 2200
Note over User,Client: Chuyển tiếp HTTP Web App
User->>Proxy: Truy cập https://client-abc.s.yourdomain.com
Proxy->>Server: Forward HTTP → cổng 8000 (TCP router)
Server->>Client: Yêu cầu mở kênh HTTP
Client->>Server: Kết nối wss://s.yourdomain.com/ws/data?channel_id=xxx
Client->>Client: Forward tới web app local (cổng 8000)
Server-->>Client: Chuyển tiếp dữ liệu hai chiều (TCP <-> WS)
Note over User,Client: Kết nối SSH Terminal
User->>Server: Chạy ssh -p 2200 user@s.yourdomain.com
Server->>Client: Yêu cầu mở kênh SSH
Client->>Server: Kết nối wss://s.yourdomain.com/ws/data?channel_id=yyy
Client->>Client: Forward tới SSH Daemon nội bộ (cổng 2222)
Server-->>Client: Chuyển tiếp luồng terminal hai chiều
Phân vai các cổng
| Cổng | Mục đích |
|---|---|
8001 |
FastAPI API + WebSocket control (/ws/control, /ws/data) + Dashboard |
8000 |
TCP raw proxy — đọc Host header và route HTTP traffic đến đúng client |
2200-2300 |
Dải cổng SSH động được server cấp phát cho từng tunnel |
[!IMPORTANT] Reverse proxy cần 2 route riêng biệt:
s.yourdomain.com→ cổng 8001 (control WebSocket + Dashboard),*.s.yourdomain.com→ cổng 8000 (HTTP tunnel traffic). Nếu route tất cả về 8000, client sẽ gặp lỗiHTTP 404khi kết nối WebSocket control.
📦 Cấu trúc dự án
raytunnel/
├── src/ # Source code Python
├── Dockerfile # Docker image definition
├── compose.yaml # Docker Compose config
├── example-env # Template file biến môi trường
├── raytunnel-server.service # Systemd service file
├── pyproject.toml
└── README.md
📥 Cài đặt
Cài từ PyPI (khuyên dùng)
uv pip install raytunnel
# hoặc: pip install raytunnel
Cài từ source (GitHub)
uv pip install git+https://github.com/mrtruongleo/raytunnel.git
# hoặc: pip install git+https://github.com/mrtruongleo/raytunnel.git
Cài ở chế độ editable (phát triển local)
git clone https://github.com/mrtruongleo/raytunnel.git
cd raytunnel
uv pip install -e .
🛠 Hướng dẫn Cấu hình & Triển khai
1. Triển khai Server (Trên Proxmox Container / VPS)
Bạn có ba cách để chạy server:
Cách A: Chạy trực tiếp qua Systemd Service (Khuyên dùng)
- Cài đặt package
raytunneltrên container:uv pip install raytunnel # hoặc: pip install raytunnel
- Copy file
raytunnel-server.servicevào/etc/systemd/system/:cp raytunnel-server.service /etc/systemd/system/
- Chỉnh sửa dòng
ExecStarttrong file service — thêm token và domain thực:ExecStart=/usr/local/bin/raytunnel server --host 0.0.0.0 --port 8001 --tcp-port 8000 --token your_very_strong_token --domain s.yourdomain.com
- Kích hoạt và chạy dịch vụ:
sudo systemctl daemon-reload sudo systemctl enable --now raytunnel-server sudo systemctl status raytunnel-server
Cách B: Chạy qua Docker Compose
- Copy file cấu hình môi trường:
cp example-env .env
- Chỉnh sửa
.envvới token và domain thực của bạn:RAYTUNNEL_TOKEN=your_very_strong_token DOMAIN=s.yourdomain.com
- Build và chạy từ thư mục gốc của project:
docker compose up -d --build
Verify sau khi start:
docker logs raytunnel-server --tail 20
curl http://localhost:8001 # → Dashboard hiện "No active tunnels"
[!WARNING] Không dùng shell variable
${RAYTUNNEL_TOKEN:-default}trong compose.yaml nếu bạn không set env trước khi chạy — sẽ dùng giá trịdefaultthay vì token thực. Luôn dùng file.envhoặc hardcode trực tiếp.
Cách C: Chạy qua Docker CLI trực tiếp
docker build -t raytunnel-server .
docker run -d --name raytunnel \
-p 8000:8000 -p 8001:8001 -p 2200-2300:2200-2300 \
-e RAYTUNNEL_TOKEN="your_very_strong_token" \
-e DOMAIN="s.yourdomain.com" \
raytunnel-server
2. Cấu hình DNS & Reverse Proxy
2.1 DNS trên Cloudflare
Tạo 2 bản ghi CNAME (hoặc A) trỏ về IP public của homelab:
| Type | Name | Target | Proxy |
|---|---|---|---|
CNAME |
s |
ip.yourdomain.com |
Proxied ☁️ |
CNAME |
*.s |
ip.yourdomain.com |
DNS only 🔘 |
[!WARNING] Cloudflare Free plan không hỗ trợ Proxied wildcard — bắt buộc để
*.slà DNS only (grey cloud). Verify bằng:dig rvc.s.yourdomain.com @1.1.1.1 +short
2.2 Port Forwarding trên Router
| External Port | Internal IP | Internal Port | Protocol |
|---|---|---|---|
443 |
IP của reverse proxy | 443 |
TCP |
2200-2300 |
IP của raytunnel server | 2200-2300 |
TCP |
[!NOTE] Cổng
2200-2300chỉ thực sự listen sau khi có client kết nối.Connection refusedtrên cổng này là bình thường khi chưa có tunnel nào hoạt động.
2.3 Cấu hình Traefik (Nếu bạn dùng Traefik)
Thêm service raytunnel vào Traefik bằng cách tạo một file config động trong thư mục dynamic config của Traefik (ví dụ: ./external/raytunnel.yaml):
http:
routers:
# Control plane: WebSocket /ws/control + Dashboard — cổng 8001 (FastAPI)
raytunnel-control:
rule: "Host(`s.yourdomain.com`)"
entryPoints:
- websecure
service: raytunnel-api
tls:
certResolver: production
# Tunnel traffic: worker.s.yourdomain.com — cổng 8000 (TCP router)
raytunnel-tunnel:
rule: "HostRegexp(`{subdomain:[a-z0-9-]+}.s.yourdomain.com`)"
entryPoints:
- websecure
service: raytunnel-tcp
tls:
certResolver: production
services:
raytunnel-api:
loadBalancer:
servers:
- url: "http://<IP_RAYTUNNEL_SERVER>:8001" # Control/WS API
raytunnel-tcp:
loadBalancer:
servers:
- url: "http://<IP_RAYTUNNEL_SERVER>:8000" # HTTP tunnel routing
[!NOTE] Thay
<IP_RAYTUNNEL_SERVER>bằng IP thực của container/VPS đang chạy raytunnel server. Đảm bảo thư mục dynamic config của Traefik đang được watch (thường cấu hình quaproviders.file.directorytrongtraefik.yaml).
2.4 Cấu hình Nginx Proxy Manager (Nếu bạn dùng NPM)
Nginx Proxy Manager cần 2 Proxy Host riêng biệt. Cả hai đều cần bật SSL với Let's Encrypt.
Proxy Host 1 — Control Plane (s.yourdomain.com)
Vào Proxy Hosts → Add Proxy Host, điền như sau:
| Trường | Giá trị |
|---|---|
| Domain Names | s.yourdomain.com |
| Scheme | http |
| Forward Hostname/IP | <IP_RAYTUNNEL_SERVER> |
| Forward Port | 8001 |
| Websockets Support | ✅ Bật |
| Block Common Exploits | ✅ Bật |
Tab SSL: Chọn Request a new SSL Certificate, bật Force SSL và HTTP/2 Support.
Proxy Host 2 — Tunnel Traffic (*.s.yourdomain.com)
Vào Proxy Hosts → Add Proxy Host, điền như sau:
| Trường | Giá trị |
|---|---|
| Domain Names | *.s.yourdomain.com |
| Scheme | http |
| Forward Hostname/IP | <IP_RAYTUNNEL_SERVER> |
| Forward Port | 8000 |
| Websockets Support | ✅ Bật |
| Block Common Exploits | ✅ Bật |
Tab SSL: Chọn Request a new SSL Certificate — NPM sẽ issue wildcard cert qua DNS Challenge.
[!IMPORTANT] Wildcard SSL (
*.s.yourdomain.com) bắt buộc phải dùng DNS Challenge (HTTP Challenge không hỗ trợ wildcard). Trong NPM, tab SSL → chọn DNS Challenge provider (ví dụ Cloudflare) → nhập API Token Cloudflare có quyềnZone:DNS:Edit.
[!WARNING] NPM không thực hiện routing theo subdomain ở tầng ứng dụng — toàn bộ
*.s.yourdomain.comđược forward thẳng về cổng8000của raytunnel server, và raytunnel server tự xử lý routing dựa vàoHostheader. Đây là hành vi đúng và mong muốn.
Tab Advanced của Proxy Host 2, thêm custom Nginx config để đảm bảo Host header được truyền đúng:
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
3. Cài đặt & Sử dụng Client (Trên Kaggle / Colab / Máy trạm)
# Cài đặt từ PyPI (khuyên dùng)
uv pip install raytunnel
# hoặc: pip install raytunnel
# Hoặc cài từ GitHub (lấy code mới nhất chưa release)
uv pip install git+https://github.com/mrtruongleo/raytunnel.git
Cách A: Sử dụng Command Line (CLI)
raytunnel client --server s.yourdomain.com --token your_very_strong_token --port 8000 --ssh --subdomain my-kaggle-job
Cách B: Chạy Programmatic trong Python / Jupyter Notebook
[!WARNING] Kaggle không hỗ trợ background process qua
!command &— sẽ gặp lỗiOSError: Background processes not supported. Phải dùngsubprocess.Popen.
import subprocess, time
proc = subprocess.Popen(
[
"raytunnel", "client",
"--server", "s.yourdomain.com",
"--token", "your_very_strong_token",
"--port", "8000", # Cổng local của Web App (RVC, Gradio, v.v.)
"--ssh", # Bật SSH Terminal
"--subdomain", "my-worker" # Subdomain mong muốn
],
stdout=open("raytunnel.log", "w"),
stderr=subprocess.STDOUT
)
print(f"Raytunnel PID: {proc.pid}")
# Chờ kết nối thiết lập
time.sleep(8)
with open("raytunnel.log") as f:
print(f.read())
Kết quả mong đợi trong log:
✔ Raytunnel Established Successfully!
HTTP Web App URL: https://my-worker.s.yourdomain.com
SSH Command: ssh -i ~/.ssh/raytunnel_id_ed25519 -p 2200 root@s.yourdomain.com
Dừng tunnel:
proc.terminate()
🔒 Cơ chế Bảo mật của SSH
Khi bạn khởi chạy client với tùy chọn --ssh:
- Client kiểm tra và tự động cài đặt
openssh-servernếu môi trường chưa có. - Sinh một cặp khóa SSH tạm thời: Private key
~/.ssh/raytunnel_id_ed25519và Public key được tự động thêm vào~/.ssh/authorized_keys. - Khởi chạy
sshdở cổng 2222 trong phạm vi user-space. - Server cấp phát một cổng SSH ngẫu nhiên trong dải
2200-2300chuyển tiếp về client. - Để kết nối SSH vào Kaggle từ máy cá nhân:
# Copy private key từ Kaggle về máy, sau đó: ssh -i raytunnel_id_ed25519 -p 2200 root@s.yourdomain.com
Server cấm đăng nhập bằng mật khẩu và chỉ nhận đúng khóa đã đăng ký — đảm bảo không ai brute-force được.
🌐 Sử dụng Tunnel sau khi kết nối
Sau khi client kết nối thành công, log sẽ in ra thông tin cần dùng:
✔ Raytunnel Established Successfully!
HTTP Web App URL : https://my-worker.s.yourdomain.com
SSH Command : ssh -i ~/.ssh/raytunnel_id_ed25519 -p 2200 root@s.yourdomain.com
🖥 Truy cập Web App qua HTTP Tunnel
Mở trình duyệt và truy cập URL được in ra — web app chạy trên remote instance (Gradio, FastAPI, RVC, v.v.) sẽ hiển thị như một trang web bình thường:
https://my-worker.s.yourdomain.com
Tất cả HTTP/HTTPS request (kể cả WebSocket) đều được tunnel tự động. Không cần cấu hình thêm bất cứ điều gì phía ứng dụng.
🔌 Tích hợp API từ máy local
Raytunnel là tunnel trong suốt — toàn bộ HTTP traffic được chuyển tiếp nguyên vẹn đến app của bạn đang chạy trên remote. Raytunnel không tự cung cấp bất kỳ endpoint nghiệp vụ nào.
Endpoint nào có thể gọi hoàn toàn phụ thuộc vào web app bạn đang chạy trên Kaggle/Colab (FastAPI, Gradio, Flask, v.v.):
import requests
# URL do raytunnel cung cấp, nhưng /your-endpoint là do app của bạn định nghĩa
BASE_URL = "https://my-worker.s.yourdomain.com"
response = requests.post(f"{BASE_URL}/your-endpoint", json={"key": "value"})
print(response.json())
Ví dụ nếu app của bạn là một FastAPI server expose /predict:
# Trên remote (Kaggle) — app FastAPI của bạn
# GET /health → {"status": "ok"}
# POST /predict → {"result": ...}
# Trên máy local — gọi qua tunnel bình thường
response = requests.get(f"{BASE_URL}/health")
response = requests.post(f"{BASE_URL}/predict", json={"data": [1, 2, 3]})
Với WebSocket:
import asyncio, websockets
async def stream():
async with websockets.connect("wss://my-worker.s.yourdomain.com/ws") as ws:
await ws.send("hello")
msg = await ws.recv()
print(msg)
asyncio.run(stream())
🔑 Kết nối SSH Terminal
Sau khi client in ra SSH Command, copy private key từ remote về máy local trước:
# 1. Copy private key từ remote về máy local qua SCP
scp -i ~/.ssh/raytunnel_id_ed25519 -P 2200 \
root@s.yourdomain.com:~/.ssh/raytunnel_id_ed25519 \
~/raytunnel_id_ed25519
# 2. Set quyền đúng cho key
chmod 600 ~/raytunnel_id_ed25519
Hoặc nếu đang dùng Kaggle Notebook, lấy key bằng Python:
# Chạy trong Kaggle cell để in ra private key
with open("/root/.ssh/raytunnel_id_ed25519") as f:
print(f.read())
# → Copy nội dung, paste vào file ~/raytunnel_id_ed25519 trên máy local
Sau đó SSH vào:
ssh -i ~/raytunnel_id_ed25519 -p 2200 root@s.yourdomain.com
Thêm vào ~/.ssh/config để gõ lệnh ngắn hơn:
Host kaggle-worker
HostName s.yourdomain.com
Port 2200
User root
IdentityFile ~/raytunnel_id_ed25519
StrictHostKeyChecking no
# Sau đó chỉ cần:
ssh kaggle-worker
💻 Kết nối VS Code Remote SSH
- Cài extension Remote - SSH trong VS Code.
- Thêm config SSH như trên vào
~/.ssh/config. - Nhấn
Ctrl+Shift+P→ Remote-SSH: Connect to Host → chọnkaggle-worker. - VS Code sẽ mở một cửa sổ editor hoàn toàn chạy trên remote instance — có thể duyệt file, chỉnh sửa code, dùng terminal như trên máy local.
📁 Truyền File qua SCP
# Upload file từ máy local lên remote
scp -i ~/raytunnel_id_ed25519 -P 2200 \
./local_file.wav \
root@s.yourdomain.com:/kaggle/working/
# Download file từ remote về máy local
scp -i ~/raytunnel_id_ed25519 -P 2200 \
root@s.yourdomain.com:/kaggle/working/output.wav \
./output.wav
Hoặc dùng rsync để đồng bộ thư mục:
rsync -avz -e "ssh -i ~/raytunnel_id_ed25519 -p 2200" \
./local_dir/ \
root@s.yourdomain.com:/kaggle/working/
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file raytunnel-0.1.1.tar.gz.
File metadata
- Download URL: raytunnel-0.1.1.tar.gz
- Upload date:
- Size: 55.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a32b70a7d6d206babecc2f87a8361756eda8627eecc769c0ffb7f9524ec6fb9c
|
|
| MD5 |
4f5d3c7b6bb0ae11d4b5c703229cc7a1
|
|
| BLAKE2b-256 |
50b614c7c1e2721e243515137d4ebb542f0a57081aa1ca5728371ae810de4bde
|
File details
Details for the file raytunnel-0.1.1-py3-none-any.whl.
File metadata
- Download URL: raytunnel-0.1.1-py3-none-any.whl
- Upload date:
- Size: 19.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4b404a6b57c4a8702346db8320e7ade50587fdf92db934d2a96d97fa95bf5d24
|
|
| MD5 |
1155ad32aebca1c54871dba210df71b6
|
|
| BLAKE2b-256 |
1a9e6a7ccd95e749e12e3649030ecd04986b574e17e8bcd062d407558855e57e
|