Initial LisgloSIPS V2 implementation
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
# Server B Web 运行时 Runbook
|
||||
|
||||
本文记录 S05 在 Server B 上建立的 Node.js、pnpm、Nginx、systemd 与开发 TLS 基线。当前环境是本地 KVM 开发环境,不是阿里云生产环境。
|
||||
|
||||
## 1. 固定版本
|
||||
|
||||
| 组件 | 版本 | 管理方式 |
|
||||
| --- | --- | --- |
|
||||
| Node.js | `v22.22.2` / Debian 包 `22.22.2-1nodesource1` | NodeSource 22.x,`apt-mark hold` |
|
||||
| Corepack | `0.34.6` | Node 全局工具 |
|
||||
| pnpm | `10.33.0` | 全局固定版本;项目应再用 `packageManager` 固定 |
|
||||
| Nginx | `1.24.0-2ubuntu7.12` | Ubuntu 24.04 security/updates,`apt-mark hold` |
|
||||
|
||||
Node.js 22 当前属于 LTS 线。固定包在升级前必须先核对 Node/Nginx 安全公告、NestJS/Prisma 兼容性并完成回归测试。
|
||||
|
||||
版本记录:`/etc/lisglosips/web-runtime-versions.env`。
|
||||
|
||||
## 2. 目录与发布模型
|
||||
|
||||
```text
|
||||
/opt/lisglosips/
|
||||
├── releases/
|
||||
│ └── s05-placeholder-20260620/
|
||||
│ ├── public/index.html
|
||||
│ └── server.mjs
|
||||
└── current -> /opt/lisglosips/releases/s05-placeholder-20260620
|
||||
```
|
||||
|
||||
- 发布目录由 `root:lisglosips` 持有,程序只读。
|
||||
- `current` 必须是指向完整 release 的符号链接。
|
||||
- 正式发布先写入新 release,完成校验后再原子切换 `current`。
|
||||
- 不允许直接在 `current` 指向的 release 内在线修改代码。
|
||||
- systemd 可写状态目录为 `/var/lib/lisglosips`,运行目录为 `/run/lisglosips`。
|
||||
|
||||
## 3. systemd 服务模板
|
||||
|
||||
模板:`/etc/systemd/system/lisglosips@.service`。
|
||||
|
||||
实例环境文件:`/etc/lisglosips/<实例名>.env`,权限必须为 `0640 root:lisglosips`。当前实例:
|
||||
|
||||
```text
|
||||
lisglosips@api.service -> /etc/lisglosips/api.env
|
||||
```
|
||||
|
||||
模板默认使用 `lisglosips` 系统用户,启用只读系统、私有临时目录、空 capability、设备隔离和内核保护。后续 Worker 如需写录音目录,应使用实例 drop-in 精确增加 `ReadWritePaths`,不要放宽整个模板。
|
||||
|
||||
常用命令:
|
||||
|
||||
```bash
|
||||
sudo systemctl status lisglosips@api
|
||||
sudo systemctl restart lisglosips@api
|
||||
sudo journalctl -u lisglosips@api -n 100 --no-pager
|
||||
sudo systemd-analyze security lisglosips@api.service
|
||||
```
|
||||
|
||||
## 4. Nginx 路由
|
||||
|
||||
| 路径 | 行为 |
|
||||
| --- | --- |
|
||||
| `/` | `/opt/lisglosips/current/public` 静态站点,支持 SPA fallback |
|
||||
| `/healthz` | 反向代理到 `127.0.0.1:3000` |
|
||||
| `/api/` | API 代理,单 IP `20 r/s`、burst 40 |
|
||||
| `/api/auth/login` | 登录专用限制,单 IP `5 r/min`、burst 3 |
|
||||
| `/_recordings/` | 仅供 `X-Accel-Redirect` 内部访问,外部请求返回 404 |
|
||||
|
||||
关键文件:
|
||||
|
||||
```text
|
||||
/etc/nginx/conf.d/lisglosips-global.conf
|
||||
/etc/nginx/sites-available/lisglosips.conf
|
||||
/etc/nginx/snippets/lisglosips-proxy.conf
|
||||
/etc/nginx/snippets/lisglosips-security-headers.conf
|
||||
/etc/nginx/snippets/lisglosips-tls.conf
|
||||
```
|
||||
|
||||
应用仅监听 `127.0.0.1:3000`。Nginx 监听 80/443,但 nftables 仅允许已登记管理端访问 443;开发阶段没有对外开放 80。
|
||||
|
||||
配置变更:
|
||||
|
||||
```bash
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## 5. 开发 TLS
|
||||
|
||||
开发 CA 和证书位于:
|
||||
|
||||
```text
|
||||
/etc/lisglosips/pki/ca/lisglosips-dev-ca.crt
|
||||
/etc/lisglosips/pki/ca/lisglosips-dev-ca.key
|
||||
/etc/lisglosips/pki/certs/server.crt
|
||||
/etc/lisglosips/pki/private/server.key
|
||||
```
|
||||
|
||||
CA 私钥为 `0600 root:root`,服务器私钥为 `0640 root:www-data`。证书 SAN 包含 `lisglosips.local`、`yanzi`、`100.90.90.91` 和 `127.0.0.1`。
|
||||
|
||||
重新签发开发证书:
|
||||
|
||||
```bash
|
||||
sudo /usr/local/sbin/lisglosips-issue-dev-cert
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
本机公有 CA 副本保存在 `.codex-private/tls/lisglosips-dev-ca.crt`,不得把 CA 私钥复制出 Server B。Windows 使用私有 CA 测试时,因开发 CA 不发布 CRL,curl 需附加 `--ssl-no-revoke`;这不会关闭证书链、主机名或签名校验。
|
||||
|
||||
迁移阿里云后必须替换为正式域名证书,并完成:
|
||||
|
||||
1. 把证书和私钥放入受控 Secret 路径。
|
||||
2. 更新 `lisglosips-tls.conf`。
|
||||
3. 将 HSTS 从开发值 `300` 调整为生产值,建议先观察再提升至 `31536000`。
|
||||
4. 验证自动续期、续期失败告警和回滚证书。
|
||||
5. 开放 80 仅用于 ACME/跳转,或使用 DNS-01 后继续关闭 80。
|
||||
|
||||
## 6. 健康检查
|
||||
|
||||
```bash
|
||||
curl --fail http://127.0.0.1:3000/healthz
|
||||
curl --fail --cacert /etc/lisglosips/pki/ca/lisglosips-dev-ca.crt \
|
||||
https://127.0.0.1/healthz
|
||||
sudo ss -lntp | grep -E ':(80|443|3000)\b'
|
||||
sudo systemctl --failed
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
- `/healthz` 返回 `status=ok`。
|
||||
- 3000 仅出现在 `127.0.0.1`。
|
||||
- 443 由 Nginx 监听。
|
||||
- 首页响应包含 HSTS、CSP、X-Frame-Options、X-Content-Type-Options 和 Permissions-Policy。
|
||||
- TLS 1.2/1.3 可用,TLS 1.1 被拒绝。
|
||||
|
||||
## 7. 新版本发布
|
||||
|
||||
```bash
|
||||
release="2026xxxxTxxxxxxZ"
|
||||
sudo install -d -o root -g lisglosips -m 0750 "/opt/lisglosips/releases/$release"
|
||||
# 将已经构建和校验的产物放入新 release。
|
||||
sudo chown -R root:lisglosips "/opt/lisglosips/releases/$release"
|
||||
sudo find "/opt/lisglosips/releases/$release" -type d -exec chmod 0750 {} +
|
||||
sudo find "/opt/lisglosips/releases/$release" -type f -exec chmod 0640 {} +
|
||||
sudo ln -sfn "/opt/lisglosips/releases/$release" /opt/lisglosips/current
|
||||
sudo systemctl restart lisglosips@api
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
发布后必须检查内部健康、HTTPS 健康、日志、应用端口监听和前端静态文件。数据库迁移从 S08 起必须在切换 `current` 前按迁移 Runbook 执行。
|
||||
|
||||
## 8. 回滚
|
||||
|
||||
应用回滚:把 `current` 指回上一 release,重启应用并检查 HTTPS 健康。
|
||||
|
||||
```bash
|
||||
sudo ln -sfn /opt/lisglosips/releases/<previous> /opt/lisglosips/current
|
||||
sudo systemctl restart lisglosips@api
|
||||
```
|
||||
|
||||
S05 变更前备份:
|
||||
|
||||
```text
|
||||
/var/backups/lisglosips-s05/20260620T123432Z
|
||||
```
|
||||
|
||||
其中 `pre-change.tar.gz` 包含变更前应用与配置,`SHA256SUMS` 用于恢复前校验。恢复 Nginx/systemd 配置后必须执行 `nginx -t`、`systemctl daemon-reload`,再重启对应服务。卸载 Nginx或解除包固定不是常规回滚动作,只有确认恢复目标需要时才执行。
|
||||
|
||||
## 9. 已知事项
|
||||
|
||||
- NodeSource 仓库在本地代理链路上偶尔出现 TLS 握手中断;现有索引和已安装包可用。升级前必须先确认仓库恢复并校验候选版本。
|
||||
- 当前证书仅供开发,不能用于阿里云生产。
|
||||
- 80 已监听但被 nftables 拒绝;生产是否开放取决于证书签发方案。
|
||||
- 当前 API 是 S05 无依赖占位服务,S07 后端骨架完成后替换。
|
||||
|
||||
Reference in New Issue
Block a user