12 releases (6 breaking)
| 0.7.0 | Jul 20, 2026 |
|---|---|
| 0.6.0 | Jul 13, 2026 |
| 0.5.0 | Jul 13, 2026 |
| 0.4.1 |
|
| 0.1.0 | Jul 12, 2026 |
#430 in HTTP server
76KB
1.5K
SLoC
Gout
受 frp 和 rathole 启发的轻量级内网穿透工具。一行命令将本地服务暴露到公网。
特点
- 零配置客户端:
gout login server:8080 key→gout tcp 4000,无需手写配置文件 - Web 管理面板:在浏览器中管理 API key、查看活跃隧道、添加备注
- REST 控制面:隧道通过 HTTP API 创建和销毁,不依赖配置文件热加载
- 多协议支持:TCP、UDP 和 HTTP(v0.1 中 HTTP 是 TCP 别名)
- 信号通道架构:每个隧道一条轻量控制连接,通知客户端有新的外部连接;每条连接使用独立的数据通道
- Token 认证:每个隧道分配一个随机 64 位 token,用于数据通道身份验证
- 端口池管理:基于空闲池的端口分配,无碎片问题
- 自动清理:客户端 30 秒内未完成数据通道握手时隧道自动过期;控制连接断开时全部清理
架构
gout (CLI) ──REST──► goutd :8080 (HTTP API + Web 面板)
│ │
└──数据通道──────► goutd :8081 (原始 TCP)
项目是一个 Cargo workspace,包含三个 crate:
- gout-api — Rust SDK:协议类型、
GoutClient(隧道操作)、GoutAdminClient(管理操作)、data_channel(握手/pipe) - gout — CLI 客户端:login/tcp/udp/http 子命令;读取
~/.goutrc - goutd — 服务端守护进程:axum HTTP 服务器 + tokio 数据通道 TCP 服务器
安装
cargo install goutd # 服务端
cargo install gout # 客户端
或从源码编译:
git clone https://github.com/fb0sh/Gout.git
cd Gout
cargo build -p goutd -p gout
Rust SDK(在 Cargo.toml 中添加):
gout-api = "0.1"
快速开始
服务端
# 在公网 VPS 上运行
goutd
# 输出:
# ──────────────────────────────────────────
# Initial admin key: sk-xxxxxxxxxxxx
# Save this key! It won't be shown again.
# ──────────────────────────────────────────
# HTTP server listening on http://127.0.0.1:8080
# Data server listening on 0.0.0.0:8081
Web 面板默认只监听 127.0.0.1,通过 SSH 端口转发访问:
ssh -L 8080:localhost:8080 your-server
# 浏览器打开 http://localhost:8080
客户端
# 通过 Web 面板创建普通 API key,然后:
gout login server.example.com:8080 sk-xxxxxxxxxxxx
gout tcp 4000 # 将本地 localhost:4000 暴露到公网
REST API 参考
所有请求和响应均为 JSON 格式。
认证
两种 key 类型:
| Header | 类型 | 用途 |
|---|---|---|
X-Admin-Key |
admin | 管理 API key(增删查) |
X-Api-Key |
tunnel | 创建/删除隧道 |
首次启动时自动生成 admin key,打印到 stdout。 通过 Web 面板或管理 API 创建普通 tunnel key。
管理 API
创建 API key
POST /api/v1/keys
X-Admin-Key: <admin-key>
Content-Type: application/json
{"name": "我的笔记本"}
{"success": true, "data": {"key": "sk-xxx...", "name": "我的笔记本"}}
列出所有 key
GET /api/v1/keys
X-Admin-Key: <admin-key>
{"success": true, "data": [{"key": "sk-xxx...", "name": "我的笔记本"}]}
删除 key
DELETE /api/v1/keys/sk-xxx...
X-Admin-Key: <admin-key>
{"success": true, "data": null}
隧道 API
创建隧道
POST /api/v1/tunnels
X-Api-Key: <tunnel-key>
Content-Type: application/json
{"type": "tcp", "local_port": 4000}
{
"success": true,
"data": {
"token": 15735302723313469543,
"public_port": 10000,
"data_port": 8081,
"tunnel_type": "tcp"
}
}
创建后客户端需要连接数据端口(data_port)完成握手,详见"数据通道协议"。
删除隧道
DELETE /api/v1/tunnels/15735302723313469543
X-Api-Key: <tunnel-key>
{"success": true, "data": null}
隧道类型
| type | 说明 |
|---|---|
tcp |
TCP 隧道,每个外部连接一条独立数据通道 |
udp |
UDP 隧道,一条持久数据通道承载数据报帧 |
http |
v0.1 等价于 tcp |
数据通道协议
创建隧道后的数据通道握手流程:
客户端 服务端
│ │
│── [token: u64 BE][type: u8] ──►│ 握手(9 字节)
│◄──── [status: u8] ─────────────│ 0x01=OK, 0x00=Error
│ │
│ (TCP:信号通道循环) │
│◄──── [0x02] ───────────────────│ 新外部连接通知
│ │
│ 客户端另开连接: │
│── [token][type] ──────────────►│ 数据通道握手
│◄──── [0x01] ──────────────────│ OK
│══════ raw bytes ══════════════│ 双向 pipe
UDP 隧道使用帧格式:
[len: u16 BE][data: len bytes]
len = 0 表示关闭信号
Rust SDK (gout-api)
gout-api 是 Rust crate,提供协议类型和客户端。
添加依赖
[dependencies]
gout-api = { git = "https://github.com/fb0sh/Gout" }
GoutClient(隧道操作)
use gout_api::client::GoutClient;
use gout_api::TunnelType;
let gout = GoutClient::new("server.example.com:8080", "sk-xxxx...");
// 创建隧道
let tunnel = gout.create_tunnel(TunnelType::Tcp, 4000).await?;
println!("公网端口: {}", tunnel.public_port);
// 连接数据端口(握手由 data_channel 模块处理)
let mut stream = tokio::net::TcpStream::connect(
format!("server.example.com:{}", tunnel.data_port)
).await?;
gout_api::data_channel::client_handshake(
&mut stream, tunnel.token, TunnelType::Tcp
).await?;
// 连接本地服务
let local = tokio::net::TcpStream::connect("127.0.0.1:4000").await?;
// 双向 pipe
gout_api::data_channel::pipe_bidirectional(stream, local).await;
// 删除隧道
gout.delete_tunnel(tunnel.token).await?;
GoutAdminClient(管理操作)
use gout_api::admin::GoutAdminClient;
let admin = GoutAdminClient::new("server.example.com:8080", "admin-key-xxx...");
// 创建 tunnel key
let key = admin.create_key("我的笔记本").await?;
println!("新 key: {}", key.key);
// 列出所有 key
let keys = admin.list_keys().await?;
for k in keys {
println!("{} ({})", k.name, k.key);
}
// 删除 key
admin.delete_key("sk-xxx...").await?;
数据通道协议(底层)
use gout_api::data_channel;
// 客户端握手
data_channel::client_handshake(&mut stream, token, TunnelType::Tcp).await?;
// 服务端接收握手
let (token, tt) = data_channel::server_receive_handshake(&mut stream).await?;
// 服务端确认/拒绝
data_channel::server_accept(&mut stream).await?;
data_channel::server_reject(&mut stream, "reason").await?;
// 双向 pipe
data_channel::pipe_bidirectional(a, b).await;
开发
cargo build # 编译全部
cargo test # 运行所有测试
cargo run -p goutd # 启动服务端
cargo run -p gout # CLI 客户端
License
MIT
Dependencies
~13–30MB
~331K SLoC