13 KiB
mmw-agent
妙妙屋X 远程服务器代理程序。部署在子服务器上,负责与主控(miaomiaowux)通信,上报流量/速度数据,并接受主控的远程管理指令。
架构
┌─────────────────────────────────────────────────────┐
│ Master (miaomiaowux) │
│ │
│ /api/remote/ws WebSocket 双向通信 │
│ /api/remote/traffic HTTP 流量推送 │
│ /api/remote/speed HTTP 速度推送 │
│ /api/remote/heartbeat HTTP 心跳 │
└──────────────────────┬──────────────────────────────┘
│
┌────────────┼────────────┐
│ WebSocket │ HTTP Push │ Pull (被动)
└────────────┼────────────┘
│
┌──────────────────────▼──────────────────────────────┐
│ mmw-agent │
│ │
│ 内部组件: │
│ ├── Agent Client 连接管理 + 数据上报 │
│ ├── Collector Xray 流量采集 (/debug/vars) │
│ ├── Handler 本地管理 API (/api/child/*) │
│ └── xRPC Xray gRPC 管理 │
│ │
│ 监听端口: :23889 (管理API) │
│ Xray metrics: :38889 (/debug/vars) │
└─────────────────────────────────────────────────────┘
构建与运行
# 构建
go build -ldflags="-s -w" -o mmw-agent ./cmd/mmw-agent
# 运行(配置文件方式)
./mmw-agent -c config.yaml
# 运行(环境变量方式)
MMWX_MASTER_URL=https://master.example.com \
MMWX_MASTER_TOKEN=your-token \
./mmw-agent
配置
配置文件 (YAML)
master_server: "https://master.example.com"
remote_token: "your-server-token"
connection_mode: "auto" # auto | websocket | http | pull
listen_port: 23889
child_api_token: "" # 可选,pull模式API认证
traffic_report_interval: 60 # 秒
speed_report_interval: 3 # 秒
heartbeat_interval: 30 # 秒
xray_servers: # 可选,不配则自动发现
- config_path: "/usr/local/etc/xray/config.json"
环境变量
| 变量 | 说明 | 默认值 |
|---|---|---|
MMWX_MASTER_URL |
主控地址 | — |
MMWX_MASTER_TOKEN |
服务器令牌 | — |
MMWX_CONNECTION_MODE |
连接模式 | auto |
MMWX_LISTEN_PORT |
监听端口 | 23889 |
MMWX_CHILD_API_TOKEN |
Pull API 认证令牌 | — |
MMWX_TRAFFIC_INTERVAL |
流量上报间隔(秒) | 60 |
MMWX_SPEED_INTERVAL |
速度上报间隔(秒) | 3 |
MMWX_HEARTBEAT_INTERVAL |
心跳间隔(秒) | 30 |
环境变量优先级高于配置文件。
Xray 自动发现
未显式配置 xray_servers 时,agent 按以下路径搜索 Xray 配置:
/usr/local/etc/xray/config.json/etc/xray/config.json/opt/xray/config.json
连接模式
Auto(推荐)
自动回退链:WebSocket → HTTP → Pull,带指数退避重连。
尝试 WebSocket 连接
├── 成功 → 保持 WebSocket 双向通信
│ 断开后退避重连,期间通过 HTTP 发送流量保持在线
└── 失败 → 尝试 HTTP 推送
├── 成功 → 定时 HTTP POST 上报
│ 连续 5 次失败后重试上层
└── 失败 → 回退 Pull 模式
等待退避时间后重试上层
退避策略:基础 5s,指数增长,上限 5min。
WebSocket
全双向通信。支持:
- Agent → Master:流量、速度、心跳、证书结果、扫描结果、域延迟探测结果
- Master → Agent:证书请求、证书部署、令牌更新、域延迟探测
连续 5 次连接失败后自动切换到 Auto 模式回退。
HTTP
单向推送。Agent 定时 POST 数据到 Master:
/api/remote/traffic— 流量数据/api/remote/speed— 速度数据/api/remote/heartbeat— 心跳
Pull
被动模式。Agent 仅暴露本地 API,等待 Master 主动拉取:
GET /api/child/trafficGET /api/child/speed
认证机制
所有请求必须同时满足:
- User-Agent:
miaomiaowux/0.1 - Authorization:
Bearer <token>(或X-Remote-Tokenheader)
未通过认证的连接被静默丢弃(TCP hijack 后直接关闭,不返回 HTTP 响应)。
API 文档
健康检查(无需认证)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /health |
健康检查 |
本地管理 API (/api/child/*)
以下所有端点均需通过 Silent Auth 中间件认证。
服务管理
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/child/services/status |
获取 Xray/Nginx 服务运行状态 |
| POST | /api/child/services/control |
控制服务启停 |
// POST /api/child/services/control
{ "service": "xray", "action": "restart" }
// service: xray | nginx
// action: start | stop | restart
Xray 管理
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/child/xray/install |
安装 Xray(SSE 流式输出) |
| POST | /api/child/xray/remove |
卸载 Xray(SSE 流式输出) |
| GET | /api/child/xray/config |
获取 Xray 完整配置 |
| PUT | /api/child/xray/config |
写入 Xray 完整配置 |
| GET | /api/child/xray/config/files |
列出 Xray 配置文件 |
| GET | /api/child/xray/system-config |
获取 Xray 系统配置路径信息 |
Nginx 管理
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/child/nginx/install |
安装 Nginx(SSE 流式输出) |
| POST | /api/child/nginx/remove |
卸载 Nginx(SSE 流式输出) |
| GET | /api/child/nginx/config |
获取 Nginx 配置 |
| PUT | /api/child/nginx/config |
写入 Nginx 配置 |
| GET | /api/child/nginx/config/files |
列出 Nginx 配置文件 |
Xray 入站/出站/路由 (gRPC)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/child/inbounds |
列出所有入站 |
| POST | /api/child/inbounds |
添加入站 |
| PUT | /api/child/inbounds |
更新入站 |
| DELETE | /api/child/inbounds |
删除入站(query: ?tag=xxx) |
| GET | /api/child/outbounds |
列出所有出站 |
| POST | /api/child/outbounds |
添加出站 |
| PUT | /api/child/outbounds |
更新出站 |
| DELETE | /api/child/outbounds |
删除出站(query: ?tag=xxx) |
| GET | /api/child/routing |
获取路由规则 |
| PUT | /api/child/routing |
更新路由规则 |
请求体为标准 Xray JSON 配置对象。
扫描与探测
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/child/scan |
扫描 Xray 运行状态与入站配置 |
| POST | /api/child/domains/latency |
域名延迟探测(TCP) |
// POST /api/child/domains/latency
{
"domains": ["example.com", "test.org"],
"timeout_ms": 5000
}
// 并发 TCP 探测,自动去重,最多 200 个域名
扫描自动补全:/api/child/scan 会检查 Xray 配置完整性,自动补全缺失的 api、stats、policy 段及 API 入站和路由规则,补全后自动重启 Xray。
系统信息
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/child/system/info |
获取系统信息(OS、CPU、内存、磁盘) |
Pull 模式数据接口
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/child/traffic |
获取当前 Xray 流量统计 |
| GET | /api/child/speed |
获取当前系统网络速度 |
// GET /api/child/traffic 响应
{
"stats": {
"inbound": { "tag": { "uplink": 1024, "downlink": 2048 } },
"outbound": { "tag": { "uplink": 512, "downlink": 1024 } },
"user": { "email": { "uplink": 256, "downlink": 512 } }
}
}
// GET /api/child/speed 响应
{
"upload_speed": 102400,
"download_speed": 204800
}
// 单位:字节/秒 (B/s),来源:/proc/net/dev 物理网卡聚合
WebSocket 交互协议
连接地址:ws(s)://<master>/api/remote/ws
消息格式
所有消息为 JSON,统一结构:
{
"type": "<message_type>",
"payload": { ... }
}
连接流程
Agent Master
│ │
│──── WebSocket Connect ────────────────>│
│ Header: User-Agent: miaomiaowux/0.1
│ │
│──── auth ─────────────────────────────>│
│ { "token": "server-token" } │
│ │
│<─── auth_result ──────────────────────│
│ { "success": true } │
│ │
│──── traffic (定时) ───────────────────>│
│──── speed (定时) ─────────────────────>│
│──── heartbeat (定时) ─────────────────>│
│ │
│<─── cert_request ─────────────────────│
│──── cert_update ──────────────────────>│
│ │
│<─── cert_deploy ──────────────────────│
│<─── token_update ─────────────────────│
│<─── domain_latency_probe ─────────────│
│──── domain_latency_result ────────────>│
│ │
│──── scan_result (首次连接) ───────────>│
消息类型
Agent → Master
| type | 说明 | payload |
|---|---|---|
auth |
认证 | { "token": "string" } |
traffic |
流量数据 | { "stats": { "inbound": {}, "outbound": {}, "user": {} } } |
speed |
实时速度 | { "upload_speed": int64, "download_speed": int64 } |
heartbeat |
心跳 | { "boot_time": "RFC3339" } |
cert_update |
证书申请结果 | { "cert_id": int, "domain": "string", "success": bool, "cert_pem": "string", "key_pem": "string", "issue_date": "time", "expiry_date": "time", "error": "string" } |
scan_result |
Xray 扫描结果 | { "xray_running": bool, "xray_version": "string", "inbounds": [...] } |
domain_latency_result |
域延迟探测结果 | { "request_id": "string", "success": bool, "results": [{ "domain": "string", "latency_ms": int64, "success": bool }] } |
Master → Agent
| type | 说明 | payload |
|---|---|---|
auth_result |
认证结果 | { "success": bool, "message": "string" } |
cert_request |
请求申请证书 | { "cert_id": int, "domain": "string", "email": "string", "provider": "string", "challenge_mode": "string", ... } |
cert_deploy |
部署证书文件 | { "domain": "string", "cert_pem": "string", "key_pem": "string", "cert_path": "string", "key_path": "string", "reload": "nginx|xray|both|none" } |
token_update |
推送新令牌 | { "server_token": "string", "expires_at": "RFC3339" } |
domain_latency_probe |
域延迟探测请求 | { "request_id": "string", "domains": ["string"], "timeout_ms": int } |
认证流程
- Agent 建立 WebSocket 连接(必须携带
User-Agent: miaomiaowux/0.1) - Agent 发送
auth消息,携带服务器令牌 - Master 验证令牌,返回
auth_result - 认证成功后进入消息循环;未认证的消息会收到
auth_result { success: false, message: "Authentication required" }
令牌轮换
Master 可通过 token_update 推送新令牌。Agent 收到后:
- 将新令牌持久化到本地配置
- 后续所有请求使用新令牌
- Master 端同步更新连接映射
首次连接自动扫描
Agent 认证成功后自动执行 Xray 扫描,将结果通过 scan_result 发送给 Master。如果服务器处于 pending 状态且配置了域名和 443 端口,Master 会自动触发 steal-self 配置部署。
数据采集
流量采集
从 Xray 的 /debug/vars HTTP 端点采集,解析 stats 对象中的 inbound/outbound/user 流量数据。
metrics 地址从 Xray 配置文件的 api 段中的 listen 字段获取,默认 127.0.0.1:38889。
速度采集
读取 /proc/net/dev,聚合所有物理网卡(排除 lo)的收发字节数,计算两次采样间的速率差值。
上传速度 = (当前 TX - 上次 TX) / 采样间隔
下载速度 = (当前 RX - 上次 RX) / 采样间隔