# FRPS 服务端管理方案(fonu-agent,部署于公网服务器) > 需求:frpc 端(Fonu/NAS)提交穿透规则后,**公网服务器端自动完成反向代理设置**,实现域名无端口访问(`http://tv.36800.cc`),并自动管理服务器侧证书与 frps。 > 实现形态:在公网 VPS 上部署一个常驻管理组件 **fonu-agent**(单个 Go 二进制 + systemd),对外提供管理 API;Fonu 在规则变化时调用该 API,agent 在 VPS 本地完成 Nginx 反代配置、frps 管理与证书部署。 > **安装与管理通道**:agent 的**首次安装、升级、卸载通过 SSH 由 Fonu 远程完成**(用户在 Fonu 界面填 SSH 信息点按钮即可,无需手工登 VPS);装好后 agent 以 **API** 接受日常管理。管理面板全部在本地 Fonu,VPS 上没有任何面板。 > 修订记录:v2 混合形态(SSH 装/卸 + API 管理);v1 为纯 SSH 推送方案,v2 初稿为「agent 纯 API」方案,两版历史见文末对照。 ## 1. 目标与效果 | 诉求 | 达成方式 | | --- | --- | | frpc 提交规则后 frps 端自动反代 | Fonu 保存规则 → 调 agent API 注册域名路由 → agent 写 Nginx vhost 并 reload | | 无端口访问 | agent 的 Nginx 监听 80(443),`proxy_pass` 到 frps vhost 端口,URL 不带端口号 | | 管理证书 | Fonu 证书页(DNS-01)签发后一键推送到 agent,自动挂 443 并续期重推 | | frps 管理 | 配置下发(vhost 端口等)、进程重启/状态/版本、端口占用诊断,全部 API 化 | | VPS 上已有 Nginx/站点 | 共存:agent 只写自己的 vhost 文件(`conf.d/fonu-*.conf`),不动其他配置 | 最终链路(http 规则为例): ```text 用户 → http://tv.36800.cc(80,无端口) → VPS Nginx(agent 生成的 vhost)→ 127.0.0.1:8080(frps vhostHTTPPort) → frps 按域名路由 → FRP 隧道 → NAS frpc → 192.168.31.152:7023 ``` ## 2. 总体架构 ```text Fonu (NAS,管理面板所在地) 公网服务器 (VPS) ┌──────────────────────────────┐ ┌────────────────────────────────┐ │ FRP 模块(现有) │ │ frps :7000 控制 / 8080 vhost │ │ 规则 CRUD / frpc / frps.toml │ │ nginx :80/443 │ │ 证书页(lego DNS-01,已有) │ │ └─ fonu-*.conf(agent 产物) │ │ ┌────────────────────┐ │ │ fonu-agent :7700(常驻) │ │ │ SSH 安装器(新增) │──SSH──┼→ 首次安装/升级/卸载 agent │ │ │ agent 客户端(新增) │──API──┼→ 日常管理:路由/配置/证书/状态 │ │ └────────────────────┘ │ │ ├─ 路由管理(nginx vhost 编排) │ │ 管理面板 = Fonu 网页 │ │ ├─ frps 管理(配置/进程/诊断) │ │ (规则页/证书页/管理弹窗) │ │ └─ 证书接收与挂载 │ └──────────────────────────────┘ └────────────────────────────────┘ ``` 两条通道各司其职: - **SSH(低频)**:仅用于 agent 生命周期——安装、升级、卸载、agent 失联自救。用户在 Fonu 填一次 SSH 信息(可选保存备用); - **API(高频)**:日常全部管理——规则同步、frps 配置下发、证书推送、状态查询。面板 100% 在 Fonu 本地,VPS 上没有任何管理界面。 职责划分:Fonu 是**控制面**(用户在哪配置就存哪),agent 是**执行面**(只接收指令并落实),frps/Nginx 是**数据面**。agent 无状态(配置即文件),可随时重装。 ## 3. fonu-agent 设计(VPS 侧) 单个 Go 二进制(复用主项目代码编译,`cmd/fonu-agent`),systemd 常驻,管理 API 监听 `:7700`(可配置)。 ### 3.1 管理 API(Bearer Token 鉴权) | 方法/路径 | 作用 | | --- | --- | | `GET /api/v1/ping` | 连通性/鉴权校验(Fonu「测试连接」按钮) | | `GET /api/v1/status` | frps 版本/运行方式/配置路径、80/443/7000/8080 端口占用者、已注册路由、证书有效期 | | `PUT /api/v1/routes` | 注册/更新域名路由:`{domain, upstream(=frps vhost 端口), tls, cert_id}` → 写 Nginx vhost → `nginx -t` → reload(失败自动回滚文件) | | `DELETE /api/v1/routes/{domain}` | 注销路由(规则删除/停用时调用) | | `GET /api/v1/routes` | 当前路由清单(对账用) | | `PUT /api/v1/frps/config` | 下发完整 frps.toml(Fonu 同源生成)→ 备份旧文件 → 按运行方式重启/重载 → 回读验证 | | `POST /api/v1/certs` | 部署证书:`{domain, fullchain_pem, key_pem}` → 写 `/var/lib/fonu-agent/certs//`(0600)→ 更新 443 vhost → reload | ### 3.2 Nginx 编排(反代核心) - 每个域名一个文件:`/etc/nginx/conf.d/fonu-tv.36800.cc.conf`,模板: ```nginx server { listen 80; server_name tv.36800.cc; location / { proxy_pass http://127.0.0.1:8080; # frps vhostHTTPPort proxy_set_header Host $host; # frps 按域名分流,必须保留 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; } } # 启用证书后追加 443 server 块(ssl_certificate 指向 agent 证书目录)+ 80 跳转 ``` - 写入流程:写 `.tmp` → `nginx -t` 校验 → 通过则替换并 `nginx -s reload`,失败保留原文件并把 `nginx -t` 输出回传给 Fonu 显示; - **只增删自己的 `fonu-*.conf`**,绝不触碰其他配置 → 与宝塔/手工站点共存; - 首次运行自检:nginx 是否安装、`conf.d`/`sites-enabled` 目录探测、80/443 监听者识别(nginx? frps? 其他?)。 ### 3.3 frps 管理 - **配置下发**:Fonu「frps.toml 同源生成」已有(含 vhost 端口、auth、webServer),改为推送给 agent 落盘:备份旧文件 → 原子替换 → 按运行方式生效(systemd 单元 `systemctl restart frps`;nohup 场景 `pkill frps` 后按原命令拉起,agent 从状态接口学习原启动命令); - **诊断**:`status` 聚合 frps 版本/存活/配置路径 + `ss -tlnp` 端口占用判定——把之前手工排查(`port unavailable`、80 被占)变成 API 一次调用; - **边界**:不安装/升级 frps 二进制(检测报告提示版本即可)。 ### 3.4 证书 - 签发仍在 **Fonu 证书页**(lego DNS-01,复用 DDNS 凭证,NAS 侧完成,VPS 无需参与); - 部署:证书页「部署到服务器」→ `POST /certs` 推送 PEM → agent 落盘并生成 443 vhost → reload; - 续期:Fonu 定时任务续期成功后自动重推,VPS 侧无感; - (可选演进)agent 本地 ACME HTTP-01:agent 已在 80 上,可自助签发,作为无 DNS 凭证场景的备选,本期不做。 ## 4. 通信协议与安全 | 项 | 设计 | | --- | --- | | 鉴权 | Bearer Token(Fonu 服务端表单生成/粘贴,agent 配置文件持有;两端一致即可用),`7700` 建议用防火墙限制仅 NAS IP 访问 | | 传输 | 明文 HTTP 起步可接受(Token + 内网限制);增强路径:agent 用 frps 证书或自签证书提供 HTTPS 管理口 | | 命令安全 | agent 只执行固定动作(写文件/nginx -t/reload/重启 frps),**不执行 Fonu 下发的任意命令**;域名/路径参数做白名单校验(域名格式、路径锁定 `conf.d/fonu-*` 与 `/var/lib/fonu-agent/`) | | 幂等 | 路由/配置/证书接口全部幂等,重复调用无副作用;`routes` 对账接口支持 Fonu 定时校准 | | 审计 | agent 每个写操作记本地日志(who/when/what),Fonu 侧同步状态入系统日志 | ## 5. Fonu 侧改动(frpc 端) | 模块 | 内容 | | --- | --- | | 数据模型 | `frp_servers` 加 `agent_url`、`agent_token_enc`(secret.Box 加密不回显)、`mgmt_enabled`(默认 0,行为与 v1.1.0 完全一致);迁移 `023_frp_server_agent.sql` | | 规则联动 | 规则保存/删除/启停且 `mgmt_enabled` 时,自动调 agent 路由接口(http/https 类型域名规则);失败不阻塞保存,规则行显示同步状态(已同步/待同步/失败+原因) | | frps.toml | 「下发到服务器」按钮替代「复制到 VPS」;一键含重启与验证 | | 证书页 | 「部署到服务器」:选目标服务端 → 推送 PEM → 回显 agent 挂载结果;续期任务成功后自动重推 | | 管理弹窗 | 服务端「管理」按钮:status 报告(frps 状态/端口占用者/路由清单/证书有效期)+ 一键操作(下发配置/全量对账) | | API 客户端 | `internal/frp/agent_client.go`:Go HTTP 客户端封装上述 7 个接口(超时/重试/错误透传) | ## 6. agent 部署与生命周期管理(SSH,全程在 Fonu 界面完成) ### 6.1 首次安装(用户不登 VPS) 1. Fonu 服务端表单「远程管理」分组填 SSH 信息(地址/端口/用户/密码或私钥,可选保存)+ 点「安装 agent」; 2. Fonu 经单次 SSH 会话自动完成: - 探测环境:`uname -m`(选二进制)、nginx 是否安装、`systemd` 可用性、端口占用快照; - SFTP 上传 `fonu-agent` 二进制与 `install.sh`(从 Fonu 自带资源解出对应架构); - 远程执行 `install.sh`:落位 `/usr/local/bin/fonu-agent`、写 `/etc/fonu-agent/agent.toml`(**生成随机 Token 并回传给 Fonu**,无需用户抄写)、注册 `systemd` 单元并启动; - 安装后立刻走 API `ping` 验证 + 防火墙提示(如 7700 未放行给出命令); 3. UI 展示每步输出,失败可重试(幂等,重复安装=覆盖升级)。 ### 6.2 升级 / 卸载 / 自救 | 动作 | 方式 | 说明 | | --- | --- | --- | | 升级 agent | SSH:上传新二进制 + `systemctl restart fonu-agent` | 路由/证书/Token 均在磁盘,不丢 | | 卸载 agent | SSH:`install.sh --uninstall`(停服务、可选保留数据) | nginx vhost 文件一并清理(可选项) | | agent 失联 | API 不可达时,Fonu 管理弹窗提示「走 SSH 通道诊断」:进程状态/端口/日志尾部 | SSH 是 API 故障时的备用通道 | ### 6.3 日常管理(API,面板在 Fonu) 安装完成后的一切操作不再用 SSH:规则联动同步、frps.toml 下发、证书部署、status 查询、全量对账——全部是 Fonu 界面按钮/自动触发,走 agent API。VPS 上没有任何管理面板,用户日常甚至不需要记住 VPS 密码。 ## 6.5 Fonu 侧新增「SSH 安装器」(与 agent 客户端并列) | 模块 | 内容 | | --- | --- | | `internal/frp/ssh_installer.go`(新) | SSH 连接(x/crypto/ssh 已在 go.mod)、SFTP 上传二进制/install.sh、远程幂等安装流程、Token 回传 | | agent 二进制内嵌 | 构建期把 `fonu-agent` 各架构二进制 embed 进 Fonu(构建矩阵:linux-amd64/arm64),安装时按 `uname -m` 选择,无需用户单独下载 | | 数据模型 | 复用 023 迁移:`ssh_host/port/user/auth/secret_enc` 可选保存(仅启用「安装/升级/卸载」功能时需要),agent API Token 由安装器自动生成并写入 Fonu | | UI | 服务端表单「远程管理」:SSH 信息(可选)+「安装 agent / 升级 / 卸载 / 走 SSH 诊断」按钮,安装过程实时输出展示 | > SSH 信息**可选保存**:仅安装/升级时需要;日常管理走 API Token。安全上 SSH 凭据同 token 机制(secret.Box 加密、不回显),远程命令固定白名单模板(上传/安装/卸载三种),无注入面。 ## 7. 实现拆解与工作量 | 模块 | 位置 | 内容 | 量级 | | --- | --- | --- | --- | | agent 主体 | `cmd/fonu-agent/` + `internal/agent/`(新) | HTTP 服务、鉴权、路由/frps/证书三个执行器、nginx 模板与 reload、状态聚合 | 大(约 1200 行) | | Fonu SSH 安装器 | `internal/frp/ssh_installer.go`(新) | SSH/SFTP 上传、远程幂等安装/升级/卸载、agent 二进制 embed、Token 自动回传 | 中 | | Fonu agent 客户端 | `internal/frp/agent_client.go`(新) | 7 个接口封装 | 中 | | 数据模型/迁移 | `023_frp_server_agent.sql` | SSH + agent 字段 + 规则同步状态 | 小 | | 规则联动 | `internal/frp/service.go` | 保存/删除/启停后同步调用 + 状态落库 | 中 | | API/UI | `frpmulti_handler.go`、`router.go`、`FrpView.vue`、`client.ts` | 5 个端点 + 表单分组 + 管理弹窗 + 同步状态列 | 中 | | 测试 | `agent_test.go` 等 | nginx 模板渲染/回滚、接口幂等、鉴权 | 中 | 总计约 2500–3000 行(含测试)。`mgmt_enabled=0` 时 Fonu 行为与 v1.1.0 完全一致;agent 未部署的服务端不受任何影响。 ## 8. 里程碑 | 阶段 | 内容 | 验收 | | --- | --- | --- | | M1 | agent 主体(ping/status/routes)+ Fonu SSH 安装器(一键安装) | Fonu 填 SSH 信息点「安装」后自动装好 agent 并回连成功,status 能报端口占用者 | | M2 | Fonu 规则联动 + 路由自动注册 + frps 配置下发 | 保存 http 规则后 `http://域名` 无端口直达;frps.toml 一键下发生效 | | M3 | 证书推送 + 续期联动 + 443 路由 | `https://域名` 可用,续期自动重推 | | M4 | 管理弹窗 UI + 全量对账 + 升级/卸载/SSH 诊断 + 手册更新 | 手册第 6 节新增方案 C(agent 模式:SSH 装、API 管) | ## 9. 明确不做(本期) - 不做 agent 侧自主 ACME(DNS-01 在 NAS 侧签发已覆盖;HTTP-01 作为后续可选); - 不做 frps 二进制远程安装/升级(status 报告版本,安装沿用现有手册); - 不做 agent 集群/多机编排(一个服务端一个 agent,模型对齐现有 frp_servers); - 不动 frp 二进制与协议(agent 只操作 frps 的配置文件、进程与 vhost 端口)。 ## 10. 方案形态对照 | | v2 最终(agent + SSH 安装器) | v1(纯 SSH 推送) | | --- | --- | --- | | VPS 侧 | 常驻 agent,但**安装/升级由 Fonu SSH 一键完成** | 零安装 | | 日常管理 | API(面板全在 Fonu,日常无需 SSH) | 每次操作都走 SSH 命令 | | 能力上限 | 高:本地编排 nginx/frps/证书,可自愈可扩展 | 受限于 SSH 命令白名单 | | 与已有站点共存 | 只写 `fonu-*.conf`,共存 | 可实现但每步都要远程拼配置 | | 故障定位 | status API 一次拿全;agent 失联时 SSH 急救 | 每次探测拼 SSH 命令 | | 适用 | VPS 长期做穿透入口、多域名/HTTPS 全自动 | 不想在 VPS 装常驻组件 | 结论:**SSH 与 API 不是二选一**——SSH 只承担 agent 生命周期(装/升/卸/急救),API 承担日常管理;管理面板 100% 在 Fonu 本地,VPS 上没有任何管理界面。