Skip to content

Commit 38dd0f9

Browse files
committed
feat(network): 新增 rathole 裸二进制转发模板
1 parent 66e2e48 commit 38dd0f9

12 files changed

Lines changed: 845 additions & 4 deletions

config/network/rathole/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# 真实 rathole 配置包含 token、内网地址、公网白名单目标等私有信息,不应提交。
2+
*.local.toml
3+
4+
# PM2 运行日志由本机生成,仓库只保留目录和示例配置。
5+
logs/*.log
6+
logs/*.pid
7+
!logs/.gitkeep

config/network/rathole/README.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# rathole template
2+
3+
这个目录提供 rathole 裸二进制 + PM2 的配置示例和维护脚本。它适合把内网服务暴露到一台公网入口服务器,也适合“目标服务只允许某台公网 IP 访问”的白名单转发场景。
4+
5+
目录职责:
6+
7+
- `server.example.toml`
8+
作用:rathole server 示例,复制为 `server.local.toml` 后在公网入口机器运行。
9+
- `client.example.toml`
10+
作用:rathole client 示例,复制为 `client.local.toml` 后在内网服务所在机器运行。
11+
- `whitelist-proxy.example.toml`
12+
作用:公网白名单转发示例,复制为 `client.local.toml` 后在白名单公网服务器运行。
13+
- `rathole-server.pm2.config.cjs`
14+
作用:PM2 server 进程配置。
15+
- `rathole-client.pm2.config.cjs`
16+
作用:PM2 client 进程配置。
17+
- `start.ps1`
18+
作用:统一封装常用 PM2 操作,避免每次手写长命令。
19+
20+
## 设计说明
21+
22+
- rathole 本身是单二进制,常驻资源占用比容器方案更直接。
23+
- PM2 负责后台运行、日志、重启和开机恢复;rathole 负责 TCP/UDP 四层转发。
24+
- server/client 的 PM2 配置拆成两个文件,避免一台机器误启动不需要的一端。
25+
- 真实配置统一使用 `.local.toml`,其中可能包含 token、内网地址和白名单目标地址,默认不会提交到 Git。
26+
27+
## 准备 rathole 和 PM2
28+
29+
先确认机器上可以直接运行 rathole:
30+
31+
```bash
32+
rathole --version
33+
```
34+
35+
如果 rathole 不在 `PATH`,可以通过环境变量指定二进制路径:
36+
37+
```bash
38+
RATHOLE_BIN=/opt/rathole/rathole pm2 start config/network/rathole/rathole-client.pm2.config.cjs
39+
```
40+
41+
安装 PM2:
42+
43+
```bash
44+
npm install -g pm2
45+
```
46+
47+
## 基础 server/client 转发
48+
49+
1. 在公网入口机器复制 server 配置:
50+
51+
```powershell
52+
Copy-Item `
53+
-LiteralPath config/network/rathole/server.example.toml `
54+
-Destination config/network/rathole/server.local.toml
55+
```
56+
57+
编辑 `server.local.toml`
58+
59+
- `server.bind_addr`:rathole client 连接入口,例如 `0.0.0.0:2333`
60+
- `server.services.<name>.token`:每个服务独立 token
61+
- `server.services.<name>.bind_addr`:对外暴露端口,例如 `0.0.0.0:5202`
62+
63+
2. 在内网服务所在机器复制 client 配置:
64+
65+
```powershell
66+
Copy-Item `
67+
-LiteralPath config/network/rathole/client.example.toml `
68+
-Destination config/network/rathole/client.local.toml
69+
```
70+
71+
编辑 `client.local.toml`
72+
73+
- `client.remote_addr`:公网入口机器地址,例如 `rathole.example.com:2333`
74+
- `client.services.<name>.token`:必须与 server 端同名服务一致
75+
- `client.services.<name>.local_addr`:本机或内网服务地址,例如 `127.0.0.1:22`
76+
77+
3. 启动:
78+
79+
```powershell
80+
# 公网入口机器
81+
./config/network/rathole/start.ps1 start -Role server
82+
83+
# 内网服务机器
84+
./config/network/rathole/start.ps1 start -Role client
85+
```
86+
87+
## 公网白名单转发
88+
89+
如果某个第三方服务只允许固定公网 IP 访问,可以把 rathole client 部署在那台已加入白名单的公网服务器上:
90+
91+
```powershell
92+
Copy-Item `
93+
-LiteralPath config/network/rathole/whitelist-proxy.example.toml `
94+
-Destination config/network/rathole/client.local.toml
95+
```
96+
97+
编辑 `client.local.toml`
98+
99+
- `client.remote_addr`:rathole 入口 server 地址
100+
- `client.services.<name>.local_addr`:白名单目标服务地址,例如 `api.allowlist-only.example.com:443`
101+
- `client.services.<name>.token`:与入口 server 同名服务一致
102+
103+
访问链路:
104+
105+
```text
106+
调用方 -> rathole server 暴露端口 -> rathole 隧道 -> 白名单公网服务器上的 rathole client -> 白名单目标服务
107+
```
108+
109+
目标服务看到的来源 IP 是白名单公网服务器。注意这个模式是 TCP/UDP 四层端口转发,不处理 HTTP Host、路径、Header 或 TLS 终止;如果需要七层路由,继续使用 Nginx、Caddy 或 Traefik。
110+
111+
## PM2 管理
112+
113+
默认管理 client:
114+
115+
```powershell
116+
./config/network/rathole/start.ps1
117+
```
118+
119+
常用命令:
120+
121+
```powershell
122+
./config/network/rathole/start.ps1 start -Role client
123+
./config/network/rathole/start.ps1 start -Role server
124+
./config/network/rathole/start.ps1 logs --lines 100
125+
./config/network/rathole/start.ps1 status
126+
./config/network/rathole/start.ps1 restart -Role client
127+
./config/network/rathole/start.ps1 stop -Role client
128+
./config/network/rathole/start.ps1 delete -Role client
129+
./config/network/rathole/start.ps1 save
130+
./config/network/rathole/start.ps1 config -Role client
131+
./config/network/rathole/start.ps1 -DryRun
132+
```
133+
134+
保存当前 PM2 进程列表:
135+
136+
```bash
137+
pm2 save
138+
```
139+
140+
配置开机恢复:
141+
142+
```bash
143+
pm2 startup
144+
pm2 save
145+
```
146+
147+
## 裸二进制直接运行
148+
149+
不需要 PM2 时可以直接运行:
150+
151+
```bash
152+
rathole config/network/rathole/server.local.toml
153+
rathole config/network/rathole/client.local.toml
154+
```
155+
156+
也可以显式指定模式:
157+
158+
```bash
159+
rathole --server config/network/rathole/server.local.toml
160+
rathole --client config/network/rathole/client.local.toml
161+
```
162+
163+
## Docker Compose 备选
164+
165+
如果后续你更想用容器,可以把同样的 `.local.toml` 挂载进 rathole 镜像。但本目录第一版不提供 Compose 生命周期脚本,因为当前主线是低资源占用的裸二进制 + PM2。
166+
167+
## 排查
168+
169+
查看 PM2 状态和日志:
170+
171+
```powershell
172+
./config/network/rathole/start.ps1 status -Role client
173+
./config/network/rathole/start.ps1 logs -Role client --lines 200
174+
```
175+
176+
确认端口监听:
177+
178+
```bash
179+
ss -lntup | grep -E '2333|5202|8080'
180+
```
181+
182+
确认 token 与服务名:
183+
184+
- server/client 两端的 service 名必须一致
185+
- 同名 service 的 token 必须一致
186+
- server 的 `bind_addr` 是暴露给调用方的端口
187+
- client 的 `local_addr` 是最终要访问的服务地址
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# rathole client 示例配置。
2+
# 复制为 client.local.toml 后再填入真实 server 地址、token 与本地服务地址。
3+
4+
[client]
5+
remote_addr = "rathole.example.com:2333"
6+
heartbeat_timeout = 40
7+
retry_interval = 1
8+
9+
[client.services.ssh_home]
10+
token = "replace-with-a-long-random-token"
11+
local_addr = "127.0.0.1:22"
12+
13+
[client.services.web_home]
14+
token = "replace-with-another-long-random-token"
15+
local_addr = "127.0.0.1:3000"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const fs = require('node:fs')
2+
const path = require('node:path')
3+
4+
const scriptDir = __dirname
5+
const logDir = path.join(scriptDir, 'logs')
6+
7+
// PM2 启动前确保日志目录存在,避免首次部署时因为目录缺失而启动失败。
8+
fs.mkdirSync(logDir, { recursive: true })
9+
10+
module.exports = {
11+
apps: [
12+
{
13+
name: 'rathole-client',
14+
cwd: scriptDir,
15+
script: process.env.RATHOLE_BIN || 'rathole',
16+
interpreter: 'none',
17+
args: [path.join(scriptDir, 'client.local.toml')],
18+
exec_mode: 'fork',
19+
instances: 1,
20+
autorestart: true,
21+
watch: false,
22+
time: true,
23+
restart_delay: 5000,
24+
max_restarts: 10,
25+
out_file: path.join(logDir, 'rathole-client.out.log'),
26+
error_file: path.join(logDir, 'rathole-client.err.log'),
27+
pid_file: path.join(logDir, 'rathole-client.pid'),
28+
env: {
29+
RUST_LOG: process.env.RUST_LOG || 'info',
30+
},
31+
},
32+
],
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const fs = require('node:fs')
2+
const path = require('node:path')
3+
4+
const scriptDir = __dirname
5+
const logDir = path.join(scriptDir, 'logs')
6+
7+
// PM2 启动前确保日志目录存在,避免首次部署时因为目录缺失而启动失败。
8+
fs.mkdirSync(logDir, { recursive: true })
9+
10+
module.exports = {
11+
apps: [
12+
{
13+
name: 'rathole-server',
14+
cwd: scriptDir,
15+
script: process.env.RATHOLE_BIN || 'rathole',
16+
interpreter: 'none',
17+
args: [path.join(scriptDir, 'server.local.toml')],
18+
exec_mode: 'fork',
19+
instances: 1,
20+
autorestart: true,
21+
watch: false,
22+
time: true,
23+
restart_delay: 5000,
24+
max_restarts: 10,
25+
out_file: path.join(logDir, 'rathole-server.out.log'),
26+
error_file: path.join(logDir, 'rathole-server.err.log'),
27+
pid_file: path.join(logDir, 'rathole-server.pid'),
28+
env: {
29+
RUST_LOG: process.env.RUST_LOG || 'info',
30+
},
31+
},
32+
],
33+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# rathole server 示例配置。
2+
# 复制为 server.local.toml 后再填入真实 token 与公网监听端口。
3+
4+
[server]
5+
bind_addr = "0.0.0.0:2333"
6+
heartbeat_interval = 30
7+
8+
[server.services.ssh_home]
9+
token = "replace-with-a-long-random-token"
10+
bind_addr = "0.0.0.0:5202"
11+
12+
[server.services.web_home]
13+
token = "replace-with-another-long-random-token"
14+
bind_addr = "0.0.0.0:8080"

0 commit comments

Comments
 (0)