11 KiB
FRPS 服务端自动管理方案(frps-manager)
目标:frpc 端在 Havline 提交穿透规则后,frps 端自动完成反向代理配置与生效,实现域名无端口访问(
http://tv.36800.cc),并自动管理 VPS 侧证书。本文是基于现有项目(Havline v1.1.0 FRP 多服务端模块)的实施方案,不引入新服务、不改 frp 二进制。
1. 现状与问题
当前(v1.1.0)frpc 侧规则提交后:
| 环节 | 现状 | 问题 |
|---|---|---|
| tcp 规则注册(远程端口) | ✅ 自动(frpc 登录时向 frps 动态注册) | 无 |
| http 规则域名路由 | ⚠️ 需 frps.toml 预先配置 vhostHTTPPort |
首次使用 http 类型时 VPS 要手工改配置+重启 |
| frps.toml 生成 | ✅ Havline 一键生成,用户手动复制到 VPS | 每次变更都要 SSH 手工同步 |
| VPS 端口释放(80/443 被占) | ❌ 手动 SSH 停 Nginx | port unavailable 最常见根因 |
| 改配置后生效 | ❌ 手动 pkill frps 重启(frp 无热加载) |
容易忘、容易误 |
| VPS 侧证书 | ❌ 无 | HTTPS 要在 VPS 手工装证书 |
| 配置漂移检测 | ❌ 无 | VPS 上的文件与 Havline 数据库可能不一致 |
2. 总体设计
在 Havline(frpc 所在机器)新增 frps-manager 子系统:Havline 通过 SSH 连接 VPS,执行「检测 → 生成 → 推送 → 生效 → 验证」闭环。VPS 侧零安装(只要求 frps + SSH)。
Havline (NAS) VPS (38.95.74.87)
┌─────────────────────────────┐ ┌──────────────────────────┐
│ FRP 模块(现有) │ │ frps (7000/80/443/7500) │
│ 规则/服务端 CRUD │ │ ↑ frpc 动态注册端口 │
│ frps-manager(新增) │ SSH │ frps.toml (推送产物) │
│ 检测→生成→推送→reload→验证 │ ────────→ │ /etc/havline/frps/ │
│ 证书申请→推送→挂载 │ │ /etc/havline/frps/certs/ │
└─────────────────────────────┘ └──────────────────────────┘
核心思路:frps 不认识"反代",它只认 frps.toml。因此"自动反代"= ① 规则变化时重算出应然的 frps.toml(含 vhost 端口、subDomainHost、TLS 配置)→ ② 与 VPS 现状 diff → ③ 无变化则跳过,有变化则原子推送 → ④ 通过 frps admin API 热重载(无需重启进程)→ ⑤ 回读验证。证书同理:Havline 证书页现有 ACME 能力签好后把证书文件推到 VPS 并写入 frps.toml 的 transport.tls/webServer.tls 段。
3. 服务端模型扩展(frp_servers 表)
新增「服务端连接信息」(加密存储,复用 secret.Box):
| 字段 | 类型 | 说明 |
|---|---|---|
ssh_host |
TEXT | VPS SSH 地址(可与 server_addr 不同) |
ssh_port |
INTEGER | 默认 22 |
ssh_user |
TEXT | 建议 root 或具备 sudo 的用户 |
ssh_auth |
TEXT | password / key |
ssh_secret_enc |
TEXT | 密码或私钥(加密) |
frps_dir |
TEXT | VPS 上 frps 目录,默认 /etc/havline/frps |
frps_bin |
TEXT | frps 二进制路径,默认 /usr/local/bin/frps |
mgmt_enabled |
INTEGER | 是否启用自动管理(0/1,默认 0=纯手动,向后兼容) |
迁移 023_frp_server_ssh.sql。SSH 凭据用 secret.Box 加密,接口不回显(同 Token 处理)。
4. 核心流程
4.1 检测(Probe)——服务端详情新增「检测服务端」按钮
一次 SSH 会话内批量执行(只读,不改任何东西):
frps --version # 版本
ss -tlnp | grep -E ':(80|443|7000) ' # 端口占用者(frps? nginx? 其他?)
systemctl is-active nginx frps # 服务状态
cat /proc/$(pidof frps)/cmdline # 实际使用的配置路径
cat <配置路径> # 现有 frps.toml 内容(做 diff)
返回结构化报告:frps 版本/运行状态/配置路径、80/443 占用者判定(frps/nginx/其他/空闲)、现有 frps.toml 与 Havline 期望配置的 diff 摘要、待办建议(如「80 被 nginx 占用,需释放」)。这就是用户之前反复手工排查的自动化。
4.2 下发(Apply)——「自动反代」的主流程
触发时机:用户点「下发配置」,或规则保存后若 mgmt_enabled 且检测到配置将变化(规则含 http 类型 / 证书变更)自动触发。
- 生成期望配置:扩展现有
buildFRPSServerConfig:- 存在 http/https/tcpmux 规则 → 输出
vhostHTTPPort = 80、vhostHTTPSPort = 443(去端口访问的关键); - 存在 subdomain 规则 → 输出
subDomainHost = <用户在服务端表单新填的主域>; - 证书启用时输出
transport.tls.force/webServer.tls段;
- 存在 http/https/tcpmux 规则 → 输出
- SSH 推送(原子):新内容写
/etc/havline/frps/frps.toml.new→mv原子替换 → 旧文件留frps.toml.bak(回滚点); - 热重载:调 frps admin API
PUT /api/reload(frps webServer 7500 已有 Basic Auth 凭据)——不杀进程,连接不断;API 不可用时降级systemctl restart frps(nohup 场景提示用户); - 验证:重读 VPS 上的 frps.toml 确认与期望一致 +
ss确认 80/443 已由 frps 监听 + 通过公网侧发起一次探测请求; - 全程动作与输出写入系统日志(复用 logstore),UI 弹窗展示每步结果。
4.3 释放端口(安全加固提示,不偷偷杀人)
检测发现 80/443 被 nginx 等占用时,不自动杀进程(危险动作),而是:
- 检测报告中标红 + 给出一键命令;
- UI 提供「释放端口」按钮:二次确认后执行
systemctl stop nginx && systemctl disable nginx(仅当占用者确认为 nginx 时可点,其他占用者只提示不动手); - 释放后自动重跑 Apply 验证闭环。
4.4 证书管理(VPS 侧 HTTPS)
复用 Havline 证书页现有 ACME(lego)能力,证书签发在 NAS 侧完成(DNS-01,无需 VPS 参与),然后:
- 证书页选「部署到 VPS」→ 选目标服务端;
- SSH 推送
fullchain.pem/privkey.pem到/etc/havline/frps/certs/<域名>/(0600); - frps.toml 写入
webServer.tls.certFile/keyFile(管理面板 HTTPS);对外流量 TLS 若需要也在 frps 侧终止时,走transport.tls相关段(v0.71 支持transport.tls.force+ 证书); - 续期:Havline 证书定时任务续期成功后自动重推 + reload(证书 60 天滚动,VPS 侧无感)。
设计取舍:对外业务的 HTTPS 终止仍推荐在 Havline Nginx(NAS 侧)(本手册方案 A 架构),frps 侧证书用于 webServer 管理面板与
tls.force场景。若用户想让 VPS 终止 TLS,同一套推送机制也覆盖。
5. API 与 UI
5.1 新增 API(挂在 /api/frpmulti 下,鉴权与现有中间件一致)
| 方法/路径 | 作用 |
|---|---|
PUT /servers/{id}/ssh |
保存 SSH 连接信息(加密存储) |
GET /servers/{id}/probe |
检测:frps 状态/端口占用/配置 diff |
POST /servers/{id}/apply |
下发期望配置 + reload + 验证 |
POST /servers/{id}/release-port |
释放被占端口(body: {port, confirm: true}) |
POST /servers/{id}/push-cert |
推送证书到 VPS 并挂载 |
5.2 前端(FrpView.vue)
- 服务端表单「高级配置」折叠区新增「远程管理」分组:SSH 地址/端口/用户/认证方式/密钥(ConfiguredSecretField,v-model)/frps 目录;
- 服务端列表操作列新增「管理」按钮 → 弹窗(复用
proxy-modal自绘骨架)三块:检测报告(分色状态行)、diff 视图(<pre>左右对照期望/现状)、操作按钮(下发配置 / 释放端口 / 推送证书); - 规则保存时的自动下发提示:非阻塞 toast「检测到 vhost 配置变化,点击下发」。
6. 实现拆解与工作量
| 模块 | 文件 | 内容 | 量级 |
|---|---|---|---|
| 迁移 | migrations/023_frp_server_ssh.sql |
SSH 字段 | 小 |
| SSH 客户端 | internal/frp/ssh.go(新) |
连接池/单会话命令执行/SFTP 推送/超时 | 中(x/crypto/ssh 已在 go.mod) |
| 检测 | internal/frp/probe.go(新) |
探测命令编排 + 结果结构体 + 80 占用者判定 | 中 |
| 配置 diff/应用 | internal/frp/frps_apply.go(新) |
期望配置生成(扩展现有 frps_template)、diff、原子推送、admin API reload、验证 | 中 |
| 证书推送 | internal/frp/cert_push.go(新) |
证书文件推送 + frps.toml 证书段写入 | 小 |
| Service/API | frpmulti_handler.go + router.go |
5 个端点 | 小 |
| 前端 | FrpView.vue + client.ts |
管理弹窗 + 表单分组 + 按钮 | 中 |
| 测试 | probe_test.go frps_apply_test.go |
命令输出解析、diff、原子写逻辑 | 中 |
总计约 1500–2000 行(含测试),纯增量、不动现有链路(mgmt_enabled=0 时行为与 v1.1.0 完全一致)。
7. 安全设计
- SSH 凭据 secret.Box 加密落库,API 永不回显(同 token 掩码机制);
- 推荐文档引导用户用密钥登录(Havline 可一键生成 ed25519 密钥对、展示公钥让用户贴到 VPS
authorized_keys); - 所有远程命令为固定白名单模板(版本/端口/状态/替换文件/reload),不拼接用户输入,杜绝命令注入;SFTP 只写
/etc/havline/frps/前缀内路径; - 危险动作(释放端口、重启 frps)二次确认 + 动作日志;
- Apply 幂等:重复执行无副作用(diff 无变化即跳过);
- frps admin API 调用走本机 127.0.0.1:7500(SSH 远端执行 curl),不暴露公网。
8. 里程碑
| 阶段 | 内容 | 验收 |
|---|---|---|
| M1 | 迁移 + SSH 客户端 + 检测(Probe) | 服务端「检测」按钮能报告 frps 状态与 80 占用者 |
| M2 | 期望配置生成 + diff + 原子推送 + reload + 验证 | 改规则后一键下发,http://域名 直达,port unavailable 类问题由系统自愈 |
| M3 | 证书推送 + 续期联动 | 证书页一键部署到 VPS,续期自动重推 |
| M4 | UI 完善 + 文档(手册第 6 节追加方案 A') | 手册更新,port unavailable 类问题由系统自愈 |
9. 明确不做(本期)
- 不实现 VPS 伴生 agent(能力更强但需安装,作为后续可选演进);
- 不做 frps 二进制的远程安装/升级(用户自装,检测报告给出版本即可);
- 不做多 VPS 并行下发编排(现有逐服务端模型已覆盖);
- 不改 frp 二进制、不依赖 frp 私有接口(只用 admin API 与文件系统)。