# frps-manager 基于 frps 0.70.1 的内网穿透 Web 管理系统,多租户 + Web 端代理规则 CRUD。 ## 功能 | 模块 | 说明 | |------|------| | 仪表盘 | frps 状态、监听端口、在线客户端/代理数、Server Token 显示、实时在线列表 | | 租户 | 多租户隔离,每个租户独立带宽/连接配额 | | 客户端 (frpc) | 注册 frpc 客户端(user 字段 + token),归属租户,启停、旋转 token | | 代理规则 | 支持 tcp / udp / http / https,配置远端端口、域名、加密压缩、健康检查 | | frpc.ini 生成 | 点客户端的「frpc.ini」按钮下载一份含所有代理 + 共享 server token + admin API 的配置 | | 热重载支持 | 生成的 frpc.ini 默认带 `admin_addr`/`admin_port`,修改代理后用户在 frpc 主机执行 `frpc reload -c /etc/frpc.ini` 即可生效(无需 SSH 重启) | | 用户 | admin / tenant_admin / user 三级 RBAC,每个用户绑定到租户 | | 审计日志 | 全部管理操作(创建/删除/旋转 token/重载 frps…)写入日志 | | frps 控制 | 通过 Web 重写 frps.ini 并重启 frps、旋转 server token、查看生成的 frps.ini | ## 关键设计:frps 0.70 的特殊性 **fatedier/frp 0.70.1 的 TOML 配置和 auth 流程有问题**: - TOML 解析器**不接受** `auth.method = "token"` / `auth.token = "..."` 这种点路径键(尽管官方 example 文件这么写) - `--token` CLI flag 设的值会被 TOML 加载流程覆盖为**空值** - **唯一真正可用的格式**:`legacy INI`(`[common]` + `token = xxx`),frp 在加载时会打印 `WARNING: ini format is deprecated` 所以本系统: - 生成 `frps.ini`(legacy INI)而不是 frps.toml - 生成 `frpc.ini`(legacy INI)给用户 - 会在 frps 日志看到 deprecation warning — 这是 frp 项目已知问题,未来升级到 0.71+(修复 TOML auth 后)只需把 `render_frps_ini` 改成 TOML 渲染 **另一个特殊性**:frps 0.70+ **没有 server-side `[[proxies]]` 段** — 所有代理规则都在 frpc 端 `[[xxx]]` 配置里,启动时通过 control message 推给 frps。所以 Web 系统**所有代理数据都在 frpc.ini 里**,frps.ini 只管 bind/dashboard/auth/log。 ## 技术栈 - **后端**: Flask 3.0 + Flask-SQLAlchemy + SQLite - **WSGI**: Gunicorn (2 workers) - **前端**: Jinja2 模板 + 原生 CSS(无前端框架依赖) - **被管理**: frps 0.70.1(GitHub: fatedier/frp)+ frpc 0.70.1 - **认证**: Werkzeug password hashing + Flask session ## 目录结构 ``` frps-manager/ ├── app.py # Flask 应用主文件(模型、路由、INI 渲染逻辑) ├── wsgi.py # Gunicorn 入口(DB 初始化) ├── init_db.py # 独立 DB 初始化脚本 ├── requirements.txt ├── README.md ├── _smoke_test.py # 端到端测试(起 frps + frpc + reload 验证) ├── scripts/ │ ├── deploy.sh # 一键部署脚本(systemd) │ └── dev.sh # 本地开发启动 ├── templates/ # Jinja2 模板 │ ├── base.html # 布局 + 导航 │ ├── login.html │ ├── dashboard.html │ ├── tenants.html / tenant_form.html │ ├── clients.html / client_form.html │ ├── proxies.html / proxy_form.html │ ├── users.html / user_form.html │ ├── logs.html │ ├── change_password.html │ └── error.html ├── static/ │ └── style.css # 全部样式 ├── instance/ # SQLite DB + server token(运行时生成) │ ├── frps_manager.db │ └── server_token # 0600 权限,仅 root 可读 └── runtime/ # 运行时数据 ├── bin/frps # frps 二进制(deploy.sh 自动下载) ├── conf/frps.ini # 由 app.py 生成的 frps 配置 ├── logs/ # gunicorn / frps 日志 ├── frps.service # frps systemd 单元 ├── frps-manager.service # web systemd 单元 └── frpc@.service # 用户给 frpc 主机用的模板 ``` ## 安装 ### 一键部署(推荐) ```bash git clone /opt/frps-manager cd /opt/frps-manager sudo bash scripts/deploy.sh ``` 部署脚本会自动: 1. 安装系统依赖(python3, pip, venv, curl) 2. 下载 frp 0.70.1 二进制到 `runtime/bin/frps` 3. 创建 venv 并安装依赖 4. 初始化 SQLite(默认管理员 `admin / admin`,默认租户 `default`) 5. 生成 `frps.ini`(含自动生成的 server token) 6. **动态生成** systemd 服务文件并启动: - `frps.service` — 监听 0.0.0.0:7000,dashboard 在 127.0.0.1:7500 - `frps-manager.service` — Web UI 在 0.0.0.0:5390 - `frpc@.service` — 客户端模板(客户端主机部署用) > systemd unit 文件由 `deploy.sh` 根据项目实际路径自动生成到 `/etc/systemd/system/`, > 所以项目装到任意目录都能正常工作。 启动后访问 `http://:5390`,用 `admin / admin` 登录,**立刻去「改密」改掉默认密码**。 ### 本地开发 ```bash cd frps-manager bash scripts/dev.sh # 浏览器打开 http://localhost:5390 ``` ## 多租户使用流程 1. **管理员**登录 → 创建 **租户**(带带宽/连接配额元数据,文档用) 2. 创建 **普通用户**(绑定到该租户,role = user) 3. 租户用户登录 → 创建 **frpc 客户端**(分配名称 + 租户) 4. 在该客户端下添加 **代理规则**(类型、远端端口/域名、本地端口、加密/压缩/健康检查…) 5. 点客户端列表的「frpc.ini」下载配置(包含所有代理 + 共享 server token + admin API) 6. 把 `frpc.ini` 拷贝到 frpc 主机,启动 frpc 7. **改代理后**:用户拉到新 frpc.ini 后在 frpc 主机执行 `frpc reload -c /etc/frpc.ini`,frpc 自动应用新配置(dashboard 立刻显示新代理在线) ## frpc 配置示例 Web 生成的 frpc.ini: ```ini # Generated by frps-manager for client: laptop-jdoe [common] server_addr = your.frps.host server_port = 7000 authentication_method = token token = AbCdEf123456... # 与所有 frpc 共用的 server token admin_addr = 127.0.0.1 admin_port = 7400 admin_user = admin admin_pwd = admin # 用于 `frpc reload` 命令 [laptop-jdoe_ssh] type = tcp local_ip = 127.0.0.1 local_port = 22 remote_port = 6001 use_encryption = true [laptop-jdoe_web] type = http local_ip = 127.0.0.1 local_port = 8080 custom_domains = web.example.com ``` 存到 frpc 主机 `/etc/frpc/laptop-jdoe.ini`,然后用模板 unit: ```bash sudo cp /path/to/frps-manager/runtime/frpc@.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now frpc@laptop-jdoe ``` 改代理后: ```bash # 在 frpc 主机执行 sudo frpc reload -c /etc/frpc/laptop-jdoe.ini # 或者 frpc reload -c /etc/frpc.ini(按你保存的路径) ``` ## 客户端(frpc)部署教程 ### 1. 下载 frpc 二进制 frpc 和 frps 来自同一个 frp 发行包。在 frpc 主机上执行: ```bash # 查看架构 uname -m # x86_64 → amd64 # aarch64 → arm64 # armv7l → arm # 下载(以 amd64 为例,版本与服务器 frps 保持一致:0.70.1) cd /tmp curl -fSL -o frp.tar.gz \ https://github.com/fatedier/frp/releases/download/v0.70.1/frp_0.70.1_linux_amd64.tar.gz tar xzf frp.tar.gz sudo cp frp_0.70.1_linux_amd64/frpc /usr/local/bin/ sudo chmod +x /usr/local/bin/frpc frpc --version # 应输出 0.70.1 ``` 其他架构替换 `amd64` 为 `arm64`、`arm` 等。 ### 2. 从 Web 管理界面下载 frpc.ini 1. 登录 http://frps-manager-host:5390 2. 进入「客户端」页面 → 找到你的客户端 → 点「frpc.ini」按钮下载 3. 如果需要额外添加代理规则:在 Web 端「代理」页添加 → 再次下载 frpc.ini ### 3. 安装配置文件 ```bash sudo mkdir -p /etc/frpc sudo cp ~/Downloads/frpc-laptop-jdoe.ini /etc/frpc/laptop-jdoe.ini sudo chmod 600 /etc/frpc/laptop-jdoe.ini ``` 配置文件包含: - `server_addr` / `server_port` — frps 服务器地址 - `token` — 共享认证 token(由管理员提供,不要外泄) - `admin_addr` / `admin_port` — 本地管理 API(用于 `frpc reload`) - 各 `[[proxies]]` 段 — 代理规则 > 如果不想用 systemd,也可以直接运行测试: > ```bash > frpc -c /etc/frpc/laptop-jdoe.ini > # 看到 "login to server success" 表示连接成功 > # Ctrl+C 停止 > ``` ### 4. 安装 systemd 服务 方法一:从 frps-manager 主机拷贝(`deploy.sh` 已自动生成): ```bash scp root@frps-host:/etc/systemd/system/frpc@.service /etc/systemd/system/ ``` 方法二:手动创建 `/etc/systemd/system/frpc@.service`(内容如下): ```ini [Unit] Description=frpc — fatedier/frp client (%i) After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=/usr/local/bin/frpc -c /etc/frpc/%i.ini Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target ``` 安装并启动(`%i` 对应 `/etc/frpc/` 下的文件名,不含 `.ini`): ```bash sudo systemctl daemon-reload sudo systemctl enable --now frpc@laptop-jdoe ``` ### 5. 验证连接 ```bash # 查看服务状态 sudo systemctl status frpc@laptop-jdoe # 看到以下关键日志表示成功: # login to server success, get run id [xxxxx] # proxy added: [ssh web] # start proxy success # 查看实时日志 sudo journalctl -u frpc@laptop-jdoe -f ``` 回到 frps-manager Web 仪表盘 → 客户端列表里应显示「在线」状态,代理列表里对应代理应为绿色「在线」。 ### 6. 修改代理后热重载 在 Web 端添加/修改/删除代理规则后: 1. 回到「客户端」页 → 重新下载最新的 `frpc.ini` 2. 在 frpc 主机上替换配置文件并 reload: ```bash sudo cp ~/Downloads/frpc-laptop-jdoe.ini /etc/frpc/laptop-jdoe.ini sudo frpc reload -c /etc/frpc/laptop-jdoe.ini # 看到 "reload success" 表示生效 # 无需重启 frpc 进程,已有的连接不受影响 ``` > frpc.ini 中的 `admin_addr` / `admin_port` 段就是为 reload 准备的。 > 如果你的 frpc 主机上运行了多个 frpc 实例,每个实例的 `admin_port` 必须不同。 ### 7. 多实例场景 同一台主机跑多个 frpc 客户端(例如多个用户共享一台服务器): ```bash # 用户 A 的配置 sudo cp frpc-alice.ini /etc/frpc/alice.ini # 用户 B 的配置(注意 admin_port 不同) sudo cp frpc-bob.ini /etc/frpc/bob.ini sudo systemctl enable --now frpc@alice sudo systemctl enable --now frpc@bob # 分别 reload sudo frpc reload -c /etc/frpc/alice.ini sudo frpc reload -c /etc/frpc/bob.ini ``` ### 8. 故障排查 | 现象 | 原因 | 解决 | |------|------|------| | `token in login doesn't match` | token 错误或过期 | 重新从 Web 下载 frpc.ini | | `type [http] not supported when vhost http port is not set` | frps 未配 vhost 端口 | 联系管理员设置 `FRPS_VHOST_HTTP_PORT` | | `listen tcp :7400: bind: address already in use` | admin 端口冲突 | 改 frpc.ini 里的 `admin_port` | | `connection refused` | frps 未启动或防火墙 | 检查 `telnet frps-host 7000` | | Web 显示「离线」但 frpc 在跑 | frps dashboard 未同步 | 检查 frps dashboard (`:7500`) 是否可达 | | `reload success` 但代理没生效 | frpc.ini 里没写对应 proxy 段 | 重新从 Web 下载最新 frpc.ini | ## 配置(环境变量) | 变量 | 默认值 | 说明 | |------|--------|------| | `FLASK_SECRET` | 随机 | Flask session 密钥(生产必须设置一个长随机串) | | `FRPS_BIN` | `runtime/bin/frps` | frps 可执行文件路径 | | `FRPS_CONFIG` | `runtime/conf/frps.ini` | frps.ini 路径 | | `FRPS_SERVICE` | `frps` | systemd 单元名(不含 .service) | | `FRPS_DASHBOARD_URL` | `http://127.0.0.1:7500` | frps dashboard URL | | `FRPS_DASHBOARD_USER` | `admin` | dashboard 用户名 | | `FRPS_DASHBOARD_PASS` | `admin123` | dashboard 密码 | | `FRPS_BIND_PORT` | `7000` | frps 监听端口 | | `FRPS_WEB_ADDR` | `127.0.0.1` | dashboard 监听地址 | | `FRPS_WEB_PORT` | `7500` | dashboard 端口 | | `FRPS_VHOST_HTTP_PORT` | `80` | http 虚拟主机端口 | | `FRPS_VHOST_HTTPS_PORT` | `443` | https 虚拟主机端口 | | `FRPC_ADMIN_ADDR` | `127.0.0.1` | 写入 frpc.ini 的 admin API 地址 | | `FRPC_ADMIN_PORT` | `7400` | 写入 frpc.ini 的 admin API 端口(每台 frpc 主机必须唯一) | | `FRPC_ADMIN_USER` | `admin` | 写入 frpc.ini 的 admin 用户名 | | `FRPC_ADMIN_PWD` | `admin` | 写入 frpc.ini 的 admin 密码 | ## 安全建议 - 改默认 admin 密码 - dashboard(7500)只在 127.0.0.1 监听 — 如果要让远程看 dashboard,把它放到 nginx 反向代理后面 + HTTPS - Web UI(5390)同理,建议放到 nginx + HTTPS 后 - 不要把 frps 暴露到公网 0.0.0.0:7000 而不设防火墙 — **每个 frpc 用同一个 server token**,谁拿到 token 都能连 - 如果需要更强的隔离,部署多个 frps 实例在不同端口,每个 frps 一个 server token - `instance/server_token` 包含所有 frpc 的共享密钥,备份时加密 ## 已知限制 - frps 0.70.1 INI 格式被官方 deprecation warning — 升级到 0.71+ 后只需把 `render_frps_ini` 改为 TOML - frpc 0.70.1 也用 INI(frpc 端 INI 一直支持到最新版本,不受影响) - Web UI 不支持批量操作(一次性添加多个代理) - 没有内嵌 traffic chart;如需图表请用 Prometheus + frps 自带的 `/metrics` 端点 ## License MIT