frps-manager 教程:多租户内网穿透 Web 管理从部署到热重载
内网穿透最麻烦的从来不是 frp 本身,而是人一多就乱套:谁的隧道占了哪个端口、token 泄露了怎么轮换、改个代理规则还要 SSH 上服务器改配置重启。frps-manager 就是为了解决这些问题:一个基于 frps 0.70.1 的内网穿透 Web 管理系统,多租户隔离、代理规则可视化增删改查、frpc.ini 一键生成、改完配置热重载生效。本文从一键部署讲到多租户使用流程、frpc 客户端部署和生产安全加固。
一、项目简介
frps-manager 是 AI-Agent 组织下的开源项目(MIT 协议),后端用 Flask 3.0 + Flask-SQLAlchemy + SQLite,Gunicorn 2 workers 跑 WSGI,前端是 Jinja2 模板加原生 CSS,没有任何前端框架依赖。被管理的服务端是 fatedier/frp 0.70.1 的 frps,客户端对应同版本的 frpc。整个系统编译部署后只有一个 Web 入口:浏览器打开 http://<host>:5390,登录后就能完成租户、客户端、代理规则、用户和 frps 本身的全部管理操作。
和直接手写 frps.toml / frpc.toml 相比,frps-manager 的价值在于三点:第一,多租户隔离,每个租户有独立的带宽和连接配额元数据,适合把一台 frps 分给多个团队或客户用;第二,代理规则全部落在数据库里,Web 端改完直接生成新的 frpc.ini,客户端执行一条 frpc reload 就生效,不用重启进程;第三,全部管理操作写审计日志,谁建了哪个代理、谁轮换了 token 都有据可查。
二、功能特性
| 模块 | 说明 |
|---|---|
| 仪表盘 | 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 即可生效,无需重启 |
| 用户与 RBAC | admin / tenant_admin / user 三级角色,每个用户绑定到租户 |
| 审计日志 | 全部管理操作(创建、删除、旋转 token、重载 frps 等)写入日志 |
| frps 控制 | 通过 Web 重写 frps.ini 并重启 frps、旋转 server token、查看生成的 frps.ini |
需要注意的是,frps 0.70 以上版本没有服务端 [[proxies]] 段,所有代理规则都在 frpc 端配置,启动时通过控制消息推给 frps。所以 Web 系统里的代理数据最终都落在 frpc.ini 里,frps.ini 只负责 bind 地址、dashboard、认证和日志这四件事。理解这一点,就能理解为什么“改代理”只需要在 frpc 侧 reload,而不需要动 frps。
三、关键设计:frps 0.70 的 TOML 认证坑
用 frps-manager 之前必须先理解它为什么坚持用 legacy INI 而不是 TOML。这不是作者保守,而是 fatedier/frp 0.70.1 的 TOML 配置和 auth 流程确实有问题,README 里明确写了三点:
- TOML 解析器不接受
auth.method = "token"/auth.token = "..."这种点路径键,尽管官方 example 文件就是这么写的; --token命令行 flag 设的值会被 TOML 加载流程覆盖为空值;- 唯一真正可用的格式是 legacy INI(
[common]+token = xxx),frp 加载时会打印WARNING: ini format is deprecated。
所以本系统生成的是 frps.ini 而不是 frps.toml,给用户下载的也是 frpc.ini。在 frps 日志里看到 deprecation warning 是正常的,这是 frp 项目的已知问题,不是部署故障。等未来升级到 0.71 以上版本(修复 TOML auth 之后),只需要把 render_frps_ini 改成 TOML 渲染即可。项目里还保留了一个 templates/frpc.toml.j2 模板,说明作者已经为 TOML 迁移留了口子。
另外一个特殊性前面提过:frps 0.70+ 没有服务端代理段。Web 端维护的代理规则,全部通过 frpc.ini 下发到 frpc,再由 frpc 注册到 frps。仪表盘上看到的“在线代理”,本质是 frpc 上报给 frps 的已注册代理。
四、适用场景
- 多团队共用一台穿透服务器:每个团队一个租户,代理规则互不可见,配额分开记,出问题先看审计日志定位到人;
- 给客户交付内网穿透:客户只需要在自己的机器上跑 frpc,代理规则由你在 Web 端配好,客户下载 frpc.ini 即用,改规则不用再远程帮他改文件;
- 开发测试环境暴露:本地跑的 Web 服务、API、数据库管理界面,通过 http / https 代理挂一个域名就能让外网同事访问;
- 远程运维通道:SSH(tcp 代理)、RDP、各类管理后台通过固定远端端口映射出去,配合加密和 token,比直接暴露端口安全得多;
- IoT 与边缘设备接入:设备侧只装 frpc,云端统一在 Web 端管理代理和 token,设备上线下线在仪表盘实时可见。
五、安装部署
项目目录结构如下,部署前先有个整体概念:
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 模板(dashboard / tenants / clients / proxies / users / logs)
├── 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 主机用的模板
5.1 一键部署(推荐)
git clone <repo-url> /opt/frps-manager
cd /opt/frps-manager
sudo bash scripts/deploy.sh
部署脚本会自动完成以下步骤:安装系统依赖(python3、pip、venv、curl);下载 frp 0.70.1 二进制到 runtime/bin/frps;创建 venv 并安装依赖;初始化 SQLite(默认管理员 admin / admin,默认租户 default);生成 frps.ini(含自动生成的 server token);动态生成 systemd 服务文件并启动。systemd unit 文件由 deploy.sh 根据项目实际路径自动生成到 /etc/systemd/system/,所以项目装到任意目录都能正常工作。
启动后会有三个服务: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 是给客户端主机部署用的模板。浏览器访问 http://<host>:5390,用 admin / admin 登录,立刻去「改密」改掉默认密码。
5.2 本地开发
cd frps-manager
bash scripts/dev.sh
# 浏览器打开 http://localhost:5390
项目还带了一个端到端冒烟测试 _smoke_test.py,会真实起 frps + frpc 并验证 reload 流程,改完代码后可以跑一遍确认核心链路没被破坏。
六、首次登录与安全设置
第一次登录后按这个顺序做三件事,缺一不可:
- 改默认密码:admin 账号的默认密码是
admin,任何人拿到地址就能登录。登录后先去改密页面改成强密码; - 确认 server token:仪表盘会显示当前的 Server Token,这是所有 frpc 共用的共享密钥。默认是部署时自动生成的随机串,如果怀疑泄露,在 Web 端旋转 token,然后所有客户端重新下载 frpc.ini;
- 检查监听地址:frps 的 dashboard 默认只在 127.0.0.1:7500 监听,Web UI 在 0.0.0.0:5390。生产环境建议把 Web UI 放到 nginx 反向代理后面加 HTTPS,不要直接把 5390 暴露到公网。
# 检查三个服务是否都在跑
systemctl status frps frps-manager
ss -tlnp | grep -E '7000|7500|5390'
# 看 frps 日志(留意 ini deprecation warning,属于正常现象)
journalctl -u frps -n 50 --no-pager
tail -n 100 /opt/frps-manager/runtime/logs/frps.log
七、多租户使用流程
完整的业务流程是七步,角色分工很明确:管理员建租户和用户,租户用户自己建客户端和代理规则。
- 第 1 步,管理员登录,创建租户(带带宽 / 连接配额元数据,配额目前是文档用途的元数据,用于标识和约定);
- 第 2 步,创建普通用户,绑定到该租户,role 设为 user;
- 第 3 步,租户用户登录,创建 frpc 客户端(分配名称,归属到自己的租户);
- 第 4 步,在该客户端下添加代理规则:类型选 tcp / udp / http / https,填远端端口或域名、本地端口,按需开加密、压缩和健康检查;
- 第 5 步,点客户端列表的「frpc.ini」下载配置,配置里包含该客户端的所有代理、共享 server token 和 admin API;
- 第 6 步,把 frpc.ini 拷贝到 frpc 主机并启动 frpc(见第八节);
- 第 7 步,改代理后热重载:在 Web 端改完规则,重新下载 frpc.ini,在 frpc 主机执行
frpc reload -c /etc/frpc.ini,新配置立即生效,已有连接不受影响,dashboard 上马上能看到新代理在线。
权限上注意:admin 能管所有租户,tenant_admin 管自己租户,user 只能操作自己名下的客户端和代理。每个用户都绑定到租户,跨租户的操作在 Web 端是看不到入口的。
八、frpc 客户端部署
8.1 下载 frpc 二进制
frpc 和 frps 来自同一个 frp 发行包,版本必须与服务器的 frps 保持一致(0.70.1):
# 查看架构
uname -m
# x86_64 → amd64
# aarch64 → arm64
# armv7l → arm
# 下载(以 amd64 为例)
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
8.2 下载并安装 frpc.ini
登录 http://<frps-manager-host>:5390,进「客户端」页面找到自己的客户端,点「frpc.ini」下载。如果需要额外加代理规则,先在 Web 端「代理」页添加,再重新下载。Web 生成的 frpc.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
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 用;其余 [xxx] 段是各代理规则。配置文件权限设为 600,因为里面有 token 和 admin 密码。
8.3 安装 systemd 服务
方法一是从 frps-manager 主机拷贝现成的模板(deploy.sh 已自动生成):
scp root@frps-host:/etc/systemd/system/frpc@.service /etc/systemd/system/
方法二是手动创建 /etc/systemd/system/frpc@.service:
[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
sudo systemctl daemon-reload
sudo systemctl enable --now frpc@laptop-jdoe
# 不想用 systemd 也可以直接前台运行测试
frpc -c /etc/frpc/laptop-jdoe.ini
# 看到 "login to server success" 表示连接成功,Ctrl+C 停止
8.4 验证连接与热重载
# 查看服务状态
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 仪表盘,客户端列表里应显示「在线」,代理列表里对应代理应为绿色「在线」。在 Web 端添加、修改或删除代理规则后,回到「客户端」页重新下载最新的 frpc.ini,在 frpc 主机上替换配置文件并 reload:
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 必须不同,否则会报 listen tcp :7400: bind: address already in use。
8.5 多实例场景
同一台主机跑多个 frpc 客户端(例如多个用户共享一台服务器),用同一个 frpc@.service 模板,实例名区分即可:
# 用户 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
九、配置详解(环境变量)
frps-manager 的行为由环境变量控制,部署时按需调整。下面是完整清单:
| 变量 | 默认值 | 说明 |
|---|---|---|
| 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 密码 |
最关键的三个:FLASK_SECRET 生产环境一定要设一个长随机串,否则 session 可能被伪造;FRPS_VHOST_HTTP_PORT / FRPS_VHOST_HTTPS_PORT 决定 http / https 类型代理能不能用,报 type [http] not supported when vhost http port is not set 就是这两个没配;FRPC_ADMIN_PORT 是写进每个 frpc.ini 的默认值,多实例场景下要在 Web 端或配置文件里逐个改掉。
十、常见问题
| 现象 | 原因 | 解决 |
|---|---|---|
| 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 |
排障时先分清是 frps 侧还是 frpc 侧的问题:frps 侧看 journalctl -u frps 和 dashboard :7500;frpc 侧看 journalctl -u frpc@xxx;Web 管理端的问题看 journalctl -u frps-manager 和 runtime/logs/ 下的 gunicorn 日志。三者日志分开,定位很快。
十一、安全建议与运维建议
- 改默认 admin 密码:部署完第一件事,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 的共享密钥,备份时加密;同时备份instance/frps_manager.db(SQLite 数据库,里面是全部租户、客户端和代理规则); - 定期轮换 token:怀疑泄露时在 Web 端旋转 server token,然后通知所有客户端重新下载 frpc.ini;
- 监控:Web UI 没有内嵌流量图表,如需图表用 Prometheus + frps 自带的
/metrics端点。
十二、已知限制与升级路径
- frps 0.70.1 的 INI 格式被官方标记 deprecation warning,升级到 0.71+ 后只需把
render_frps_ini改为 TOML 渲染; - frpc 0.70.1 也用 INI,frpc 端 INI 一直支持到最新版本,不受影响;
- Web UI 不支持批量操作,一次性添加多个代理需要逐个添加;
- 没有内嵌流量图表,如需图表请用 Prometheus + frps 自带的
/metrics端点; - 租户的带宽 / 连接配额目前是元数据(文档用途),不做硬性限流 enforcement,超配额需要人工干预。
总结一下:frps-manager 把 frp 0.70.1 的服务端管起来了——多租户、RBAC、审计、可视化代理规则、一键生成 frpc.ini、热重载,是一套能直接上生产的内网穿透管理方案。部署时记住三个要点:改默认密码、配好 vhost 端口、把 Web UI 藏到反代后面。客户端侧记住一个命令就够了:frpc reload -c /etc/frpc/xxx.ini。






