chore: 初始提交 Fonu 1.0.2
- 公网反代:Nginx 状态卡片(版本 / 运行状态 / 配置校验 / 端口监听) - 部署设置第一、二梯队(HTTPS 开关、安全头、限流、连接数、仅中国大陆 IP) - 隧道检测兼容 frps v0.68 / v0.71(V2 与旧版端点) - 恢复被清空的 internal/frp/store.go、internal/api/ddns_handler.go、web/src/views/DdnsView.vue
@@ -0,0 +1,305 @@
|
||||
# Fonu 部署安装文档
|
||||
|
||||
> 适用版本:v1.1.0(源码包 `fonu-1.1.0-src.zip`)。本文覆盖从拿到安装包到服务上线的完整流程:环境要求 → 打包 → 三种部署方式 → 首次配置 → FRP/DDNS/证书联动 → 升级与回滚 → 故障排查。
|
||||
> 功能细节与组合场景请配合项目内两份文档:`README.md`(功能总览)与 FRP 页面右上「使用说明」(内置十个组合场景详解)。
|
||||
|
||||
## 0. 交付物清单与安装路径速查
|
||||
|
||||
| 交付物 | 内容 | 适用 | 安装方式 |
|
||||
| --- | --- | --- | --- |
|
||||
| `fonu-1.1.0-linux-amd64.tar.gz`(14MB) | 编译好的 Linux 二进制(前端已内嵌)+ migrations + systemd + 一键脚本 | **裸机 Linux 服务器**,目标机只需系统 nginx | 解压 → `sudo bash install.sh`(见 3.4) |
|
||||
| `fonu-1.1.0-image.tar.gz`(约 100-150MB) | 构建好的 Docker 镜像 `fonu:local`(含 nginx + frpc) | 目标机有 Docker、要离线安装 | `docker load` → `docker run`(见 3.3 方式 C) |
|
||||
| `fonu-1.1.0-src.zip`(11MB) | 完整源码 + Dockerfile/compose + 部署文档 | 要自行构建 / 二次开发 / 交叉编译 | `docker compose build` 后按方式 A/B 部署(见第 2 节) |
|
||||
|
||||
**怎么选**:飞牛 fnOS 首选镜像包导入(图形界面,见第 3 节末「飞牛 fnOS 部署详解」),要在 NAS 上构建则用源码包 + host 模式 compose;普通 Linux 服务器要省事用 Linux 运行包(免 Docker);目标机不方便联网构建用镜像包离线导入。三者的功能完全一致——容器里跑的也是同一份二进制 + 系统 nginx。
|
||||
|
||||
## 1. 包内容与环境要求
|
||||
|
||||
### 1.1 源码包内容
|
||||
|
||||
`fonu-1.1.0-src.zip`(约 11MB,304 项)为完整可构建源码:
|
||||
|
||||
| 目录/文件 | 说明 |
|
||||
| --- | --- |
|
||||
| `cmd/` `internal/` | Go 后端源码(含已迁移的多服务端 FRP 模块) |
|
||||
| `web/` | Vue 3 + TypeScript 前端源码 |
|
||||
| `migrations/` | SQLite 数据库迁移 001–022(启动时自动按序应用) |
|
||||
| `Dockerfile` / `docker-compose*.yml` | 本地构建镜像与三种部署编排 |
|
||||
| `VERSION` / `CHANGELOG.md` | 版本号单一来源(当前 `1.1.0`)与版本记录 |
|
||||
| `deploy/nginx/mime.types` | 容器内 Nginx 依赖的 MIME 类型文件 |
|
||||
| `docs/screenshots/` | README 引用的界面截图 |
|
||||
|
||||
**已排除**(打包时即剔除,无需自行处理):`.data/`(本地运行数据)、`web/node_modules`(构建时自动安装)、`internal/frp-1.0.2/` 与 `FrpView-1.0.2.vue`(弃用归档)、`*.db`。
|
||||
|
||||
### 1.2 部署机要求
|
||||
|
||||
| 项 | 要求 |
|
||||
| --- | --- |
|
||||
| 系统 | 飞牛 fnOS(主要目标平台);其他 Linux x86_64 / arm64 理论可用,未充分测试 |
|
||||
| Docker | 20.10+(含 compose v2) |
|
||||
| 内存 | ≥ 512MB 空闲(构建期建议 ≥ 2GB,前端编译吃内存) |
|
||||
| 磁盘 | ≥ 2GB(镜像约 300MB + 数据目录按日志量增长) |
|
||||
| 网络 | 构建期需访问 npm registry、Go module proxy、GitHub(下载 frpc 0.71.0);运行期需访问你的 DNS 服务商 API(DDNS/证书) |
|
||||
| 端口 | 管理端口 6893;反代端口默认 80/443(Bridge)或 18080/9443(飞牛 host 模式) |
|
||||
|
||||
## 2. 构建镜像(所有部署方式的前提)
|
||||
|
||||
解压源码包后进入项目根目录执行:
|
||||
|
||||
```bash
|
||||
docker compose build
|
||||
```
|
||||
|
||||
一次构建同时产出:前端 `dist`、Go 后端二进制、内置 frpc 0.71.0 的运行镜像,标签为 `fonu:local`。
|
||||
|
||||
**版本号**:读取根目录 `VERSION` 文件(当前 `1.1.0`)自动注入前后端显示;要改版本号只需编辑该文件后重新 build。
|
||||
|
||||
**网络受限环境**可覆盖基础镜像与 frpc 下载源(默认值见 `Dockerfile` 顶部注释):
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg NODE_IMAGE=docker.m.daocloud.io/library/node:22-alpine \
|
||||
--build-arg GO_IMAGE=docker.m.daocloud.io/library/golang:1.23-bookworm \
|
||||
--build-arg RUNTIME_IMAGE=docker.m.daocloud.io/library/debian:bookworm-slim \
|
||||
-t fonu:local .
|
||||
```
|
||||
|
||||
## 3. 三种部署方式(按环境二选一)
|
||||
|
||||
### 方式 A:飞牛 fnOS / 已占用 80/443 的环境 —— host 模式(推荐)
|
||||
|
||||
飞牛系统自带 Nginx 占用 80/443,Fonu 使用 host 网络 + 高位反代端口避免冲突。
|
||||
|
||||
```bash
|
||||
FONU_SESSION_SECRET=请改成一串随机字符 docker compose -f docker-compose.host.yml up -d --build
|
||||
```
|
||||
|
||||
- 无端口映射,容器直接使用宿主机网络栈;
|
||||
- 反代端口由环境变量 `FONU_NGINX_HTTP_PORT=18080`、`FONU_NGINX_HTTPS_PORT=9443` 决定(文件内已内置);
|
||||
- 入口:管理后台 `http://<NAS-IP>:6893`,HTTP 反代 `http://<NAS-IP>:18080`,HTTPS 反代 `https://<NAS-IP>:9443`;
|
||||
- 数据持久化在命名卷 `fonu-data`(挂载到容器 `/data`)。
|
||||
|
||||
### 方式 B:其他环境 / 空闲 80/443 —— Bridge 端口映射
|
||||
|
||||
```bash
|
||||
FONU_SESSION_SECRET=请改成一串随机字符 docker compose -f docker-compose.hub.yml up -d --build
|
||||
```
|
||||
|
||||
- 端口映射 `6893:6893`、`18080:80`、`9443:443`(宿主机 18080/9443 → 容器 80/443);
|
||||
- 数据同样在命名卷 `fonu-data`。
|
||||
|
||||
### 方式 C:docker run(不想用 compose)
|
||||
|
||||
```bash
|
||||
docker run -d --name fonu \
|
||||
--net=host \
|
||||
-v ./data:/data \
|
||||
-e FONU_SESSION_SECRET=请改成一串随机字符 \
|
||||
--restart unless-stopped \
|
||||
fonu:local
|
||||
```
|
||||
|
||||
host 模式下反代端口用环境变量控制:`-e FONU_NGINX_HTTP_PORT=18080 -e FONU_NGINX_HTTPS_PORT=9443`。Bridge 环境改为 `-p 6893:6893 -p 18080:80 -p 9443:443`(去掉 `--net=host`)。
|
||||
|
||||
**生成随机 SESSION_SECRET**:`openssl rand -hex 32`(Linux)或 PowerShell:`-join ((48..57)+(97..122) | Get-Random -Count 32 | % {[char]$_})`。
|
||||
|
||||
> **`FONU_SESSION_SECRET` 是什么**:Fonu 的主密钥,两个用途——① 签名/加密登录会话 Cookie(决定登录态是否可被伪造);② 作为 `secret.Box` 加密存储中敏感凭据(DDNS API Token、FRP Token、SMTP 密码等)。默认值 `change-me-in-production` 是公开的:拿到它即可伪造管理员会话、解密数据库里的全部凭据,**生产环境必须替换**。⚠️ 换密钥的时机:首次启动前设置;若之后更换,旧密钥加密的凭据将无法解密(应用会提示重新保存),需到对应页面重录 Token。
|
||||
|
||||
### Linux 服务器完整部署步骤(Ubuntu / Debian / CentOS)
|
||||
|
||||
```bash
|
||||
# 0. 安装 Docker(已装跳过)
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
sudo systemctl enable --now docker
|
||||
|
||||
# 1. 解压源码包并进入目录
|
||||
unzip fonu-1.1.0-src.zip -d fonu && cd fonu
|
||||
|
||||
# 2. 构建镜像(约 3-10 分钟,取决于网络与机器性能)
|
||||
docker compose build
|
||||
|
||||
# 3. 生成密钥并启动(Bridge 模式,映射 6893/18080/9443)
|
||||
echo "FONU_SESSION_SECRET=$(openssl rand -hex 32)" > .env
|
||||
docker compose -f docker-compose.hub.yml up -d
|
||||
|
||||
# 4. 防火墙放行(按需)
|
||||
sudo ufw allow 6893/tcp # 管理后台
|
||||
sudo ufw allow 80/tcp 443/tcp # 反代(Bridge 模式容器内监听 80/443)
|
||||
# firewalld 用: firewall-cmd --permanent --add-port=6893/tcp && firewall-cmd --reload
|
||||
|
||||
# 5. 验证
|
||||
docker logs fonu | grep -i password # 取 admin 初始密码
|
||||
curl -I http://127.0.0.1:6893/login # 应返回 200
|
||||
```
|
||||
|
||||
- **开机自启**:compose 内已含 `restart: unless-stopped`,且第 0 步已 `enable docker`,重启服务器自动拉起;
|
||||
- **80/443 被系统 Nginx 占用**:改用 `docker-compose.host.yml`(host 网络 + 18080/9443 高位端口),或改 `docker-compose.hub.yml` 里的宿主端口映射;
|
||||
- **离线部署**(目标机不方便联网构建):在有网的机器上 `docker compose build` 后 `docker save fonu:local | gzip > fonu.tar.gz`,拷贝到目标机 `docker load < fonu.tar.gz`,再用方式 C 的 `docker run` 启动(无需再构建);
|
||||
- **数据位置**:命名卷 `fonu-data`(`docker volume inspect fonu-data` 查看宿主机实际路径),备份该卷即备份全部配置与证书。
|
||||
|
||||
### Linux 运行包安装(免 Docker,裸机部署)
|
||||
|
||||
适用:不想装 Docker 的 Linux 服务器。前提:系统已装 nginx(Fonu 复用它做反代),root 权限。
|
||||
|
||||
```bash
|
||||
# 1. 解压
|
||||
tar xzf fonu-1.1.0-linux-amd64.tar.gz -C fonu && cd fonu
|
||||
|
||||
# 2. 一键安装(自动建 fonu 系统用户、装到 /opt/fonu、生成随机密钥、注册 systemd 开机自启)
|
||||
sudo bash install.sh
|
||||
|
||||
# 3. 验证
|
||||
systemctl status fonu
|
||||
curl -I http://127.0.0.1:6893/login
|
||||
journalctl -u fonu | grep -i password # 取 admin 初始密码
|
||||
```
|
||||
|
||||
包内容:`fonu` 二进制(前端已 `go:embed` 内嵌,无需单独部署 dist)、`migrations/`(从二进制同级目录自动查找)、`deploy/nginx/mime.types`、`fonu.service`(systemd 模板)、`install.sh`、`DEPLOYMENT.md`。
|
||||
|
||||
关键路径(install.sh 自动配置):
|
||||
|
||||
| 项 | 值 |
|
||||
| --- | --- |
|
||||
| 二进制 | `/opt/fonu/fonu` |
|
||||
| 数据目录 | `/var/lib/fonu`(数据库、证书、日志全在这) |
|
||||
| 会话密钥 | `/etc/default/fonu`(重复安装保留原值) |
|
||||
| 服务 | `systemctl restart fonu` 生效配置改动 |
|
||||
|
||||
**备份/迁移**:停服务后打包 `/var/lib/fonu` 即可(`tar czf fonu-data.tar.gz /var/lib/fonu`),新机器解压到同路径 + 装同版本二进制即完成迁移。
|
||||
|
||||
**手动安装**(不用脚本):按包内 `README.txt` 要点——建 `fonu` 系统用户、复制二进制与 migrations 到 `/opt/fonu`、`EnvironmentFile` 写入随机密钥、`systemctl enable --now fonu`。
|
||||
|
||||
> 镜像包(`fonu-1.1.0-image.tar.gz`)来源:在任意装了 Docker 的机器上对源码包执行 `docker compose build` 后 `docker save fonu:local | gzip > fonu-1.1.0-image.tar.gz`;目标机 `docker load < fonu-1.1.0-image.tar.gz` 后按方式 C 启动。
|
||||
|
||||
### 飞牛 fnOS 部署详解(图形界面,不敲命令)
|
||||
|
||||
前置:fnOS 应用中心安装 Docker 应用(较新版本自带)。以下菜单名以 fnOS 当前版本实际界面为准,大致路径一致。
|
||||
|
||||
**方式一:导入现成镜像(推荐,5 分钟)**
|
||||
|
||||
1. **上传文件**:把 `fonu-1.1.0-image.tar.gz` 通过文件管理 / SMB 上传到 NAS,如共享目录 `docker/fonu/`;
|
||||
2. **导入镜像**:打开 Docker 应用 → 镜像 → 导入(从文件加载)→ 选择该 tar.gz → 等待镜像列表出现 `fonu:local`(若界面只接受 .tar,先在电脑上解压出内层的 .tar 再导入);
|
||||
3. **创建容器**:容器 → 创建:
|
||||
|
||||
| 配置项 | 填写 |
|
||||
| --- | --- |
|
||||
| 名称 | `fonu` |
|
||||
| 镜像 | `fonu:local` |
|
||||
| 网络 | **host**(飞牛 80/443 被系统占用,host + 高位端口最省事;UI 不支持 host 时选 Bridge 并映射 6893/18080/9443) |
|
||||
| 存储挂载 | NAS 建目录(如 `/vol1/…/docker/fonu/data`)→ 映射到容器 `/data`(读写) |
|
||||
| 环境变量 | `FONU_SESSION_SECRET=随机串`、`FONU_DATA_DIR=/data`、`FONU_LISTEN=:6893`、`FONU_NGINX_HTTP_PORT=18080`、`FONU_NGINX_HTTPS_PORT=9443` |
|
||||
| 重启策略 | 自动重启 / unless-stopped(开机自启) |
|
||||
|
||||
4. **启动并取初始密码**:容器详情 → 日志,搜 `password`;浏览器打开 `http://<NAS-IP>:6893` 登录。
|
||||
|
||||
**方式二:Compose 项目(用源码包,在 NAS 上构建)**
|
||||
|
||||
1. 把源码包 zip 上传到 NAS 并解压(文件管理支持解压,或电脑解压后整目录上传);
|
||||
2. Docker 应用 → 项目(Compose)→ 新建:指定项目目录为解压后的源码目录、使用 `docker-compose.host.yml`(或直接把该文件内容粘贴到 Compose 编辑器);
|
||||
3. 在源码目录同层放一个 `.env` 文件,内容一行:`FONU_SESSION_SECRET=随机串`;
|
||||
4. 构建并启动。⚠️ NAS 上构建耗时较长(前端编译),低配机器建议改用方式一;
|
||||
5. 后续升级:上传新源码覆盖 → 项目重新构建即可,数据在卷里不受影响。
|
||||
|
||||
**Compose 构建报 401(docker.fnnas.com)**:fnOS 的 Docker 设置里默认配了自家加速地址,但它对 `library/` 官方镜像拒绝匿名拉取。三种解法任选:
|
||||
|
||||
1. **改用镜像包(最省事)**:跳过构建,按方式一导入 `fonu-1.1.0-image.tar.gz`;
|
||||
2. **覆盖基础镜像源**:在源码目录的 `.env` 里加三行(已有 FONU_SESSION_SECRET 就追加在下面):
|
||||
```
|
||||
NODE_IMAGE=docker.m.daocloud.io/library/node:22-alpine
|
||||
GO_IMAGE=docker.m.daocloud.io/library/golang:1.23-bookworm
|
||||
RUNTIME_IMAGE=docker.m.daocloud.io/library/debian:bookworm-slim
|
||||
GOPROXY=https://goproxy.cn,direct
|
||||
```
|
||||
|
||||
(Dockerfile/compose 现已内置这些默认值,无需 .env;此处列出供手动 docker build 参考或覆盖。`GOPROXY` 解决 go mod download 访问 proxy.golang.org 超时——国内默认 goproxy.cn,海外可覆盖回 direct。)
|
||||
然后 Compose 重新构建——compose 文件已支持这三个变量透传;
|
||||
3. **换/关掉 fnOS 加速地址**:Docker 应用 → 设置 → 镜像源,把 `docker.fnnas.com` 换成可用加速源(如 `docker.m.daocloud.io`)或清空,再重新构建。
|
||||
|
||||
**fnOS 注意事项**:
|
||||
|
||||
- 80/443 已被 fnOS 系统 Nginx 占用——**不要**把反代规则监听 80/443,用默认 18080/9443;
|
||||
- host 模式下容器端口即宿主机端口,fnOS 防火墙(如有启用)放行 6893 / 18080 / 9443;
|
||||
- 数据目录建议放在存储池挂载路径下(`/vol1/…`,以文件管理器显示的实际路径为准),系统重装时数据不随系统分区丢失;
|
||||
- 升级:方式一重新导入新镜像 → 停止并删除旧容器 → 用相同挂载/环境变量重建(数据在映射目录,不丢);方式二直接重新构建。
|
||||
|
||||
## 4. 首次登录与安全配置
|
||||
|
||||
1. 启动后查看初始密码:`docker logs fonu | grep -i password`(自动生成的 admin 初始密码会打印在日志里);
|
||||
2. 浏览器打开 `http://<NAS-IP>:6893/login`,用 `admin` + 初始密码登录;
|
||||
3. **立即**在「设置 → 安全」修改管理员密码;
|
||||
4. 确认「设置 → 常规」中 `FONU_SESSION_SECRET` 已改为非默认值(compose 文件中已传入则跳过);
|
||||
5. 建议在「设置 → 通知」配置一种告警方式(邮件 / Webhook / Telegram),DDNS 失败、证书续签失败、登录异常等事件会推送。
|
||||
|
||||
## 5. 反向代理与 HTTPS 证书
|
||||
|
||||
1. **反向代理页 → 添加规则**:填前端域名(如 `app.example.com`)、后端目标(内网应用地址)、HTTPS 开关与证书;
|
||||
2. **证书页 → 申请证书**:ACME DNS-01 验证,需先在 DDNS 页配置对应服务商凭证(Cloudflare / DNSPod / 阿里云 / 腾讯云 / 火山引擎),通配符证书 `*.example.com` 可覆盖整组域名;
|
||||
3. DNS 记录指向本机 IP:家庭宽带场景由 DDNS 自动跟随(见第 6 节);
|
||||
4. 验证:浏览器访问域名,证书页看剩余有效期,日志中心看访问/错误日志。
|
||||
|
||||
## 6. DDNS(域名解析自动跟随)
|
||||
|
||||
DDNS 页 → 添加任务:选服务商 → 填 API 凭证(可「测试连接」)→ 添加解析记录(支持 `@`、子域、完整域名)→ 开 IPv4/IPv6 → 保存。
|
||||
|
||||
**IP 来源两种模式**(任务详情 → 「自定义 IP」按钮设置):
|
||||
|
||||
| 模式 | 填法 | 适用 |
|
||||
| --- | --- | --- |
|
||||
| 自动检测(默认) | 自定义 IP 留空 | 域名指向家庭宽带出口 IP |
|
||||
| 自定义 IP | 填固定公网 IP(如 VPS 的 IP) | 域名直接指向公网服务器 |
|
||||
|
||||
自定义 IP 必须为公网地址,填内网 IP 会被拒绝更新。VPS 动态 IP 场景:自定义 IP 填 VPS 当前 IP,FRP 服务端地址填 DDNS 域名,VPS 换 IP 后自动跟随。
|
||||
|
||||
## 7. FRP 内网穿透(无公网 IP 时)
|
||||
|
||||
前置:一台有公网 IP 的 VPS。
|
||||
|
||||
1. **Fonu 添加服务端**:FRP 穿透页 → 添加服务端 → 填 VPS 地址、bindPort(默认 7000)、认证方式与 Token(与 frps 一致);
|
||||
2. **生成 frps 配置**:服务端「详情」→「frps.toml」→ 复制生成内容到 VPS 保存为 `frps.toml`,执行 `frps -c frps.toml`;按文件末尾的防火墙清单在 VPS 安全组放行端口;
|
||||
3. **添加穿透规则**:8 种类型按需选——tcp/udp 填远程端口;http/https/tcpmux 填自定义域名(走 frps vhost 端口);stcp/sudp/xtcp 凭 secretKey(无公网监听);
|
||||
4. **应用并验证**:「应用配置」→「校验并应用配置」;服务端「详情」看五项诊断(TCP 可达 / frpc 登录 / 代理可达 / DNS 解析 / 归属地)与独立日志。
|
||||
|
||||
## 8. 升级与回滚
|
||||
|
||||
### 升级
|
||||
|
||||
```bash
|
||||
# 1. 备份数据(最重要)
|
||||
docker run --rm -v fonu-data:/data -v ${PWD}:/backup alpine tar czf /backup/fonu-data-backup.tar.gz /data
|
||||
# 2. 解压新版源码包覆盖项目目录(VERSION 与 migrations 会更新)
|
||||
# 3. 重建并启动(迁移 015–022 会在启动时自动应用)
|
||||
docker compose -f docker-compose.host.yml up -d --build
|
||||
# 4. 看启动日志确认迁移与版本
|
||||
docker logs -f fonu
|
||||
```
|
||||
|
||||
### 回滚
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
# 还原上一版源码目录后:
|
||||
docker compose -f docker-compose.host.yml up -d --build
|
||||
# 数据目录如需还原:解压第 1 步的备份 tar 到挂载卷
|
||||
```
|
||||
|
||||
> 注意:数据库迁移只向前应用(`schema_migrations` 记录版本号)。跨大版本回滚前必须先做第 1 步的数据备份,否则旧版代码遇到新表结构可能启动异常。
|
||||
|
||||
## 9. 故障排查速查
|
||||
|
||||
| 现象 | 处理 |
|
||||
| --- | --- |
|
||||
| 容器起来但 6893 打不开 | `docker logs fonu` 看启动报错;`docker port fonu`(Bridge 模式)确认映射 |
|
||||
| 提示「会话加密密钥」警告 | 未设置 `FONU_SESSION_SECRET`,compose 传入即可 |
|
||||
| 反代 502 | 目标内网应用未启动或地址填错;日志中心看 Nginx error 日志 |
|
||||
| 证书申请失败 | DDNS 凭证是否有效、域名 DNS 是否已生效、ACME 邮箱是否已设置 |
|
||||
| frpc 连不上 frps | 两端 Token/端口不一致;用服务端「详情 → frps.toml」重新生成对齐 |
|
||||
| 穿透流量显示 0 | frps 未配置 webServer 或 Fonu 服务端表单未填管理地址(非必须功能) |
|
||||
| 页面改动未生效 | 确认访问的是容器端口而非开发服务器;`docker compose up -d --build` 重建 |
|
||||
|
||||
## 10. 卸载
|
||||
|
||||
```bash
|
||||
docker compose down # 停止并删除容器(数据卷保留)
|
||||
docker volume rm fonu-data # 彻底删除数据(不可逆,先备份)
|
||||
docker rmi fonu:local # 删除镜像
|
||||
```
|
||||
@@ -0,0 +1,170 @@
|
||||
# 内网应用经 FRP 暴露公网:NAS 侧完整操作手册
|
||||
|
||||
> 场景:把 NAS 内网应用(本例 `192.168.31.152:7023`)通过一台公网 VPS(`38.95.74.87`)以域名(`tv.36800.cc`)暴露出去,URL 不带端口号,且后续域名 / HTTPS / 访问控制全部在 NAS 的 Fonu 网页里管理,VPS 只做纯流量转发。
|
||||
> 适用版本:Fonu v1.1.0+(FRP 多服务端模块)。VPS 上 **不需要也不应该** 装 Nginx 反代。
|
||||
|
||||
## 0. 最终架构与访问地址
|
||||
|
||||
```text
|
||||
用户浏览器
|
||||
↓ http://tv.36800.cc(80 端口,URL 不带端口)
|
||||
VPS frps(监听 80 —— 由 frpc 动态注册的 tcp 隧道,VPS 上无需 Nginx)
|
||||
↓ FRP 隧道(bindPort 7000,TLS)
|
||||
NAS frpc
|
||||
↓ 127.0.0.1:18080
|
||||
Fonu Nginx(按域名分流,后续可加 HTTPS/黑白名单)
|
||||
↓ http://192.168.31.152:7023
|
||||
内网应用
|
||||
```
|
||||
|
||||
**访问地址:`http://tv.36800.cc`**
|
||||
|
||||
| 角色 | 地址 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 公网 VPS | `38.95.74.87`(tv.36800.cc) | 只跑 frps,Nginx 停用 |
|
||||
| NAS 内网应用 | `192.168.31.152:7023` | 真正的 Web 服务 |
|
||||
| Fonu 管理后台 | `http://<NAS-IP>:6893` | 全部配置入口 |
|
||||
|
||||
## 1. 前置条件
|
||||
|
||||
- VPS:已部署 frps(bindPort 7000、token 认证;参考 Fonu 服务端详情「frps.toml」一键生成);
|
||||
- VPS:frps 的 Nginx **未**占用 80(占用见第 5 节排障);
|
||||
- 云厂商安全组已放行:7000/tcp(frps 控制)、80/tcp(对外)、7500/tcp(frps 管理接口,可选,用于流量统计);
|
||||
- NAS:Fonu 已部署并能打开管理后台;
|
||||
- 域名:`tv.36800.cc` 的 DNS A 记录指向 VPS IP(可用 Fonu DDNS 页「自定义 IP」固定,或由 DNS 服务商直接管理)。
|
||||
|
||||
## 2. Fonu 网页配置(三处)
|
||||
|
||||
### 2.1 FRP 服务端
|
||||
|
||||
路径:**FRP 穿透 → 添加服务端**
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| 主机地址 | VPS 的 IP 或域名 |
|
||||
| 端口 | 7000(= frps bindPort) |
|
||||
| 认证方式 | Token,与 VPS frps.toml 的 `auth.token` 一致 |
|
||||
| TLS | 开启(frps 默认接受) |
|
||||
| frps 管理接口(可选) | 填 frps `webServer` 的地址/端口/用户/密码,用于显示穿透流量 |
|
||||
|
||||
保存后状态应显示「运行中 · 已连接」。
|
||||
|
||||
### 2.2 穿透规则(核心)
|
||||
|
||||
路径:**FRP 穿透 → 添加规则**
|
||||
|
||||
| 字段 | 值 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 所属服务端 | 上一步的服务端 | |
|
||||
| 名称 | `tv`(frps 全局唯一,出现在日志里) | |
|
||||
| 类型 | **tcp** | ⚠️ 不选 http——80 直接由 frps 转发,域名分流交给 Fonu 反代 |
|
||||
| 远程端口 | **80** | 选 tcp 类型后出现此字段;frpc 登录时向 frps 动态注册 |
|
||||
| 内网目标 | **127.0.0.1:18080** | 指向 Fonu 自己的 Nginx HTTP 监听(fnOS host 模式默认 18080) |
|
||||
|
||||
保存 → 右上 **「应用配置」→「校验并应用配置」**,或对该服务端「停止→启动」。
|
||||
|
||||
### 2.3 反向代理(域名分流)
|
||||
|
||||
路径:**反向代理 → 添加规则**
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| 前端域名 | `tv.36800.cc` |
|
||||
| 监听端口 | **18080**(与 2.2 的内网目标端口一致;fnOS 下不要用 80,系统 Nginx 已占用) |
|
||||
| 目标地址 | `http://192.168.31.152:7023`(⚠️ 指向真正的内网应用,完整 URL 带协议与端口) |
|
||||
| HTTPS | 按需;fnOS 下 Fonu Nginx 的 HTTPS 监听默认 9443 |
|
||||
|
||||
> ⚠️ **回环警告**:目标地址千万不要填 `http://tv.36800.cc:xxxx`——那会让请求从 NAS 出公网再回 NAS(NAS→VPS→NAS 绕圈)。反代目标是**内网应用本身**。
|
||||
> ⚠️ **「目标地址缺少主机名」报错**:目标地址格式必须完整 `http://IP:端口`(协议+主机+端口缺一不可,单斜杠 `http:/` 或中文冒号都会报 400)。
|
||||
|
||||
## 3. VPS 侧配置(一次性的两步)
|
||||
|
||||
### 3.1 frps.toml(Fonu 一键生成)
|
||||
|
||||
Fonu 服务端「详情」→「frps.toml」→ 复制内容到 VPS 保存为 `frps.toml`:
|
||||
|
||||
```toml
|
||||
bindAddr = "0.0.0.0"
|
||||
bindPort = 7000
|
||||
|
||||
auth.method = "token"
|
||||
auth.token = "<与 Fonu 一致>"
|
||||
|
||||
webServer.addr = "0.0.0.0" # 可选:Fonu 拉取穿透流量
|
||||
webServer.port = 7500
|
||||
webServer.user = "admin"
|
||||
webServer.password = "<自定义>"
|
||||
|
||||
log.to = "console"
|
||||
log.level = "info"
|
||||
log.maxDays = 3
|
||||
log.disablePrintColor = false
|
||||
```
|
||||
|
||||
> 注意:**这个文件里没有任何远程端口**。tcp 类型规则的远程端口(80)由 frpc 登录时动态注册,frps 只管 bindPort 和认证——规则改了也不用改这个文件。
|
||||
|
||||
启动:`./frps -c frps.toml`(建议配 systemd)。
|
||||
|
||||
### 3.2 释放 80 端口(关键,`port unavailable` 的唯一解法)
|
||||
|
||||
80 只能给一个进程。frps 要绑 80,VPS 上原有的 Nginx 必须停掉:
|
||||
|
||||
```bash
|
||||
# 确认 80 被谁占用
|
||||
ss -tlnp | grep ':80 '
|
||||
|
||||
# 停止并禁用 Nginx(宝塔面板安装的在宝塔「软件商店」里停)
|
||||
systemctl stop nginx
|
||||
systemctl disable nginx
|
||||
|
||||
# 确认已释放(应无输出)
|
||||
ss -tlnp | grep ':80 '
|
||||
```
|
||||
|
||||
frpc 约每 33 秒自动重试注册,释放后无需任何手动操作,注册成功后 frps 日志出现 proxy 启动记录、frpc 日志的 `start error: port unavailable` 消失。
|
||||
|
||||
> 如果 VPS 的 80/443 还要跑其他站点(Nginx 不能停),本方案不适用——改回「VPS Nginx 监听 80 → proxy_pass 127.0.0.1:8080(frps vhost)」的方案,FRP 规则用 http 类型,见第 6 节方案对比。
|
||||
|
||||
## 4. 验证清单
|
||||
|
||||
| 步骤 | 命令 / 操作 | 预期 |
|
||||
| --- | --- | --- |
|
||||
| VPS 80 已监听 | `ss -tlnp \| grep ':80 '` | 进程为 frps |
|
||||
| frpc 注册成功 | Fonu 服务端「日志」 | 无 `port unavailable`,有 proxy 启动记录 |
|
||||
| 隧道通 | Fonu 服务端「测速」/「详情」诊断 | TCP 可达、frpc 登录 ok、延迟正常 |
|
||||
| 分流通 | 服务端「详情」→ 诊断 `proxy_reachable` | ok |
|
||||
| 端到端 | 外网浏览器 `http://tv.36800.cc` | 直达内网应用页面 |
|
||||
|
||||
## 5. 故障排查(按链路顺序)
|
||||
|
||||
| 现象 | 断点 | 处理 |
|
||||
| --- | --- | --- |
|
||||
| `Connection refused`(外部 curl 80) | **VPS 侧**:frps 没绑上 80 | ① `ss -tlnp \| grep ':80 '` 看 80 被谁占(多为 Nginx 没停)→ 停用;② `systemctl status frps` 确认在跑且用的新配置(nohup 手动跑的必须 `pkill frps` 重启);③ 重启后等 frpc 自动重试 |
|
||||
| frpc 日志 `start error: port unavailable` | 同上 | VPS 80 被占;见 3.2 |
|
||||
| 诊断「frpc 登录」失败 | Token/端口不一致 | 两端 token 对齐;端口=frps bindPort |
|
||||
| 登录成功但 `proxy_reachable` 失败 | Fonu 反代 | 反代规则监听端口与 FRP 内网目标端口一致(18080);反代已启用 |
|
||||
| 打开域名 404 | 域名分流失败 | 反代规则的前端域名与访问域名完全一致;frps 无需 vhost(tcp 方案) |
|
||||
| 域名解析不对 | DNS | `nslookup tv.36800.cc` 应返回 VPS IP;用 DDNS 自定义 IP 固定 |
|
||||
| 流量统计全 0 | frps webServer 未配 | 非必须;要在 Fonu 看流量就配 webServer 并在服务端表单填管理接口 |
|
||||
|
||||
## 6. 两种方案对比(VPS 80 的归属)
|
||||
|
||||
| | 方案 A:NAS 侧全管(本文) | 方案 B:VPS Nginx 反代 |
|
||||
| --- | --- | --- |
|
||||
| FRP 规则类型 | tcp,远程端口 80 → 127.0.0.1:18080 | http 类型 + frps `vhostHTTPPort=8080` |
|
||||
| http 类型去端口 | —(tcp 方案直接占 80) | `vhostHTTPPort` 从 8080 改 **80**:URL 变 `http://域名`(无端口);前提同样是 VPS 停 Nginx 释放 80,改后 `pkill frps` 重启生效;NAS 侧规则无需动 |
|
||||
| VPS Nginx | 不需要(停用) | 必须:listen 80 → proxy_pass 127.0.0.1:8080(**必须带** `proxy_set_header Host $host`) |
|
||||
| 加新域名 | Fonu 反代加一条 + FRP 加一条 tcp 规则(远程端口仍 80,内网目标同 18080) | VPS 上加 server 块 |
|
||||
| 上 HTTPS | Fonu 证书页(DNS-01)+ 反代开 HTTPS + FRP 加 tcp 443→127.0.0.1:9443 | VPS 上装证书 |
|
||||
| 访问控制(IP/BasicAuth) | Fonu 反代页直接配 | VPS 上手写 |
|
||||
| 限制 | VPS 80/443 不能再跑别的站点 | VPS 可共存多站点 |
|
||||
| URL | `http://tv.36800.cc`(无端口) | `http://tv.36800.cc`(80)或带 vhost 端口 |
|
||||
|
||||
选型:VPS 专用做穿透入口 → 方案 A;VPS 80/443 另有他用 → 方案 B。两方案不混用(80 归属唯一)。
|
||||
|
||||
## 7. HTTPS 升级路径(方案 A 延伸)
|
||||
|
||||
1. **Fonu 证书页**:申请 `tv.36800.cc` 或 `*.36800.cc` 证书(ACME DNS-01,复用 DDNS 页服务商凭证);
|
||||
2. **反代规则**:开 HTTPS、绑定证书、(可选)HTTP 强制跳转;
|
||||
3. **FRP 加一条 tcp 规则**:远程端口 443 → 内网目标 `127.0.0.1:9443`(Fonu Nginx HTTPS 监听);
|
||||
4. 安全组放行 443;之后 `https://tv.36800.cc` 可用,TLS 全程在 NAS 侧终止。
|
||||
@@ -0,0 +1,163 @@
|
||||
# 公网反代(Agent 页「公网 Nginx 反代」区)功能增强规划
|
||||
|
||||
> 基于实际部署踩坑史(目录探测、证书缺失、端口漂移、类型错配均发生过)梳理,
|
||||
> 每项标注:价值 / 实现成本 / 依赖。供后续逐项开发选用。
|
||||
> 更新日期:2026-09-21
|
||||
|
||||
---
|
||||
|
||||
|
||||
第一梯队(最推荐先做,1-2 天)
|
||||
功能 解决什么
|
||||
① 部署后在线验证 部署成功后 agent 在 VPS 上 curl -H "Host: 域名" 127.0.0.1:9080,页面直接显示「回源自检 200 ✓」或「回源 502 ✗(隧道未通)」——你昨天遇到的“部署成功但打开 502”当时就能看见断在哪层
|
||||
③ 状态徽章实时化 「已部署」升级为「已生效 / 已写入未加载 / 未部署」三态(nginx -T 真实验证),tcp/ 目录那种“写了但不加载”的残留一眼可见
|
||||
⑥ WebSocket 开关 家庭影院等实时服务的标配,一个开关 + 模板片段,成本极低
|
||||
第二梯队(反代能力追平本地反代)
|
||||
② 配置查看/自定义:每条规则可看生成的 nginx 配置、可改(WebSocket 头、特殊 location 不再需要 SSH)——本地反代已有 auto/custom 模式可直接复用;
|
||||
|
||||
④ HTTP 强制跳转 HTTPS、⑤ IP 白名单 / Basic Auth——公网暴露面的安全标配。
|
||||
|
||||
第三/四梯队(多规则后有价值)
|
||||
批量同步、部署历史回滚、域名健康检查面板(含证书剩余天数)、自定义 upstream、流量统计、错误日志快捷查看——文档里都有具体设计。
|
||||
|
||||
文档末尾附了配套 API 草案(4 个端点 + agent 侧新增项),实现时按这个走即可。说一声做哪几个,我按第一批开始。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 现状回顾(已具备)
|
||||
|
||||
- 手动部署 / 移除单条规则的反代(部署时自动推证书、自动读最新 vhost 端口);
|
||||
- VPS 当前已加载路由展示(`agentStatus.routes`);
|
||||
- 反代文件按 `fonu-<域名>.conf` 写入 Nginx 实际加载目录(`-T` 试探-验证)。
|
||||
|
||||
---
|
||||
|
||||
## 第一梯队:立刻有用(解决「部署了≠生效」的可见性)
|
||||
|
||||
### 1. 部署后在线验证(强烈推荐,第一个做)
|
||||
|
||||
部署成功后 agent 自动做一次真实探测并把结果返回给页面:
|
||||
|
||||
```text
|
||||
在 VPS 上执行:
|
||||
curl -s -o /dev/null -w "%{http_code}" -H "Host: <域名>" http://127.0.0.1:<vhost端口>
|
||||
```
|
||||
|
||||
- 页面显示「部署成功 · 回源自检 200 ✓」或「部署成功 · 但回源 502 ✗(隧道未通)」;
|
||||
- 直接区分「反代层 OK / 隧道层断」,消灭"部署成功但打开 502"这类排障;
|
||||
- 成本:低(agent 加一个验证步骤 + 前端展示字段)。
|
||||
|
||||
### 2. Nginx 配置内容查看 / 自定义
|
||||
|
||||
每条规则提供「查看生成的 nginx 配置」弹窗(对齐本地反代已有的 auto/custom 模式):
|
||||
|
||||
- auto:显示 agent 生成的配置(只读);
|
||||
- custom:用户编辑后下发,agent 写入文件并 reload;
|
||||
- 排障时可确认「文件里到底写了什么」;特殊需求(WebSocket 头、自定义 location、缓存策略)不再需要 SSH;
|
||||
- 成本:中(agent 加 GET/PUT conf 接口;本地反代的 nginx_config.go 模式可直接复用)。
|
||||
|
||||
### 3. 反代状态徽章的实时化
|
||||
|
||||
「已部署 / 未部署」目前基于 `routes` 文件名推断,升级为真实状态:
|
||||
|
||||
- 检查文件存在 ✓ + `nginx -t` 通过 ✓ + 域名出现在 `nginx -T` ✓ → 「已生效」;
|
||||
- 文件在但 `nginx -T` 没有它 → 「已写入未加载」(目录不对的残留场景);
|
||||
- 成本:低。
|
||||
|
||||
## 第二梯队:安全与可控性(公网暴露面的标配)
|
||||
|
||||
### 4. 一键 HTTPS 强制跳转
|
||||
|
||||
生成的配置加一段 80 → 443 的 301 redirect(现在 http 和 443 是两个平行的 server)。
|
||||
|
||||
- 表单按规则勾选「HTTP 跳转 HTTPS」;
|
||||
- 成本:低。
|
||||
|
||||
### 5. 访问控制(IP 白名单 / Basic Auth)
|
||||
|
||||
公网服务(尤其路由器/摄像头等敏感入口)的标配:
|
||||
|
||||
- IP 白名单:`allow x.x.x.x; deny all;` 列表来自表单;
|
||||
- Basic Auth:agent 生成 htpasswd 并在 location 中引用;
|
||||
- 成本:中。
|
||||
|
||||
### 6. WebSocket 支持开关
|
||||
|
||||
生成配置追加 `proxy_set_header Upgrade $http_upgrade; Connection "upgrade";`(群晖/家庭影院等实时服务需要)。
|
||||
|
||||
- 成本:低(一个开关 + 模板片段)。
|
||||
|
||||
## 第三梯队:运维效率(多规则后才有价值)
|
||||
|
||||
### 7. 批量部署 / 全部同步
|
||||
|
||||
「一键同步全部规则」:对当前服务端所有候选规则循环部署,展示每条的成败。
|
||||
配合规则增删后的批量重建场景。
|
||||
|
||||
- 成本:低-中。
|
||||
|
||||
### 8. 部署历史与回滚
|
||||
|
||||
每次部署在 agent 侧留 `fonu-<域名>.conf.bak-<时间戳>`(保留最近 N 份),
|
||||
页面提供「查看历史 / 回滚到此版本」。
|
||||
|
||||
- 误操作(比如改坏了 upstream)30 秒恢复;
|
||||
- 成本:中。
|
||||
|
||||
### 9. 域名健康检查面板
|
||||
|
||||
页面顶部一行:每个已部署域名的公网可达性(HTTPS 状态码 + 证书剩余天数),
|
||||
由 Fonu 侧定时探测(复用服务端诊断的 TTL 缓存模式)。
|
||||
|
||||
- 证书临期/隧道断链主动可见;
|
||||
- 成本:中。
|
||||
|
||||
## 第四梯队:锦上添花
|
||||
|
||||
### 10. 自定义 upstream 覆盖
|
||||
|
||||
允许某条规则的反代目标不跟随穿透规则(例如指向本机 Nginx:80 做二次分流),
|
||||
默认仍自动取穿透规则的 vhost 回源。
|
||||
|
||||
- 成本:低。
|
||||
|
||||
### 11. 流量统计接入
|
||||
|
||||
frps dashboard 已有连接数/流量数据(Fonu 正在拉取),按域名聚合展示到反代列表,
|
||||
可见每条反代的实际使用量。
|
||||
|
||||
- 成本:中。
|
||||
|
||||
### 12. Nginx 错误日志快捷查看
|
||||
|
||||
agent 提供 `tail /www/wwwlogs/<域名>.error.log`,规则行内展开最近 20 行。
|
||||
|
||||
- 成本:低。
|
||||
|
||||
---
|
||||
|
||||
## 建议路线
|
||||
|
||||
```text
|
||||
第一批(1-2 天):① 部署后在线验证 + ③ 状态徽章实时化 + ⑥ WebSocket 开关
|
||||
→ 直接消灭「部署成功但不通」的灰区
|
||||
第二批(2-3 天):② 配置查看/自定义 + ④ 强制跳转 + ⑤ 访问控制
|
||||
→ 反代层能力追平本地反代
|
||||
第三批(按需):⑦⑧⑨⑩⑪⑫
|
||||
```
|
||||
|
||||
## 附:接口草案(第一/二批共用)
|
||||
|
||||
```text
|
||||
GET /api/frpmulti/servers/{id}/agent/routes/{proxyID}/conf # 查看生成配置
|
||||
PUT /api/frpmulti/servers/{id}/agent/routes/{proxyID}/conf # 自定义配置下发
|
||||
POST /api/frpmulti/servers/{id}/agent/routes/{proxyID}/verify # 部署后在线验证
|
||||
POST /api/frpmulti/servers/{id}/agent/routes/batch # 批量同步
|
||||
```
|
||||
|
||||
agent 侧新增:`GET/PUT /api/v1/routes/{domain}/conf`、验证逻辑复用部署链路的探测段。
|
||||
@@ -0,0 +1,209 @@
|
||||
# 公网反代「部署设置」对齐本地反代 —— 方案设计
|
||||
|
||||
> 目标:把本地反向代理(`ProxyView.vue` 规则弹窗)中真正有价值的安全与运维设置,按公网反代的语义迁移到
|
||||
> 「公网反代」页(`/public-proxy`,`ProxyRemoteView.vue`)的「部署设置」中,由 fonu-agent 生成对应的 Nginx vhost 配置。
|
||||
>
|
||||
> 原则:**公网反代只管「公网入口」这一层**。凡是会破坏 frps vhost 路由、或本质属于「本机内网分流」的选项,
|
||||
> 一律不迁移(见「明确不做」一节)。
|
||||
>
|
||||
> 更新日期:2026-09-22
|
||||
|
||||
---
|
||||
|
||||
## 建议
|
||||
「部署设置」弹窗从现在的单页改为**三个 Tab(基础 / 安全 / 高级)**,与本地反代弹窗结构对齐:
|
||||
|
||||
-
|
||||
**基础**:启用 HTTPS、强制跳转、WebSocket、上传大小、代理超时
|
||||
|
||||
-
|
||||
**安全**:IP 白名单 / 黑名单、Basic Auth(用户名 + 可选密码)
|
||||
|
||||
-
|
||||
**高级**:仅 TLS 1.3、安全响应头、限流、连接数限制
|
||||
|
||||
第一梯队 7 项都是 agent 模板内改动 + Fonu 参数透传,风险低、见效快;第二梯队 3 项需要 agent 多写一个 http 上下文文件。说一声我从第一梯队开始做,或者指定要哪几项。
|
||||
|
||||
|
||||
|
||||
## 一、现状对照
|
||||
|
||||
### 本地反代(ProxyView.vue)规则弹窗现有设置项
|
||||
|
||||
| 分组 | 设置项 |
|
||||
| --- | --- |
|
||||
| 基础 | 名称、前端域名、目标地址、**启用 HTTPS**、**HTTP 跳转 HTTPS**、启用规则 |
|
||||
| 安全 · IP 访问控制 | 仅中国大陆 IP、黑名单模式 + 列表、白名单模式 + 列表 |
|
||||
| 安全 · 认证 | Basic Auth(用户名 + **自定义密码**) |
|
||||
| 安全 · 流量控制 | 请求限流(速率 / 突发)、连接数限制(每 IP 并发) |
|
||||
| 安全 · 高级 | 忽略后端 TLS 证书、使用目标 Host 头、仅 TLS 1.3、安全响应头 |
|
||||
| Nginx | auto / custom 自定义配置 |
|
||||
|
||||
### 公网反代(agent `nginxRoute` 模板)现有能力
|
||||
|
||||
`internal/agent/server.go` 的 `routeRequest` 与 `nginxRoute` 已支持:
|
||||
|
||||
- `domain` / `upstream`(`127.0.0.1:<frps vhost 端口>`)/ `tls` + `cert_dir`;
|
||||
- `websocket`(Upgrade / Connection 头);
|
||||
- `redirect_https`(80 → 443 的 301 + 443 TLS server 两块);
|
||||
- `allow_ips`(location 内 allow + deny all);
|
||||
- `basic_auth_user`(密码由 agent 自动生成并落盘 htpasswd);
|
||||
- 默认头:Host / X-Real-IP / X-Forwarded-For。
|
||||
|
||||
Fonu 侧对应链路:`ProxyOptions`(`internal/frp/model.go`)→ `DeployAgentRoute` → `AgentClient.SyncRouteEx`
|
||||
→ `PUT /api/v1/routes`。
|
||||
|
||||
---
|
||||
|
||||
## 二、迁移清单
|
||||
|
||||
### 第一梯队:纯模板级改动,无 Nginx 上下文限制(建议先做)
|
||||
|
||||
| # | 功能 | 对应本地项 | 生成的 Nginx 指令 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | **启用 HTTPS 开关** | 启用 HTTPS | 关闭时仅 `listen 80`;开启且证书已推送时 `listen 443 ssl` |
|
||||
| 2 | **安全响应头** | 安全响应头 | `add_header Strict-Transport-Security / X-Frame-Options / X-Content-Type-Options / Referrer-Policy` |
|
||||
| 3 | **仅 TLS 1.3** | 仅 TLS 1.3 | `ssl_protocols TLSv1.3;` |
|
||||
| 4 | **客户端最大上传大小** | (本地为全局默认) | `client_max_body_size 50m;`(server 级,按规则可覆盖) |
|
||||
| 5 | **代理超时** | (本地固定 3600s) | `proxy_read_timeout 3600s;`(长轮询 / 流媒体 / 大文件必需) |
|
||||
| 6 | **IP 黑名单** | 黑名单模式 | location 内 `deny x.x.x.x;`(与现有白名单对称) |
|
||||
| 7 | **Basic Auth 自定义密码** | Basic Auth 密码 | 用表单密码写 htpasswd;留空则保持「首次自动生成」 |
|
||||
|
||||
### 第二梯队:需要 Nginx `http` 上下文或额外数据下发(成本中等)
|
||||
|
||||
| # | 功能 | 关键约束 |
|
||||
| --- | --- | --- |
|
||||
| 8 | **请求限流** | `limit_req_zone` 只能出现在 `http` 块。agent 需额外生成 `fonu-limits.conf`(宝塔将 vhost 目录 include 在 `http{}` 内,因此该文件顶层指令即 http 上下文),各域名 server 内 `limit_req zone=fonu_<域名>_req burst=N nodelay;` |
|
||||
| 9 | **连接数限制** | 同上,`limit_conn_zone` + `limit_conn fonu_<域名>_conn N;` |
|
||||
| 10 | **仅中国大陆 IP** | 需 `geo` 模块 + 中国 IP 段下发到 VPS(Fonu 已有 china cidr 模块,7456 条 v4),下发体积最大、成本最高,建议最后做 |
|
||||
|
||||
### 明确不做(字段 / 语义不匹配)
|
||||
|
||||
| 功能 | 原因 |
|
||||
| --- | --- |
|
||||
| 使用目标 Host 头 | 公网回源目标是 frps 的 vhost 端口,**frps 靠 Host 头匹配隧道**。改成上游 Host 会让 vhost 无法路由,直接 404。必须保持 `$host` |
|
||||
| 忽略后端 TLS 证书 | 公网 upstream 是 `http://127.0.0.1:<vhost端口>`,不涉及上游 TLS 校验,无作用 |
|
||||
| 自定义 upstream 覆盖 | 技术上可行,但会让流量绕过 frps 隧道(健康检查「隧道」必然 ✗),语义属于「本机内网分流」,只在本地反代页做 |
|
||||
|
||||
---
|
||||
|
||||
## 三、字段契约
|
||||
|
||||
两侧 JSON key 必须严格一致(历史教训:字段未逐项对照导致多轮返工)。
|
||||
|
||||
| 显示名 | `ProxyOptions`(Go,`internal/frp/model.go`) | JSON key | TS(`web/src/api/types.ts`) | `routeRequest`(agent) | Nginx 指令 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 启用 HTTPS | `HTTPSDisabled bool` | `https_disabled` | `https_disabled?: boolean` | `HTTPSDisabled bool` | 关闭时不出 `listen 443` |
|
||||
| 上传大小 | `ClientMaxBodySize string` | `client_max_body_size` | `client_max_body_size?: string` | `ClientMaxBodySize string` | `client_max_body_size 50m;` |
|
||||
| 代理超时 | `ProxyReadTimeout string` | `proxy_read_timeout` | `proxy_read_timeout?: string` | `ProxyReadTimeout string` | `proxy_read_timeout 3600s;` |
|
||||
| 安全响应头 | `SecurityHeaders bool` | `security_headers` | `security_headers?: boolean` | `SecurityHeaders bool` | `add_header ...` |
|
||||
| 仅 TLS 1.3 | `TLS13Only bool` | `tls13_only` | `tls13_only?: boolean` | `TLS13Only bool` | `ssl_protocols TLSv1.3;` |
|
||||
| IP 黑名单 | `DenyIPs []string` | `deny_ips` | `deny_ips?: string[]` | `DenyIPs []string` | `deny x;` |
|
||||
| Basic Auth 密码 | `BasicAuthPassword string` | `basic_auth_password` | `basic_auth_password?: string` | `BasicAuthPassword string` | 写入 htpasswd |
|
||||
| 请求限流(速率) | `RateLimitRate int` | `rate_limit_rate` | `rate_limit_rate?: number` | `RateLimitRate int` | `limit_req_zone ... rate=Nr/s;` |
|
||||
| 请求限流(突发) | `RateLimitBurst int` | `rate_limit_burst` | `rate_limit_burst?: number` | `RateLimitBurst int` | `limit_req ... burst=N` |
|
||||
| 连接数限制 | `ConnLimitMax int` | `conn_limit_max` | `conn_limit_max?: number` | `ConnLimitMax int` | `limit_conn_zone` / `limit_conn` |
|
||||
| 仅中国大陆 | `ChinaOnly bool` | `china_only` | `china_only?: boolean` | `ChinaOnly bool` | `geo` + `allow`/`deny` |
|
||||
|
||||
约束:
|
||||
|
||||
- 这些字段**只作用于公网 Nginx 模板,绝不写入 frpc.toml**(与现有 `websocket` / `redirect_https` 等一致,在 `ProxyOptions` 上以注释标明);
|
||||
- 「启用 HTTPS」采用**反向字段 `https_disabled`**:字段缺省时保持现有行为(证书推送成功即启用 TLS),
|
||||
保证新 Fonu + 旧 agent、旧数据不出现行为突变。
|
||||
|
||||
---
|
||||
|
||||
## 四、实施步骤
|
||||
|
||||
### 阶段 1:第一梯队后端(agent + Fonu)
|
||||
|
||||
1. `internal/agent/server.go`
|
||||
- `routeRequest` 增加第一梯队字段;
|
||||
- `nginxRoute` 模板按字段输出指令:`listen 443` 受 `HTTPSDisabled` 控制;`redirect_https` 仅在 TLS 实际启用时生成;
|
||||
- `client_max_body_size` / `proxy_read_timeout` / 安全响应头 / `ssl_protocols` / `deny` 列表;
|
||||
- Basic Auth:传入密码时 `htpasswd -nbB` 写入,未传则沿用现有「首次自动生成 + 复用」。
|
||||
2. `internal/frp/model.go`:`ProxyOptions` 增加同名 Go 字段(含注释)。
|
||||
3. `internal/frp/agent_client.go`:`SyncRouteEx` 参数改为**传结构体**(当前 10 个位置参数已到上限,继续追加易错):
|
||||
```go
|
||||
type RouteSpec struct {
|
||||
Domain, Upstream, CertDir string
|
||||
TLS, WebSocket, RedirectHTTPS, HTTPSDisabled bool
|
||||
VhostPort int
|
||||
AllowIPs, DenyIPs []string
|
||||
BasicAuthUser, BasicAuthPassword string
|
||||
ClientMaxBodySize, ProxyReadTimeout string
|
||||
SecurityHeaders, TLS13Only bool
|
||||
}
|
||||
func (c *AgentClient) SyncRouteSpec(ctx, spec RouteSpec) (map[string]any, error)
|
||||
```
|
||||
保留 `SyncRoute` / `SyncRouteEx` 作为兼容包装。
|
||||
4. `internal/frp/service.go`:`DeployAgentRoute` 从 `proxy.Options` 组装 `RouteSpec` 透传。
|
||||
|
||||
### 阶段 2:第一梯队前端
|
||||
|
||||
5. `web/src/api/types.ts` 补 `FrpProxyOptions` 字段;
|
||||
6. `ProxyRemoteView.vue`「部署设置」弹窗改为三个 Tab(与本地反代结构对齐):
|
||||
- **基础**:启用 HTTPS、HTTP 强制跳转、WebSocket、上传大小、代理超时;
|
||||
- **安全**:IP 白名单、IP 黑名单、Basic Auth(用户名 + 可选密码);
|
||||
- **高级**:仅 TLS 1.3、安全响应头(第二梯队上线后再加限流/连接数/中国 IP)。
|
||||
|
||||
### 阶段 3:第二梯队(按需)
|
||||
|
||||
7. agent 增加 `fonu-limits.conf` 生成器(http 上下文 zone 定义)+ 每域名 server 引用;
|
||||
8. 中国 IP:Fonu 将 china cidr 列表推送到 agent(`/var/lib/fonu-agent/geo/china.conf`),agent 生成 `geo` 块。
|
||||
|
||||
---
|
||||
|
||||
## 五、验证方案
|
||||
|
||||
每次改动后全量跑(项目既有闭环):
|
||||
|
||||
```powershell
|
||||
cd f:\Fonu-main\Fonu-1.0.2
|
||||
gofmt -w internal/agent/server.go internal/frp/model.go internal/frp/agent_client.go internal/frp/service.go
|
||||
go build ./... ; go vet ./internal/frp ./internal/agent ./internal/api
|
||||
go test ./internal/frp ./internal/api
|
||||
cd web ; npx vue-tsc --noEmit ; npx vite build
|
||||
```
|
||||
|
||||
agent 二进制需按既有流程交叉编译(amd64 + arm64)并升级:
|
||||
|
||||
```powershell
|
||||
$env:GOOS='linux'; $env:GOARCH='amd64'; $env:CGO_ENABLED='0'
|
||||
go build -trimpath -o internal/frp/agentbin/fonu-agent-linux-amd64.new ./cmd/fonu-agent
|
||||
# arm64 同理;Move-Item 覆盖后,在「公网 Agent」页执行「升级 Agent」
|
||||
```
|
||||
|
||||
VPS 侧实测(部署 7023 规则后):
|
||||
|
||||
```bash
|
||||
nginx -t # 语法
|
||||
cat /www/server/panel/vhost/nginx/fonu-7023.36800.cc.conf # 配置内容(或页面「查看配置」)
|
||||
curl -I http://7023.36800.cc # 强制跳转 → 301
|
||||
curl -sI -H 'Host: 7023.36800.cc' http://127.0.0.1:9080 # 回源自检
|
||||
curl -k -sI https://7023.36800.cc # 响应头(HSTS / X-Frame-Options)
|
||||
openssl s_client -connect 7023.36800.cc:443 -tls1_2 </dev/null | head -3 # 仅 TLS1.3 时应握手失败
|
||||
curl -sI -H 'Host: 7023.36800.cc' -d @bigfile http://127.0.0.1:9080 # 上传大小限制
|
||||
for i in $(seq 1 50); do curl -s -o /dev/null -w '%{http_code} ' http://7023.36800.cc/; done # 限流 → 429(第二梯队)
|
||||
```
|
||||
|
||||
排查口径沿用现有约定:健康检查五段徽标(DNS / 证书 / 隧道 / 服务 / 反代生效态)任一 ✗,
|
||||
悬停看详情定位断点,不再靠猜。
|
||||
|
||||
---
|
||||
|
||||
## 六、兼容性与回滚
|
||||
|
||||
| 场景 | 期望行为 |
|
||||
| --- | --- |
|
||||
| 新 Fonu + 旧 agent | 旧 agent 忽略未知 JSON 字段(Go 默认行为),新选项静默不生效 → **部署前必须先「升级 Agent」**,页面提示 |
|
||||
| 旧 Fonu + 新 agent | 新字段缺省,行为与现状一致(`https_disabled` 缺省 = 启用 TLS) |
|
||||
| frps v0.68.0 / v0.71.0 | 与本方案无关(作用于 Nginx vhost,不依赖 dashboard API) |
|
||||
| 回滚 | 关闭对应开关后重新部署即可恢复;agent 侧每域名配置为整文件覆盖写,无残留 |
|
||||
|
||||
---
|
||||
|
||||
## 七、附:与既有文档的关系
|
||||
|
||||
- `docs/agent-proxy-roadmap.md`:功能梯队总览(本方案是其「第二批」在部署设置维度的细化);
|
||||
- `docs/frps-features.md`:frps 服务端配置能力对照(frps.toml 层面,与本文的 Nginx 层面互补)。
|
||||
@@ -0,0 +1,121 @@
|
||||
# frps(服务端)功能实现清单
|
||||
|
||||
> 基于 frp v0.71.0 官方 `frps_full_example.toml` 全量对照。
|
||||
> 状态图例:✅ 已实现 🔶 部分实现(有缺口或需手动步骤) ⬜ 未实现 🚫 有意不实现(有说明理由)
|
||||
>
|
||||
> 「Fonu 入口」= 用户在 Fonu 界面操作的位置;「生成器」= `internal/frp/frps_template.go` 的 frps.toml 同源生成器。
|
||||
> 更新日期:2026-09-21
|
||||
|
||||
---
|
||||
|
||||
## 一、网络与监听([common] 顶层键)
|
||||
|
||||
| 配置项 | 官方默认 | 状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `bindAddr` | `0.0.0.0` | ✅ | 固定生成 `0.0.0.0` |
|
||||
| `bindPort` | `7000` | ✅ | 取服务端表单「端口」,与 frpc 同源 |
|
||||
| `kcpBindPort` | 0(禁用) | ✅ | 表单「协议」选 kcp 时生成 |
|
||||
| `quicBindPort` | 0(禁用) | ✅ | 表单「协议」选 quic 时生成 |
|
||||
| `proxyBindAddr` | 同 bindAddr | ⬜ | 未提供(UDP/HTTP 代理单独绑定地址的场景才需要) |
|
||||
| `vhostHTTPPort` | 0(禁用) | ✅ | 有 http/https 规则时生成;**表单「远程管理 → frps vhost 反代端口」可自定义**,默认 8080 |
|
||||
| `vhostHTTPSPort` | 0(禁用) | ✅ | 同上,默认 8443 |
|
||||
| `vhostHTTPTimeout` | 60 | ⬜ | 未提供(vhost 响应超时,一般无需改) |
|
||||
| `tcpmuxHTTPConnectPort` | 0(禁用) | ⬜ | tcpmux 需要它(存在 tcpmux 规则时生成器只提示 vhost,未生成此键) |
|
||||
| `tcpmuxPassThrough` | false | ⬜ | 未提供 |
|
||||
|
||||
## 二、认证(auth.*)
|
||||
|
||||
| 配置项 | 官方默认 | 状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `auth.method`(token) | `token` | ✅ | 与 frpc 同源;Token 加密存储,生成时解密写入 |
|
||||
| `auth.token` | — | ✅ | 同上 |
|
||||
| `auth.method`(none) | — | ✅ | 生成安全提示注释 |
|
||||
| `auth.oidc.*`(issuer/clientID/clientSecret/audience/scope/tokenEndpointURL 等) | — | 🔶 | frpc 侧 OIDC 表单已支持;frps 侧校验参数依赖 IdP,生成器只输出注释指引手动配置 |
|
||||
|
||||
## 三、TLS(transport.tls.*)
|
||||
|
||||
| 配置项 | 官方默认 | 状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `transport.tls.force` | false | 🔶 | frpc 侧未启用 TLS 时生成**注释**建议开启;无表单开关直接生成 |
|
||||
| `transport.tls.certFile / keyFile` | — | ⬜ | frps 侧 TLS 证书未提供(frpc 侧证书表单已有,frps 反向 TLS 需另加) |
|
||||
| `transport.tls.trustedCaFile` | — | ⬜ | 未提供 |
|
||||
|
||||
## 四、管理面板(webServer.*)
|
||||
|
||||
| 配置项 | 官方默认 | 状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `webServer.addr / port` | `127.0.0.1:0` | ✅ | 表单「frps 管理接口」地址/端口;Fonu 借此拉流量;「frps 管理」按钮直达面板 |
|
||||
| `webServer.user / password` | — | ✅ | 表单同源;密码加密存储(ConfiguredSecretField 掩码闭环) |
|
||||
| `webServer.tls.certFile / keyFile` | — | ⬜ | dashboard 自身 HTTPS 未提供 |
|
||||
|
||||
## 五、HTTP / 子域名(vhost 语义)
|
||||
|
||||
| 配置项 | 官方默认 | 状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `subDomainHost` | "" | 🔶 | **缺口检测已实现**:规则填了 subdomain 时生成器输出注释提醒必须配置;但表单无字段、不能直接生成 |
|
||||
| `custom404Page` | — | ⬜ | 未提供 |
|
||||
| `httpPlugins`(server 端) | — | ⬜ | frps 端 HTTP 插件未提供(frpc 端 8 种插件表单已支持,两者是不同层的功能) |
|
||||
|
||||
## 六、限流与配额
|
||||
|
||||
| 配置项 | 官方默认 | 状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `allowPorts`(端口白名单/范围) | 不限 | ⬜ | 未提供 |
|
||||
| `maxPortsPerClient` | 0(不限) | ⬜ | 未提供 |
|
||||
| `userConnTimeout` | 10 | ⬜ | 未提供 |
|
||||
|
||||
## 七、传输调优(transport.* 其余)
|
||||
|
||||
| 配置项 | 官方默认 | 状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `transport.tcpMux` | true | ✅ | 与 frpc 同源;仅关闭时输出 |
|
||||
| `transport.tcpMuxKeepaliveInterval` | 30 | ✅ | 表单「高级配置」同源(frpc 侧);frps 生成器未输出该键,两端不一致时 TCPMux keepalive 各用默认值——**低风险缺口** |
|
||||
| `transport.maxPoolCount` | 0(按 frpc) | ✅ | 有 pool_count 时输出 |
|
||||
| `transport.heartbeatInterval / timeout` | 30/90 | ✅ | frpc 侧表单同源;frps 生成器不输出(服务端跟随客户端心跳,官方无需配置) |
|
||||
| `transport.tls.force` | — | 🔶 | 见第三节 |
|
||||
|
||||
## 八、运行与日志
|
||||
|
||||
| 配置项 | 官方默认 | 状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `log.to / level / maxDays` | console | ⬜ | frps 日志走 journalctl(agent 采集尾部显示),未提供文件日志配置生成 |
|
||||
| `log.disablePrintColor` | false | ⬜ | 未提供 |
|
||||
| `detailedErrorsToClient` | true | ⬜ | 未提供 |
|
||||
| `printStats`(每 30s) | — | ⬜ | 未提供 |
|
||||
|
||||
## 九、用户隔离与资产
|
||||
|
||||
| 配置项 | 官方默认 | 状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| 多用户 `user` 前缀隔离 | — | ⬜ | 未提供(frps 端 `user` 是 frpc 登录名前缀隔离,与 Fonu 的服务端 user 概念不同) |
|
||||
| `udpPacketSize` | 1500 | ⬜ | 未提供(需与 frpc 同源,frpc 侧也无此表单项) |
|
||||
|
||||
## 十、Fonu Agent 侧的 frps 运维能力(与配置生成配套)
|
||||
|
||||
| 功能 | 状态 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 一键安装 / 升级(SSH) | ✅ | 原子替换 + `-version` 校验,失败不伪报成功 |
|
||||
| systemd 托管(frps.service) | ✅ | 坏 unit 自动接管重写 |
|
||||
| 状态可视(进程/端口/版本/日志尾/dashboard 地址) | ✅ | 「公网 Agent」页 |
|
||||
| 启动 / 停止 / 重启 | ✅ | 含 `ok:false` 失败契约与 journal 透出 |
|
||||
| 配置下发(同源 frps.toml) | ✅ | 「下发 frps 配置」按钮 |
|
||||
| 官方版本安装 / 升级(下拉选择) | ✅ | 含下载代理 |
|
||||
| 证书下发(HTTPS 规则) | ✅ | `/api/v1/certs` |
|
||||
| **公网 Nginx 反代手动部署 / 移除** | ✅ | 试探-验证式目录自动发现(`-T` 加载验证),规则保存与反代解耦 |
|
||||
| **vhost 端口自定义** | ✅ | 表单 → frps.toml → Nginx upstream 三方同源 |
|
||||
|
||||
---
|
||||
|
||||
## 优先级建议(下一步最值得补的)
|
||||
|
||||
1. **`subDomainHost` 表单字段**(🔶):subdomain 功能目前只能靠手动改 frps.toml,是 vhost 体系里最后一块手动拼图;配合 `custom404Page` 一起做收益最高;
|
||||
2. **`transport.tls.force` 开关**(🔶):一条注释提示不如一个开关——公网 frps 的标准安全实践;
|
||||
3. **`allowPorts` / `maxPortsPerClient`**(⬜):多用户/防滥用场景需要;单用户自用优先级低;
|
||||
4. **`tcpmuxHTTPConnectPort`**(⬜):存在 tcpmux 规则时的硬依赖,用到 tcpmux 的话必须补;
|
||||
5. **frps 日志配置**(⬜):journalctl 尾部只能看 6 行,排障时不够;生成 `log.to = "/var/log/frps.log"` 即可接入现有日志查看。
|
||||
|
||||
## 附:架构口径
|
||||
|
||||
- **frps.toml 是生成器产物,不是用户手写文件**:所有已实现项均以 Fonu 服务端表单为唯一事实源,VPS 侧禁止手改(改了会被下次「下发 frps 配置」覆盖);
|
||||
- **端口同源链**:表单 `vhost_http_port/vhost_https_port` → frps.toml `vhostHTTPPort/vhostHTTPSPort` → Nginx `proxy_pass 127.0.0.1:<port>`,三方由 `agentUpstreamFor`/生成器共用同一值;
|
||||
- **agent 与 frps 解耦**:agent 只管 Nginx 片段/证书/frps 生命周期,frps 进程由 systemd 托管;二者通过 Fonu 下发配置保持一致。
|
||||
@@ -0,0 +1,199 @@
|
||||
# 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/<domain>/`(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 上没有任何管理界面。
|
||||
@@ -0,0 +1,156 @@
|
||||
# FRPS 服务端自动管理方案(frps-manager)
|
||||
|
||||
> 目标:frpc 端在 Fonu 提交穿透规则后,frps 端**自动完成反向代理配置与生效**,实现域名无端口访问(`http://tv.36800.cc`),并自动管理 VPS 侧证书。本文是基于现有项目(Fonu v1.1.0 FRP 多服务端模块)的实施方案,不引入新服务、不改 frp 二进制。
|
||||
|
||||
## 1. 现状与问题
|
||||
|
||||
当前(v1.1.0)frpc 侧规则提交后:
|
||||
|
||||
| 环节 | 现状 | 问题 |
|
||||
| --- | --- | --- |
|
||||
| tcp 规则注册(远程端口) | ✅ 自动(frpc 登录时向 frps 动态注册) | 无 |
|
||||
| http 规则域名路由 | ⚠️ 需 frps.toml 预先配置 `vhostHTTPPort` | 首次使用 http 类型时 VPS 要手工改配置+重启 |
|
||||
| frps.toml 生成 | ✅ Fonu 一键生成,用户手动复制到 VPS | 每次变更都要 SSH 手工同步 |
|
||||
| VPS 端口释放(80/443 被占) | ❌ 手动 SSH 停 Nginx | `port unavailable` 最常见根因 |
|
||||
| 改配置后生效 | ❌ 手动 `pkill frps` 重启(frp 无热加载) | 容易忘、容易误 |
|
||||
| VPS 侧证书 | ❌ 无 | HTTPS 要在 VPS 手工装证书 |
|
||||
| 配置漂移检测 | ❌ 无 | VPS 上的文件与 Fonu 数据库可能不一致 |
|
||||
|
||||
## 2. 总体设计
|
||||
|
||||
在 Fonu(frpc 所在机器)新增 **frps-manager** 子系统:Fonu 通过 **SSH** 连接 VPS,执行「检测 → 生成 → 推送 → 生效 → 验证」闭环。VPS 侧**零安装**(只要求 frps + SSH)。
|
||||
|
||||
```text
|
||||
Fonu (NAS) VPS (38.95.74.87)
|
||||
┌─────────────────────────────┐ ┌──────────────────────────┐
|
||||
│ FRP 模块(现有) │ │ frps (7000/80/443/7500) │
|
||||
│ 规则/服务端 CRUD │ │ ↑ frpc 动态注册端口 │
|
||||
│ frps-manager(新增) │ SSH │ frps.toml (推送产物) │
|
||||
│ 检测→生成→推送→reload→验证 │ ────────→ │ /etc/fonu/frps/ │
|
||||
│ 证书申请→推送→挂载 │ │ /etc/fonu/frps/certs/ │
|
||||
└─────────────────────────────┘ └──────────────────────────┘
|
||||
```
|
||||
|
||||
核心思路:**frps 不认识"反代",它只认 frps.toml**。因此"自动反代"= ① 规则变化时重算出应然的 frps.toml(含 vhost 端口、subDomainHost、TLS 配置)→ ② 与 VPS 现状 diff → ③ 无变化则跳过,有变化则原子推送 → ④ 通过 frps **admin API 热重载**(无需重启进程)→ ⑤ 回读验证。证书同理:Fonu 证书页现有 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/fonu/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 会话内批量执行(只读,不改任何东西):
|
||||
|
||||
```bash
|
||||
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 与 Fonu 期望配置的 **diff 摘要**、待办建议(如「80 被 nginx 占用,需释放」)。这就是用户之前反复手工排查的自动化。
|
||||
|
||||
### 4.2 下发(Apply)——「自动反代」的主流程
|
||||
|
||||
触发时机:用户点「下发配置」,或规则保存后若 `mgmt_enabled` 且检测到配置将变化(规则含 http 类型 / 证书变更)自动触发。
|
||||
|
||||
1. **生成期望配置**:扩展现有 `buildFRPSServerConfig`:
|
||||
- 存在 http/https/tcpmux 规则 → 输出 `vhostHTTPPort = 80`、`vhostHTTPSPort = 443`(去端口访问的关键);
|
||||
- 存在 subdomain 规则 → 输出 `subDomainHost = <用户在服务端表单新填的主域>`;
|
||||
- 证书启用时输出 `transport.tls.force` / `webServer.tls` 段;
|
||||
2. **SSH 推送(原子)**:新内容写 `/etc/fonu/frps/frps.toml.new` → `mv` 原子替换 → 旧文件留 `frps.toml.bak`(回滚点);
|
||||
3. **热重载**:调 frps admin API `PUT /api/reload`(frps webServer 7500 已有 Basic Auth 凭据)——**不杀进程**,连接不断;API 不可用时降级 `systemctl restart frps`(nohup 场景提示用户);
|
||||
4. **验证**:重读 VPS 上的 frps.toml 确认与期望一致 + `ss` 确认 80/443 已由 frps 监听 + 通过公网侧发起一次探测请求;
|
||||
5. 全程动作与输出写入系统日志(复用 logstore),UI 弹窗展示每步结果。
|
||||
|
||||
### 4.3 释放端口(安全加固提示,不偷偷杀人)
|
||||
|
||||
检测发现 80/443 被 nginx 等占用时,**不自动杀进程**(危险动作),而是:
|
||||
- 检测报告中标红 + 给出一键命令;
|
||||
- UI 提供「释放端口」按钮:二次确认后执行 `systemctl stop nginx && systemctl disable nginx`(仅当占用者确认为 nginx 时可点,其他占用者只提示不动手);
|
||||
- 释放后自动重跑 Apply 验证闭环。
|
||||
|
||||
### 4.4 证书管理(VPS 侧 HTTPS)
|
||||
|
||||
复用 Fonu 证书页现有 ACME(lego)能力,证书签发在 NAS 侧完成(DNS-01,无需 VPS 参与),然后:
|
||||
|
||||
1. 证书页选「部署到 VPS」→ 选目标服务端;
|
||||
2. SSH 推送 `fullchain.pem` / `privkey.pem` 到 `/etc/fonu/frps/certs/<域名>/`(0600);
|
||||
3. frps.toml 写入 `webServer.tls.certFile/keyFile`(管理面板 HTTPS);对外流量 TLS 若需要也在 frps 侧终止时,走 `transport.tls` 相关段(v0.71 支持 `transport.tls.force` + 证书);
|
||||
4. 续期:Fonu 证书定时任务续期成功后自动重推 + reload(证书 60 天滚动,VPS 侧无感)。
|
||||
|
||||
> 设计取舍:对外业务的 HTTPS 终止仍推荐在 **Fonu 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. 安全设计
|
||||
|
||||
1. SSH 凭据 secret.Box 加密落库,API 永不回显(同 token 掩码机制);
|
||||
2. 推荐文档引导用户用**密钥登录**(Fonu 可一键生成 ed25519 密钥对、展示公钥让用户贴到 VPS `authorized_keys`);
|
||||
3. 所有远程命令为**固定白名单模板**(版本/端口/状态/替换文件/reload),不拼接用户输入,杜绝命令注入;SFTP 只写 `/etc/fonu/frps/` 前缀内路径;
|
||||
4. 危险动作(释放端口、重启 frps)二次确认 + 动作日志;
|
||||
5. Apply 幂等:重复执行无副作用(diff 无变化即跳过);
|
||||
6. 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 与文件系统)。
|
||||
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 602 KiB |
|
After Width: | Height: | Size: 216 KiB |
|
After Width: | Height: | Size: 229 KiB |
|
After Width: | Height: | Size: 306 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 201 KiB |