Files
2026-09-27 20:09:10 +08:00

156 lines
6.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 宿主机部署说明
HookPilot 直接部署在 Linux 宿主机上,由 systemd 管理,通过 HTTPS 反向代理对外提供服务。脚本以服务账户身份运行,可以操作该账户有权访问的文件和命令。
## 1. 环境要求
- Linux(systemd),x86_64 或 arm64
- Node.js **22.13 及以上**(使用内置的 `node:sqlite`,无需编译原生模块)
- Bash
- Nginx 或 Caddy(提供 HTTPS)
```bash
node -v # 应输出 v22.13.0 或更高
```
## 2. 构建
可以在服务器上直接构建,也可以在其他机器构建后复制整个目录。
```bash
git clone <仓库地址> /opt/hookpilot # 或将项目目录上传到 /opt/hookpilot
cd /opt/hookpilot
npm ci
npm run build # 构建前端(web/dist)与后端(server/dist)
npm prune --omit=dev # 可选:移除开发依赖
```
## 3. 创建服务账户与目录
默认使用专用账户 `hookpilot`,数据目录 `/var/lib/hookpilot`:
```bash
sudo useradd --system --home-dir /var/lib/hookpilot --create-home --shell /usr/sbin/nologin hookpilot
sudo chmod 700 /var/lib/hookpilot
sudo chown -R root:root /opt/hookpilot # 程序目录只读,服务账户无需写权限
```
数据目录结构(服务启动时自动创建):
| 路径 | 内容 |
|---|---|
| `hookpilot.db` | SQLite 数据库:管理员、会话、接口、脚本版本、任务与输出、系统设置 |
| `scripts/<接口ID>/v<版本>.sh` | 各脚本版本的文件,执行时由数据库内容校验并重写 |
| `runs/<任务ID>/request.json` | 任务执行期间的请求文件(`HOOK_REQUEST_FILE`),结束后删除 |
| `workspace/` | 未配置工作目录时的默认工作目录 |
脚本需要访问的其他目录,请用常规的文件权限授予 `hookpilot` 账户,例如:
```bash
sudo chown -R hookpilot:hookpilot /srv/my-app
```
## 4. 配置
```bash
sudo mkdir -p /etc/hookpilot
sudo cp deploy/hookpilot.env.example /etc/hookpilot/hookpilot.env
sudo chmod 600 /etc/hookpilot/hookpilot.env
sudo nano /etc/hookpilot/hookpilot.env
```
建议设置 `HOOKPILOT_ADMIN_PASSWORD` 作为初始密码(只在首次启动、数据库中还没有管理员时生效)。未设置时会随机生成,并打印在服务日志中。
## 5. 安装 systemd 服务
```bash
sudo cp deploy/hookpilot.service /etc/systemd/system/hookpilot.service
# 如果 node 不在 /usr/bin/node,请修改 ExecStart(可用 `which node` 查看)
sudo systemctl daemon-reload
sudo systemctl enable --now hookpilot
sudo systemctl status hookpilot
journalctl -u hookpilot -f # 查看日志,未设置初始密码时在这里找到随机密码
```
服务停止或重启时:
- 运行中的任务会被终止(整个进程组),状态记为“中断”,**不会自动重跑**;
- 排队中的任务保留在数据库中,服务启动后按原顺序继续执行;
- 服务异常退出(如被 `kill -9`、断电)后再启动,上次运行中的任务同样标记为“中断”。
## 6. 配置 HTTPS 反向代理
服务默认只监听 `127.0.0.1:8787`,由反向代理提供统一域名和 HTTPS。
- Nginx:参考 [`deploy/nginx.conf.example`](../deploy/nginx.conf.example)
- Caddy:参考 [`deploy/Caddyfile.example`](../deploy/Caddyfile.example)(自动申请证书)
注意事项:
1. **同步模式**的接口会等脚本执行完才响应,代理的读取超时要大于最长脚本超时加排队时间。
2. 代理的请求体上限应不小于 `HOOKPILOT_BODY_LIMIT`(默认 1 MiB)。
3. 代理与服务不在同一台机器时,把 `HOOKPILOT_TRUST_PROXY` 设置为代理的 IP,否则执行记录中的请求来源会显示为代理地址。
4. 使用 HTTPS 访问时登录 Cookie 自动带 `Secure` 标记(`HOOKPILOT_COOKIE_SECURE=auto`)。
## 7. 按命令授予提权
脚本默认只有 `hookpilot` 账户的权限。确实需要重启系统服务或修改受保护文件时,由部署者在 sudoers 中**按具体命令**授权,不要授予全部权限:
```bash
sudo visudo -f /etc/sudoers.d/hookpilot
```
```text
# 只允许无密码执行这两条命令
hookpilot ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
hookpilot ALL=(root) NOPASSWD: /usr/bin/systemctl reload caddy
```
脚本中使用 `sudo -n`(不交互询问密码,未授权时立即失败):
```bash
sudo -n /usr/bin/systemctl restart nginx
```
平台页面不提供切换系统用户的功能。启用提权后请保持 systemd 单元中 `NoNewPrivileges` 处于关闭状态。
## 8. 安全要点
- 管理端使用登录会话,Webhook 使用每个接口独立的 Token,两种凭证互不通用;Token 只保存哈希。
- 执行子进程使用显式构造的环境(`PATH`、`HOME`、`USER`、`LANG` 与 `HOOK_` 变量),不继承服务的环境变量与凭证。
- 请求数据只通过环境变量和请求文件传给脚本,不会替换进脚本文本或拼接成 Shell 命令。脚本中请始终用双引号引用变量(`"$HOOK_IP"`)。
- 平台面向可信管理员:任何能登录管理界面的人都能以服务账户身份执行任意命令,请使用强密码并限制管理界面的访问来源(如在代理层对 `/` 做 IP 白名单、仅放行 `/hooks/`)。
- 连续登录失败 10 次后,该来源 IP 锁定 15 分钟。
## 9. 升级、备份与维护
**升级:**
```bash
cd /opt/hookpilot
git pull # 或替换为新版本文件
npm ci && npm run build
sudo systemctl restart hookpilot
```
数据库结构变更会在启动时自动迁移。
**备份:** 停止服务后复制整个数据目录;或在运行中使用 SQLite 在线备份:
```bash
sudo -u hookpilot sqlite3 /var/lib/hookpilot/hookpilot.db ".backup '/var/backups/hookpilot.db'"
```
**忘记管理员密码:**
```bash
cd /opt/hookpilot
sudo -u hookpilot env HOOKPILOT_DATA_DIR=/var/lib/hookpilot node server/dist/cli.js reset-password
# 或指定新密码:
sudo -u hookpilot env HOOKPILOT_DATA_DIR=/var/lib/hookpilot HOOKPILOT_NEW_PASSWORD='新密码' node server/dist/cli.js reset-password
```
重置后所有登录会话失效。
**日志清理:** 超过“日志保留天数”(默认 30 天)的已结束执行记录及其输出每小时自动清理一次,可在“系统设置”中调整。