Jabali Panel
一个用于 WordPress 和 PHP 托管的现代托管控制面板,使用 Go 和 React 构建。
多租户隔离。根安全自动化。数据库驱动的协调。 单个面板二进制文件同时提供 API 和嵌入式 SPA,而特权主机 操作则委托给一个由 root 拥有的 Unix 套接字代理执行。
[!NOTE] Jabali Panel 目前处于发布候选阶段。在 1.0 版本之前,请预期快速迭代和 破坏性变更。
演示与网站
演示模式(由构建标签控制,位于 main)
位于 https://demo.jabali-panel.com 的公共演示运行 演示模式。截至
JAB-159,它位于 main,在编译时从生产构建产物中排除
(参见 ADR-0160)。它不是运行时切换
开关,也没有 feat/demo-mode 分支可供变基。
演示模式添加的内容(仅存在于演示构建中):
- 写入拦截中间件 — 每个非幂等
/api/v1/*请求 (POST/PUT/PATCH/DELETE) 返回403 {"error":"demo_mode"},因此访客可以 浏览所有读取端点,而永远不会触及代理或数据库写入; - 一个
/info端点,用于公开预置的演示凭据; - 固定的 DEMO 横幅 + “以管理员身份进入 / 以用户身份进入” 按钮。
为什么这在 main 上是安全的。 演示 Go 代码带有 //go:build demo,并且
演示 UI 受 import.meta.env.VITE_DEMO === "1" 保护,因此生产构建
不包含任何这些内容 — 写入拦截、暴露凭据的 /info 以及
横幅/登录覆盖在非演示二进制文件和 SPA 包中物理上不存在。
CI 在每个 PR 上证明这一点(make demo-guard + demo-guard / ui-unit 作业
断言生产构建没有 demo_mode / jabali-demo-banner 标记,而
演示构建则有)。这严格强于旧的“将其保留在分支上并
希望没有人翻转标志”的模型。
运行演示: 使用 echo demo > /etc/jabali/deploy-profile 标记主机。
install.sh 和每个 jabali update 随后使用 -tags demo 构建面板,并使用
VITE_DEMO=1 自动构建 SPA —— 演示跟踪 main 且无需变基。
生产主机保持该文件不存在(默认状态),且从不包含演示代码。
安装
在全新的 Debian 13 系统上一行安装 —— 默认启动 TUI 安装程序 (选择部署配置文件 + 可选模块,然后查看实时进度 窗格):
curl -fsSL https://get.jabali-panel.com | sudo bash
get.jabali-panel.com 提供 bootstrap.sh:它下载最新的经 sha256 校验的发布 tarball,解压预构建的 jabali-installer
二进制文件 + install.sh,并在真实终端上运行安装程序(即使通过 curl | bash 管道传输也能正常工作)。在 -s -- 后添加参数,例如 --dry-run。
经典 / 脚本化安装(无 TUI —— 直接运行经过验证的 bash 安装程序):
curl -fsSL https://raw.githubusercontent.com/shukiv/jabali-panel/main/install.sh | sudo bash
两者运行相同的引擎。install.sh 获取 Go 1.25,构建面板 + 代理二进制文件,使用 Vite 构建 SPA,写入 systemd 单元,配置 MariaDB + Redis + PowerDNS + Stalwart + Bulwark + CrowdSec(根据所选模块),并对 /health 进行冒烟测试。幂等——重新运行以升级。设置 JABALI_MODULES=…(逗号分隔列表)或保留 TUI 以构建最小化 / 模块化安装。
可选标志:
--debug显示完整输出而非旋转指示器--hostname <fqdn>覆盖自动检测的主机名JABALI_HOSTNAME=<fqdn>用于无人值守安装的环境变量等效项
卸载(回滚系统软件包,可选保留 /home):
curl -fsSL https://get.jabali-panel.com | sudo bash -s -- --uninstall
安装完成后:
- 管理面板:
https://your-host:8443/jabali-admin - 用户面板:
https://your-host:8443/jabali-panel - Webmail:
https://mail.your-domain/
面板 API 监听 Unix socket;nginx 在 :8443 上终止 TLS 并
代理上游。如果 nginx 宕机,面板和 agent 保持运行,因此
运维人员可以通过 jabali CLI 进行恢复,而不会丢失进行中的状态。
亮点
- 每用户 Linux 账户,配备每用户 PHP-FPM 主进程 + cgroup v2 + POSIX 配额
- 通过 nspawn 容器提供 SSH shell 访问,支持自动启动和空闲超时
- 用于 SSL、邮件、DNS、备份、迁移的 Root agent —— 前端为基于
/run/jabali-agent/agent.sock的类型化 NDJSON RPC 契约(面板不执行 shell 命令) - 以数据库为真相源模型:60 秒协调器读取面板数据库,并收敛 nginx vhosts、PHP 池、邮箱、DKIM、DNS、SSL、mta-sts 以及每用户限制
- cPanel 和 WHM 迁移(分析 → 修复权限 → 验证 → 恢复),保留 MySQL 用户 + 密码哈希
- 用于从外部服务器迁移邮件的 IMAP 同步
- Stalwart Mail Server,支持浏览器信任的 IMAPS/465/587(LE 证书推送到 Stalwart 证书对象)以及自删除 SSO 文件(Installatron 模式),实现一键 Webmail
- 每邮箱转发器、自动回复、共享文件夹、免责声明
- Bulwark(Next.js JMAP)Webmail,支持同源每租户路由(nginx
sub_filter将面板主机名重写为$host,使 SPA 在mail.<tenant>上保持同源) - PowerDNS 权威服务器 + 递归解析器,支持原生 DNSSEC(每域名开关)
- 每域名监听 IP 绑定(M24 IP 管理器),针对 MariaDB 11.x 进行保留字安全迁移
- 每域名可选的 nginx FastCGI 微缓存,针对购物车 / 管理 / 认证 Cookie 提供安全绕过
- Restic 备份(account_full + system_backup),支持加密、去重、SFTP / S3 目标,计划任务 + 按需执行
- WordPress 一键安装 / 删除 / 克隆 (M10) — 15 个应用目录 (M19) 包括 Moodle / Joomla / NextCloud / OpenCart / Mautic / Drupal
- 每用户资源限制:cgroups v2 slice drop-in, nginx limit_req, POSIX quota — 管理员可切换,由 reconciler 收敛
- 集成安全套件:CrowdSec parsers + AppSec WAF + 每用户 出站防火墙 (nftables + cgroupv2-vmap) + ModSecurity 被 CrowdSec AppSec 取代 (ADR-0060) + LMD + ClamAV-on-demand + YARA + Tetragon 用于恶意软件检测 + jabali 隔离 + M14 通知分发
- 6 通道通知:Discord, ntfy, Web Push (VAPID), SMS, Email, Webhook, Slack, 应用内铃铛 — 4 个事件源包括证书续期, 磁盘已满, 服务宕机, CrowdSec 峰值
- 一次性登录令牌 (CLI + 仪表盘) 带有 IP 绑定
- 面板与 webmail 之间无魔法链接的 SSO (无 Hydra / OIDC 开销 — ADR-0040 取代了 M16 Hydra 回滚)
- 审计日志, 账户活动流, 加密诊断日志共享至 支持, 应用内更新 + 支持选项卡
功能地图
管理面板
- 仪表盘,包含统计信息、健康状态、最近活动、通知铃铛
- 用户管理,支持暂停、套餐、配额、身份模拟
- 服务器设置(主机名、名称服务器、公网 IP、面板证书)
- systemd 服务的服务管理器 + 启动/停止/重启
- PHP 版本及每用户池管理(服务器级扩展选项卡)
- DNS 区域、模板、DNSSEC、辅助 NS
- SSL 签发与续期(面板证书 + 每域名)
- IP 地址分配(托管 IP 池、每域名绑定)
- 备份:account_full + system_backup,本地 + 远程(SFTP/S3), 计划任务,加密目标
- 迁移(cPanel 恢复、WHM 下载、IMAP 同步)
- 安全:CrowdSec 白名单 / 警报 / 控制台 / 验证码 + UFW + AppSec 地理封锁 + 每用户出站防火墙 + 恶意软件隔离
- 更新 + 支持选项卡:实时
jabali update配合临时 systemd 单元, 向 webmaster 共享封装加密的诊断信息 - 服务器状态(CPU / 内存 / 磁盘 / 队列 / 5 秒轮询)
- 数据库管理操作(精选调优器、root 密码、processlist、 pmaAdmin SSO)
- 邮件队列、限流、MTA-STS、出站报告
- 审计日志、通知分发器、jabali-isolator 事件
- 通知渠道管理(测试发送、作用域、限流)
用户面板
- 域名、重定向、自定义 nginx 规则、监听 IP、FastCGI 微缓存
- DNS 记录编辑器,具备冲突检测(依据 RFC 1034 §3.6.2 的 CNAME 排他性)和去重功能
- 邮件:域名、邮箱、转发器、自动回复、全捕获(catch-all)、 免责声明(HTML)、共享文件夹、邮件日志
- IMAP 同步(单个 + 批量)
- Webmail SSO(Bulwark, Next.js JMAP)
- WordPress(安装、更新、扫描、SSO)+ 其他 14 个一键应用
- 文件管理器(AntD 原生)+ SFTP + SSH 密钥
- 通过 nspawn 容器访问 SSH shell(空闲超时)
- 数据库(标签页视图中的 MariaDB + Postgres),支持 phpMyAdmin SSO
- 按账户配置 PHP 设置
- SSL 管理
- Cron 任务(systemd-user 定时器 + 允许列表)
- 备份 + 恢复(account_full)
- 日志、统计、带宽使用情况(每日 nginx 日志同步)
- 支持访问链接生成器(一次性 IP 绑定令牌)
- 通知偏好设置(Discord, ntfy, Web Push, SMS, Email)
平台
- 根级代理(
panel-agent),带有类型化的 NDJSON RPC 处理器注册表 - 协调器(60 秒周期)收敛 domain.create / SSL / DKIM / vhosts / PHP 池 / nginx 速率限制 / 邮箱 / mtasts / ssh 密钥 / cron
- 作业队列:异步备份 + 迁移步骤 + WordPress 安装
- 健康监控器,在服务宕机 / 证书即将 过期 / 磁盘已满 / CrowdSec 激增 / 队列深度时触发通知分发
- Redis(Unix 套接字,ACL 作用域)用于缓存、会话、通知 分发器流
- 每域名可选的 FastCGI 微缓存 + 手动清除
- 多语言 UI(默认 en;i18n 框架已就绪)
架构
- 控制平面: Go 二进制文件
panel-api(Gin) 监听/run/jabali-panel/api.sock。嵌入 SPA 并从/提供服务 - 数据平面: 以 root 身份运行的 Go 二进制文件
panel-agent,监听/run/jabali-agent/agent.sock(0660, 组jabali)。类型化 NDJSON RPC 注册表 — 每个特权操作 (nginx reload, certbot, systemctl, mysql DDL, 文件操作) 都是面板按名称调用的命名处理器。面板本身 不执行 shell 命令 - 状态平面: MariaDB
jabali_panel(单数据库, 单写入者 = panel- api)。协调器每 60 秒读取数据库并收敛主机状态 - 任务平面: Redis Streams 调度器 (通知, 备份, 邮件 扫描)
- 前端: React 19 + Ant Design 5 + TanStack Query, 由 Vite 构建, 从 Go 二进制文件的嵌入式文件系统提供服务 — 单一部署单元, 主机上无 Node 运行时
- Webmail: Bulwark (Next.js JMAP) 位于
/opt/jabali-webmail, 通过 nginx → Unix socket 按域名在mail.<tenant>提供服务 - SSH shell: nspawn 容器 (debian-13-v1 镜像) 用于 SSH 访问 隔离; jabali-isolator 处理容器生命周期
- 安全: CrowdSec 解析器 + AppSec (nginx-bouncer Lua, WAF) + 每用户出口防火墙 (nftables + cgroupv2-vmap, ADR-0084) + LMD + ClamAV-on-demand + YARA + Tetragon
- 日志: 通过 slog 输出结构化 JSON; nginx 访问日志馈送 CrowdSec
- 服务器指标: 实时
/proc读取, 无 Prometheus exporter 依赖
服务栈 (单节点默认):
- panel-api (Go, Unix socket, 嵌入式 SPA)
- panel-agent (Go, Unix socket, root)
- nginx (在
:8443上作为 TLS 终结点,用户 vhosts 在:80/:443, 每域邮件 vhost 在:443,共享 FastCGI 缓存 keyzone,AppSec bouncer Lua) - MariaDB (仅 Unix socket —
skip-networking) - Redis (Unix socket, 模式 0660, 组
jabali-sockets) - PowerDNS authoritative (分端口 :5300, MySQL 后端) + pdns-recursor (回环 :53, 解析器链)
- Stalwart Mail Server (SMTP / IMAP / 465 / 587 / 993 / JMAP / ManageSieve, LE 证书推送到 Certificate 对象)
- Bulwark (Next.js JMAP 网页邮件, Unix socket, 按租户提供服务)
- Kratos (Unix socket 管理 + 公共, 唯一认证源 — M20)
- CrowdSec (LAPI socket + AppSec :7422 + nginx-bouncer Lua)
- Restic (加密, 去重, 备份目标)
- jabali-isolator (nspawn 容器生命周期)
- systemd-user (作为用户范围定时器的 cron 任务)
需求
- 全新的 Debian 13 安装(无预装的 Web 或邮件栈)
- 最低 2 GB 内存,推荐 4 GB — 与 cPanel 相同的指导原则(在 ≤4 GB 的主机上,安装程序会增加交换空间并限制本地 SPA/Go 构建内存,以防止 OOM;在 2 GB 上构建较慢但能完成)
- 用于面板 + 邮件的域名(如果托管 DNS 则需包含胶水记录)
- 邮件主机名的 PTR(反向 DNS)
- 开放端口:22, 80, 443, 8443, 25, 465, 587, 993, 995, 53
安全加固
参见 docs/adr/ 获取完整的架构决策记录
(涵盖所有关键设计决策的 110+ 个 ADR)。要点如下:
环境变量
| 变量 | 用途 | 默认值 |
|---|---|---|
JABALI_HOSTNAME | 在安装期间覆盖自动检测的面板主机名 | (auto) |
JABALI_PANEL_BIND | 覆盖 panel-api 监听套接字 | /run/jabali-panel/api.sock |
JABALI_AGENT_SOCKET | 覆盖 agent RPC 套接字 | /run/jabali-agent/agent.sock |
JABALI_TEST_DATABASE_URL | 用于集成测试的真实 MariaDB DSN | (unset) |
JABALI_LOG_LEVEL | Slog 级别 (debug / info / warn / error) | info |
TLS_CERT / TLS_KEY | 更新时从 panel.env 中清理 — nginx 终止 | (auto-cleaned) |
关键安全特性
- Panel 绝不以 root 身份运行。每个特权操作都通过 agent Unix
套接字作为类型化 RPC 调用进行;agent 通过
SO_PEERCRED验证调用者 - Shell 参数按处理器进行验证 + 转义(无
sh -c $arg模式);域名根据validateDomainNameForShell进行验证 - DKIM 私钥 + SSO 令牌 + 邮箱明文在静态时
通过 AES-GCM 使用每主机 SSO 密钥进行加密(
/etc/jabali-panel/sso.key) - 一次性管理员 SSO 令牌为 256 位,单次使用,5 分钟 TTL, 由 systemd 定时器每 30 秒清理一次(ADR-0040 webmail SSO 文件模式)
- Stalwart Certificate 对象从 LE 续期的证书在每次 certbot deploy-hook 时推送 — IMAPS / 465 / 587 始终提供浏览器信任的 证书(无 rcgen 自签名回退)
- CrowdSec AppSec WAF + 每用户出口防火墙(cgroupv2-vmap, ADR-0084)
- 自愈协调器 — 磁盘上的配置漂移在下一个 tick 时回滚;操作员对 nginx vhosts 的手动编辑按设计丢失
- CSP, HSTS, SameSite cookies, X-Forwarded-Proto 由 nginx 处理
- 迁移仅涉及模式(没有由 SQL 填充的应用数据表)
- 每次管理员写入 + 模拟开始/停止时记录审计日志
- Pre-commit + CI 门禁:
go vet,go test -race ./...,npx tsc -b,bash -n install.sh, Playwright E2E, AppSec geoblock 黄金测试
更新
更新面板(代码、依赖项、数据库迁移、基础设施):
jabali update
此操作会拉取最新代码,重新构建面板 + 代理二进制文件,重新构建 SPA,应用 golang-migrate 迁移,同步 nginx vhosts + systemd 单元 + PHP 配置 + CrowdSec acquis,并重启面板 + 代理。 在运行中的服务器上执行是安全的——协调器能够容忍短暂的面板 重启,并在下一个周期收敛状态。
自愈损坏的安装(7 个检测器,--diagnose 默认, --auto 安全,--all --yes 破坏性):
jabali repair --diagnose # report only
jabali repair --auto # fix safe issues
jabali repair --all --yes # destructive recovery
CLI
jabali 命令采用名词:动词模式。所有命令均支持
--json 以获取机器可读的输出,以及 --yes 以跳过确认。
jabali user list|create|delete|show|password|suspend|unsuspend|admin
jabali domain list|create|delete|show|enable|disable|email-enable|email-disable
jabali db list|create|delete|users|user-create|user-delete|tune|root-password
jabali mailbox list|create|delete|passwd|set-quota|forwarder|autoresponder|shares
jabali ssl list|status|check|issue|renew|panel|panel-issue
jabali dns list|records|add|delete-record|sync|dnssec-enable|dnssec-disable
jabali backup list|create|delete|info|restore|password|destinations|schedules
jabali cron list|create|delete|toggle|run
jabali php list|install|uninstall|default|extensions
jabali service list|status|start|stop|restart|enable|disable
jabali system info|status|disk|memory|hostname|kill
jabali wp list|install|delete|update|scan|import
jabali agent ping|status|restart|log
jabali cpanel analyze|restore|fix-permissions
jabali login token [--user=] [--ttl=15] [--panel=]
jabali logs share [--raw] [--ttl=86400]
jabali ufw migrate-ip-bans # M43 CrowdSec single IP-trust
jabali repair --diagnose|--auto|--all
jabali panel-primary set|show # ADR-0048 primary mail domain
jabali nspawn list|build|update|delete
jabali malware-purge # M33 retention sweep
jabali update [--force]
参见 docs/CONVENTIONS.md 了解全仓库范围的
模式(路由族、SearchableTable、Drawer-for-CRUD、列表信封、
速率限制)以及 docs/adr/ 了解所有关键决策。
开发
make build # compile panel-api + panel-agent
make run # run panel-api (dev, embedded SPA)
make test # all Go tests, race detector on
make test-coverage # coverage report (internal packages)
make test-integration # needs JABALI_TEST_DATABASE_URL + real MariaDB
make coverage-check # fail if combined coverage < 80%
make lint # golangci-lint v2
make fmt # go fmt + vet
前端开发(来自 panel-ui/):
npm install
npm run dev # Vite on http://localhost:5173
# proxies /api and /health to 127.0.0.1:8443
E2E(来自 panel-ui/):
npm run test:e2e # Playwright against the dev server
参见 docs/CONTRIBUTING.md 了解完整的特性
开发工作流(研究 → 计划 → TDD → 审查 → 发布)。
版本控制
版本字符串位于 VERSION(在构建时读取并通过
/health 暴露)。当安装程序为全新安装克隆仓库时,
它会读取 VERSION 以显示已安装的版本。始终在包含相应 install.sh 更改的
同一提交中更新 VERSION —— 偏差
会表现为页脚和安装程序横幅不匹配。
翻译
面板 UI 的翻译托管在我们自建的 Weblate 上, 地址为 https://translate.jabali-panel.com/。英语是源语言;所有 其他语言版本均由那里的翻译者编写,并合并回此仓库。
已发布的语言:英语(源语言)、阿拉伯语、简体中文、法语、德语、 希伯来语、意大利语、日语、巴西葡萄牙语、俄语、西班牙语、土耳其语和 乌克兰语。阿拉伯语和希伯来语从右向左显示。
致翻译者
在 translate.jabali-panel.com 注册并开始翻译——无需编程、无需 git、无需本地环境配置。请先阅读该项目的 翻译说明;简而言之:
- 不要翻译产品或协议名称(
nginx、MariaDB、Stalwart、PHP-FPM、DNS、SSL、...)或backticks中的任何内容。 - 保持占位符(如
{{count}})原样不变。你可以为了适应语序而移动 它们,但绝不能重命名或删除。 - 保持字符串简短。大多数是按钮、菜单项、表头和表单 标签,过长的翻译会破坏布局。
- 希伯来语和阿拉伯语从右向左显示。如果界面看起来损坏,请报告问题, 而不是通过缩短文本来规避。
- 如果某个英文字符串含糊不清或错误,请添加评论而不是 猜测——它会在源端一次性修复,适用于所有语言。
缺少你的语言?请提交一个 issue,我们会添加它。
致开发者
panel-ui/src/locales/en/common.json是唯一手动编辑的目录。 Weblate 拥有所有其他语言文件;在那里的手动编辑将被覆盖。- 面向用户的文本通过
t("some.key")(react-i18next)处理,绝不使用 硬编码的字面量。在引入该键的同一提交中,将键及其英文文本添加到en/common.json。 panel-ui/src/i18n.ts包含语言列表以及 AntD 和 dayjs 语言 映射和 RTL 集合。添加一种语言意味着需要修改这三处。- 尚未翻译的键会回退到英文源文本,因此部分 翻译的语言环境永远不会渲染出空白标签。
仓库布局
jabali-panel/
├── panel-api/ # Go HTTP server (Gin) + reconciler + agent RPC client
│ ├── cmd/server/ # main entry
│ ├── internal/ # api/, auth/, repository/, reconciler/, config/, ...
│ └── migrations/ # golang-migrate SQL (000xxx_*.up/.down.sql)
├── panel-agent/ # Go binary running as root; typed NDJSON RPC handlers
│ ├── cmd/jabali-agent/
│ └── internal/commands/
├── panel-ui/ # React SPA (AntD + TanStack Query)
│ ├── src/ # shells/, components/, theme/, pages/, ...
│ └── public/
├── agentwire/ # NDJSON RPC types shared by panel-api + panel-agent
├── internal/ # shared Go libs (cronvalidate, dbtuning, phpext, ...)
├── install/ # install.sh assets (nginx tmpl, stalwart plan,
│ # letsencrypt deploy hooks, bulwark env, ...)
├── docs/ # CONVENTIONS, BLUEPRINT, adr/, runbooks/, KNOWN_ISSUES
├── plans/ # per-milestone implementation blueprints
├── .github/workflows/ # CI (Go + vitest + E2E)
├── install.sh # single-supported install path (curl | sudo bash)
├── config.example.toml # reference config (copied to /etc/jabali-panel/)
├── Makefile # build / test / lint targets
└── go.mod # Go workspace root
许可证
AGPL-3.0 — 参见 LICENSE.
邮件子域名
在浏览器中访问 mail.<your-domain> 会通过每域名的 nginx vhost 路由到 webmail (Bulwark)。该 vhost 安装了一个 nginx sub_filter,
它会将面板主机名重写为 Bulwark 的
/api/config 和 Stalwart 的 /.well-known/jmap 响应中请求的 $host,从而确保 SPA
在 mail.<tenant> 上保持同源,且 Stalwart 的 JMAP Session URL
不会泄露面板主机名。
autodiscover / autoconfig 路径被排除,以便邮件客户端
自动发现(Thunderbird、Outlook)继续正常工作。
文档
参见 docs/ 目录以获取详细指南:
- Conventions — 仓库范围内的模式(路由族、 SearchableTable、用于创建+编辑的 Drawer、图标 shim、列表信封、速率 限制)+ 以惨痛教训学到的反模式
- Blueprint — 完整功能地图 + 里程碑路线图
- ADRs — 所有关键的架构决策(110+)
- Plans — 每个里程碑的实现蓝图
- Runbooks — SSL、邮件、M16 回滚、 M22 SSO 重构、M27 CrowdSec 扩展、M30/M30.1 备份的运维指南
- Known Issues — 注意事项 + 变通方案
- Contributing — 功能开发工作流
- Translations — Weblate 实例;英文
是源目录(
panel-ui/src/locales/en/common.json) - Environment — 完整的环境变量参考