在 3X-UI 之上的多用户订阅管理面板,动态渲染 Clash / Mihomo、sing-box、V2rayN 订阅
功能特性 • 快速开始 • 二进制部署 • Docker Compose 部署 • 配置 • API
Passwall Sub Panel 是一个基于 Go + React 的代理订阅管理系统,通过与 3X-UI 面板集成,提供完整的用户管理、订阅生成、流量监控,以及 SSO 单点登录(SAML / OIDC)、本地登录的 2FA / Passkey、操作审计、多数据库(SQLite / MySQL / PostgreSQL)与非 root 运行等能力。
适用场景:从个人 / 朋友圈、小团队,到需要单点登录、操作审计、合规留痕与非 root 部署的中大型组织 —— 同一套单文件二进制都能覆盖,按需开启。
部署形态:单文件 Go 二进制(前端 SPA 通过 go:embed 嵌入),可直接 ./psp 启动,也提供 Docker 镜像。
- 订阅管理 — 动态生成 Clash/Mihomo、Sing-box、V2rayN URI list 配置
- 用户管理 — 用户 CRUD、分组管理(tag filter + 渲染布局)、到期时间、流量限额
- 节点管理 — 通过面板封装管理 3X-UI inbound,支持多面板,新建/导入双流程
- 客户端检测 — UA 自动识别客户端类型,支持白名单/黑名单
- 自动停用 — 多次使用禁用客户端后自动停用账号;超量/到期自动同步 disable
- 本地账号 — UPN/密码登录,bcrypt 哈希
- SAML 2.0 SSO — 支持 Entra ID 等 SAML IdP
- OIDC SSO — 支持 OpenID Connect
- 到期前提醒、到期通知
- 流量低剩余提醒
- 账号停用 / 恢复通知
- 失败自动重试(指数退避)
- 流量统计与历史曲线(用户级 + 节点级)
- 审计日志、订阅访问日志
- 同步任务队列(异步可重试的 3X-UI 写操作)
- 多语言(中文 / 英文)、暗色/亮色主题
- RBAC:admin / operator / user 三角色
三个内置角色。后端按路由组授权(staffGroup = admin+operator,adminGroup = 仅 admin),前端按能力(capability)显示/隐藏操作按钮 —— operator 无权的按钮直接隐藏,不会出现"点了报错"。
| 能力 / 操作 | admin | operator | user |
|---|---|---|---|
| 登录管理后台 | ✓ | ✓ | — |
| 用户 CRUD(普通用户,role=user) | ✓ | ✓ | — |
| 操作 admin/operator 账户、分配 admin/operator 角色 | ✓ | — | — |
| 流量查看 / 手动设置用量 / 紧急访问 | ✓ | ✓ | — |
| 节点:查看、启用/禁用 | ✓ | ✓ | — |
| 节点:新建 / 编辑 / 删除 / 导入 / 认领、分隔符增改删 | ✓ | — | — |
| 分组 / 规则库 / 配置方案:查看 | ✓ | ✓ | — |
| 分组 / 规则库 / 配置方案:新建 / 编辑 / 删除 | ✓ | — | — |
| 日志:查看 | ✓ | ✓ | — |
| 日志:清空 / 清理 / 改保留期 | ✓ | — | — |
| 同步任务:查看、重试、取消 | ✓ | ✓ | — |
| 同步任务:清理已完成 | ✓ | — | — |
| 3X-UI 服务器(面板凭据)管理 | ✓ | — | — |
| 系统设置 / 邮件 SMTP / SSO(SAML / OIDC) | ✓ | — | — |
用户自助页(/user/me:订阅、改密、紧急访问、个人规则) |
— | — | ✓ |
operator=日常运营:管普通用户与流量,但碰不到面板凭据 / 系统设置 / SSO,也不能动 admin/operator 账户或把谁提升为 admin/operator。服务器、系统设置页对 operator 直接重定向到 Dashboard。
自定义角色 / 细粒度权限是预留扩展点,实现与扩展方式见 docs/ARCHITECTURE.md §6.3。
- 运行时:Linux(推荐)或任何支持 Go 的系统
- 数据库:内置 SQLite(默认,零配置)或 MySQL 8.0+
- 3X-UI:≥ 3.1.0,已部署且可访问,准备好 API token 或管理员账号(3.1.0 起
/inbounds/list把 settings 改成了嵌套对象,PSP 适配的就是这一版的响应格式) - 构建时(如果从源码构建):Go 1.26+、Node.js 20+
# 克隆项目
git clone https://github.com/KazuhaHub/Passwall-Sub-Panel.git
cd Passwall-Sub-Panel
# 构建前端(输出到 internal/web/dist,会被 go:embed 嵌入)
cd web-react
npm install
npm run build
cd ..
# 构建后端(-s -w 去掉符号表与调试信息,二进制可瘦 ~25%)
go build -ldflags="-s -w" -o psp ./cmd/panel
# 运行(首次启动会生成 config.yaml 模板)
./psp构建完成的 psp 是一个自包含的单二进制,不依赖任何外部文件即可启动(前端、SQLite 驱动都在里面)。
适合:单机自部署,希望最简单可靠。
# 假设以 root 操作
useradd -r -s /usr/sbin/nologin psp
mkdir -p /opt/psp/{config,data}
chown -R psp:psp /opt/psp
# 把构建产物放到 /opt/psp/psp
cp psp /opt/psp/psp
cp -r config/* /opt/psp/config/ # 默认 rulesets、templates
chmod +x /opt/psp/psp/opt/psp/config/config.yaml(首次启动会自动生成,下面是关键字段):
listen: ":8788"
# JWT 签名密钥(用 openssl rand -base64 36 生成;首启会自动生成)
jwt_secret: "REPLACE-ME-WITH-RANDOM-STRING"
# 加密数据库里存的 3X-UI / OIDC / SAML / SMTP 等凭据(同样首启自动生成)
# 丢了这个 key 就解不开 DB 里 enc: 前缀的密文,记得备份
encryption_key: "REPLACE-ME-WITH-ANOTHER-RANDOM-STRING"
config_dir: "/opt/psp/config"
data_dir: "/opt/psp/data"
# 不写 mysql 块就用默认 SQLite:/opt/psp/data/panel.db
# 要用 MySQL,加上:
# mysql:
# host: "127.0.0.1"
# port: 3306
# user: "psp"
# password: "..."
# database: "passwall"完整的注释化示例见 config/config.yaml.example。
你完全不需要写这个文件——首次启动二进制时它会自动生成(含随机 secrets)。手动写只是想精确控制路径或预置 MySQL DSN 时用。
仓库内 deploy/systemd/passwall-sub-panel.service 已经写好(含 hardening 选项):
sudo cp deploy/systemd/passwall-sub-panel.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now passwall-sub-panel
sudo systemctl status passwall-sub-panel默认监听 :8788。浏览器访问 http://<server>:8788 即可。首次启动会自动创建 admin / admin 账号,登录后立刻改密码。
仓库提供示例 deploy/nginx/passwall-sub-panel.conf:
sudo cp deploy/nginx/passwall-sub-panel.conf /etc/nginx/conf.d/
# 改 server_name 和证书路径
sudo nginx -t && sudo systemctl reload nginx走 Nginx 时记得在管理后台「系统设置 → 公网基地址」填上 https://your-domain —— 订阅 URL 生成依赖这一项。
最简方式——不用 clone 仓库,下载一份 docker-compose.yml 就能跑:
mkdir -p /opt/Passwall-Sub-Panel && cd /opt/Passwall-Sub-Panel
curl -O https://raw.githubusercontent.com/KazuhaHub/Passwall-Sub-Panel/main/docker-compose.yml
docker compose up -d
docker compose logs -f psp完事。首启后当前目录会出现 ./config/,里面是:
/opt/Passwall-Sub-Panel/
├── docker-compose.yml
└── config/
├── config.yaml # 自动生成,含随机 secrets
├── rulesets/ # Clash 分流规则集
└── templates/ # 订阅渲染模板
访问 http://<host-ip>:8788,首登 admin / admin。
仓库 docker-compose.yml 的关键决策:
| 项 | 默认 | 原因 |
|---|---|---|
| 网络模式 | network_mode: host |
让 PSP 容器以 127.0.0.1:<port> 直访同宿主机的 3X-UI,无需 extra_hosts |
| 镜像 | ghcr.io/kazuhahub/passwall-sub-panel:latest,pull_policy: always |
:latest = 最新稳定版,每次 up -d 自动重拉;想跟测试版(beta)见下「镜像渠道」 |
| 配置 | bind mount ./config:/app/config |
宿主机直接 nano 编辑,所见即所得;首启 entrypoint 从镜像内 /app/defaults/ 把模板/规则集种到空目录里 |
| 数据 | 命名 volume psp-data |
SQLite 数据库 + 运行时数据,不需要宿主机直接访问 |
所有运维设置(含 secrets、MySQL DSN)都改 ./config/config.yaml——不用 .env、不用 一堆 PSP_* 环境变量。
发布分两条滚动通道,与面板内版本号颜色(绿 = 稳定、黄 = 测试)一致:
| Tag | 跟踪 | 适合 |
|---|---|---|
:latest |
最新稳定版(默认) | 生产 / 日常 |
:beta |
最前沿——任何最新发布(预发布或稳定版) | 想一直用最新 |
:v3.7.0 / :v3.7.0-beta.17 |
钉死某个确切版本 | 不想自动滚动 |
切换通道只改 docker-compose.yml 里那一行 image,然后 docker compose up -d(pull_policy: always 会自动重拉):
services:
psp:
image: ghcr.io/kazuhahub/passwall-sub-panel:beta # latest → beta
:beta是「永远最新」而非「只有 beta」:在:beta上的部署,等稳定版(如v3.7.0)发布后会自动滚上稳定版,之后再滚到下一个 beta。想固定在稳定轨就用:latest,想钉死不动就用:vX.Y.Z。此外面板会在管理员通知里检测并提示 PSP 新稳定版(只提示稳定版、不催 beta),3X-UI 面板低于 PSP 已测最高支持版本时也会提示。
cd /opt/Passwall-Sub-Panel
nano config/config.yaml # 用任何编辑器
nano config/templates/clash.yaml # 模板也是
docker compose restart psp # 改完重启生效不用 docker compose exec、不用进容器、不用临时装编辑器。
宿主机上跑着 3X-UI(比如监听 127.0.0.1:2053)→ 登录 PSP 后,「服务器」页面 URL 填 http://127.0.0.1:2053。
如果不能用 host 网络(比如和外部 MySQL 容器走自定义 bridge):
services:
psp:
# ... 其他不变
# network_mode: host # 注释掉
ports:
- "127.0.0.1:8788:8788" # 启用端口映射
extra_hosts:
- "host.docker.internal:host-gateway"然后「服务器」页面里 3X-UI URL 改成 http://host.docker.internal:<3xui_port>。
不想用 GHCR 上的预构建版(自定义代码、网络拉不到 GHCR):
services:
psp:
# image: ghcr.io/kazuhahub/passwall-sub-panel:latest # 注释掉
build: . # 用仓库根的 Dockerfile然后 git clone 仓库后在仓库根目录 docker compose build && docker compose up -d。
config.yaml 只放启动必需的最少字段,绝大多数运维设置(公网地址、邮件、登录模式、CRON 周期、限流等)走管理后台「系统设置」存到数据库。
首次启动没有 config.yaml 时会自动生成一个,里面 jwt_secret / encryption_key 都是随机的,开箱即用。手动配置时参考 config/config.yaml.example — 这是带完整注释的官方样板。
| 字段 | 必填 | 说明 |
|---|---|---|
listen |
否 | 监听地址,默认 :8788 |
jwt_secret |
是 | JWT 签名密钥(首启自动生成;轮换 = 让所有现有 session 失效) |
encryption_key |
是 | DB 里 3X-UI / OIDC / SAML / SMTP 凭据的 AES-GCM 加密 key;丢了无法解密 |
config_dir |
否 | rulesets、templates 所在目录,默认 ./config |
data_dir |
否 | SQLite 数据库与运行时数据,默认 ./data |
mysql.* |
否 | 留空 = 用嵌入 SQLite;填了即切到 MySQL |
日常运维只改 config.yaml。代码里保留几个环境变量做应急逃生口,绝大多数人用不到:
| 变量 | 何时才用 |
|---|---|
PSP_CONFIG |
改 config.yaml 文件路径(Docker 镜像里默认指 /app/config/config.yaml) |
PSP_TRUSTED_PROXIES |
反向代理不在 loopback 时填代理 CIDR;none = 忽略所有 X-Forwarded-For |
PSP_JWT_SECRET / PSP_ENCRYPTION_KEY / PSP_MYSQL_DSN 也仍然能覆盖对应 yaml 字段,但不推荐——会让"一个 config.yaml 看到所有配置"的心智模型破掉。
| 分类 | 说明 |
|---|---|
| 公网基地址 | 订阅 URL 生成的 base,必填(Nginx 模式下填 https://domain) |
| 登录模式 | SSO 优先 / 双形态(SSO + 本地)/ 仅本地 |
| 订阅路径 | URL 前缀,默认 sub |
| 客户端规则 | UA 匹配关键词 + 渲染格式(mihomo / sing-box)+ 白/黑名单 |
| 邮件提醒 | SMTP 配置 + 模板编辑 |
| CRON 周期 | 流量拉取、健康检查、对账等节奏 |
| RBAC | operator 角色可见范围、admin 邮件白名单 |
| 类型 | 触发时机 |
|---|---|
expire_before |
到期前提醒(窗口可配) |
expired |
已到期 |
traffic_low |
流量剩余低于阈值 |
traffic_exhausted |
流量已用尽 |
account_disabled / account_enabled |
账号停用 / 恢复 |
announcement |
管理员群发公告 |
GET /health # 健康检查
GET /<sub_path>/:token # 订阅(路径前缀由系统设置控制)
GET /api/auth/saml/login
POST /api/auth/saml/acs
GET /api/auth/oidc/login
GET /api/auth/oidc/callback
GET /api/me # 当前用户信息
GET /api/me/sub-url # 订阅 URL 与重置
PATCH /api/me/password # 改密
GET /api/me/traffic # 个人流量
# Admin / operator 视权限
GET/POST/PUT/DELETE /api/admin/users
GET/POST/PUT/DELETE /api/admin/nodes
GET/POST/PUT/DELETE /api/admin/groups
GET/POST/PUT/DELETE /api/admin/servers
GET/POST/PUT/DELETE /api/admin/rules
GET/POST/PUT/DELETE /api/admin/templates
GET/PUT /api/admin/settings
GET /api/admin/sub-logs
GET /api/admin/audit-logs
GET /api/admin/sync-tasks
完整路由见 internal/transport/http/router.go。
| 层 | 技术 |
|---|---|
| 后端 | Go 1.26, Gin, GORM |
| 前端 | React 18, TypeScript, MUI (Material Design 3), Zustand, i18next, ECharts |
| 构建 | Vite (前端), go build -ldflags="-s -w" (后端) |
| 数据库 | SQLite (默认,纯 Go) / MySQL 8.0+ |
| 认证 | JWT (HS256), SAML 2.0 (crewjam/saml), OIDC (coreos/go-oidc) |
| 加密 | AES-GCM (敏感字段)、bcrypt (密码) |
| 集成 | 3X-UI Bearer token 优先,username/password cookie 兜底 |
本项目采用 GNU AGPL v3 许可证。
这是带有"网络条款"的强 copyleft 协议:任何人修改本项目后,无论是分发二进制还是部署成对外的网络服务,都必须向使用者提供完整的对应源码。详见根目录 LICENSE 全文。
Copyright (C) 2026 Kazuha Mo
