Files
TPSchedule/server/feedback/deployment-safeline-1panel.md
T

153 lines
8.6 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.
# 雷池 WAF + 1Panel:TPSchedule 反馈服务部署
适用环境:Ubuntu,雷池占用80/443;OpenResty为host网络模式,HTTP为10080、HTTPS为12443;雷池现有站点通过127.0.0.1回源。以下是按已知环境制定的操作步骤,尚未在你的服务器执行。
## 访问链路
```text
手机APP:https://app.glowtps.cn/v1/feedback
↓ HTTPS,公网443
雷池WAF:证书和外部HTTPS跳转
↓ http://127.0.0.1:10080,Host仍为app.glowtps.cn
OpenResty:app.glowtps.cn站点
↓ http://127.0.0.1:18080
反馈服务容器:内部8000
↓ smtp.feishu.cn:465,SSL
authors@glowtps.cn → tps-david@glowtps.cn
```
该反馈站点在雷池终止HTTPS,后两段是同机内部HTTP。OpenResty已有12443端口及其他网站保持原设置。反馈容器使用现有Compose配置,宿主机仅发布127.0.0.1:18080,公网安全组无需增加该端口。
## 1. 上传、解压和确认目录
将TPSchedule-feedback-server.zip上传到1Panel文件管理并解压。压缩包内有feedback目录;将这个目录中的文件放到 `/opt/app-feedback`,确保 `/opt/app-feedback/Dockerfile` 和 `/opt/app-feedback/docker-compose.yml` 实际存在。不要形成多套嵌套目录后在错误目录启动。
在1Panel服务器终端查看目标端口是否已被占用:
```sh
ss -ltn '( sport = :18080 )'
```
若18080已有其他服务,不要停止它;告诉开发者占用情况后再协调新端口。以下步骤使用18080。
## 2. 填写服务器配置
在文件管理中将 `.env.example` 复制为 `.env`,保留模板其他参数,核对:
```dotenv
SMTP_HOST=smtp.feishu.cn
SMTP_PORT=465
SMTP_TLS=ssl
SMTP_USERNAME=authors@glowtps.cn
MAIL_FROM=authors@glowtps.cn
MAIL_TO=tps-david@glowtps.cn
DATABASE_PATH=/app/data/feedback.sqlite3
```
SMTP_PASSWORD填写飞书邮箱的第三方客户端授权密码,只在服务器编辑器里填写。IDEMPOTENCY_SECRET填写持久随机密钥,在服务器终端生成:
```sh
openssl rand -hex 32
```
将生成结果填回 `.env` 中的IDEMPOTENCY_SECRET。两个示例REPLACE_占位值均须替换,秘密值建议用英文单引号包住。不要将真实密码或密钥发到聊天里。
## 3. 启动服务并验证第一段
在1Panel服务器终端执行:
```sh
cd /opt/app-feedback
chmod 600 .env
docker compose up -d --build
docker compose ps
curl --fail --max-time 10 http://127.0.0.1:18080/healthz
```
最后返回 `{"status":"ok"}` 才继续。这一步不发送邮件,也不验证飞书密码;它验证容器和本机18080。首次构建需要服务器能下载镜像及Python依赖。启动失败可以查看 `docker compose logs --tail 50 feedback`,分享错误时不要附带.env或完整容器环境详情。
可以改用1Panel“容器→编排”启动,但构建目录须为上述目录,环境文件也在该目录;固定使用同一个Compose项目 `app-feedback`,避免创建多个副本。
## 4. 配置OpenResty的10080站点
在1Panel“网站”中找到或新建 `app.glowtps.cn` 站点。新建可选择反向代理,代理地址填 `http://127.0.0.1:18080`。网站域名绑定app.glowtps.cn,HTTP监听10080;如果主域名字段要求域名加端口,填 `app.glowtps.cn:10080`。最终配置应能看到 `listen 10080;` 和 `server_name app.glowtps.cn;`,以实际生成配置为准。
这个内部HTTP站点关闭“强制HTTPS/HTTP转HTTPS”。外部HTTPS由雷池负责,否则雷池每次HTTP回源都可能再次触发HTTPS重定向。无需给这个新反馈站点另开12443。
先保存该站点原配置。将同目录 `openresty-safeline-http.conf.example` 中的配置加入此站点的HTTP `server { ... }` 内。不要替换OpenResty总配置。若已经有同名精确location,修改原块,保持每个路径只有一个对应块;域名已有页面时保留其他路由。
该片段只信任127.0.0.1/::1传来的真实IP信息。雷池通过X-Forwarded-For透传源IP,经OpenResty校正后再发送给服务,以免所有学生被当成同一个IP、共享每小时10次的限制。不要将可信来源设置成0.0.0.0/0。若现有配置也在此层处理真实IP,先核对并合并,避免重复指令;如果雷池实际连接源地址不是回环地址,应仅补充那个确定的可信地址。
在1Panel执行配置检查,检查成功后重新加载OpenResty。然后在宿主机终端验证:
```sh
curl --fail --max-time 10 -H 'Host: app.glowtps.cn' http://127.0.0.1:10080/healthz
```
同样应返回 `{"status":"ok"}`。到这里已验证服务和OpenResty,雷池尚未参与。
## 5. 配置雷池站点
在雷池中找到或添加app.glowtps.cn防护站点,按以下设置:
| 配置项 | 值 |
|---|---|
| 域名 | app.glowtps.cn |
| 公网HTTP端口 | 80,保持现有入口 |
| 公网HTTPS端口 | 443 |
| 上游服务器 | http://127.0.0.1:10080 |
| 回源Host | app.glowtps.cn,保留原Host或明确填写 |
| HTTPS证书 | 能覆盖app.glowtps.cn的有效证书 |
| 外部HTTP跳转HTTPS | 在雷池设置 |
如果上游地址UI将协议/地址/端口拆开,分别选择HTTP、127.0.0.1、10080。保持路径 `/v1/feedback` 原样转发,不额外增加前缀;上游不要填公网上的app.glowtps.cn,以免请求转回雷池形成循环。证书可使用已有覆盖该域名的通配证书;申请新证书时可选DNS验证,避免绕行到内部站点的HTTP校验问题。
域名A记录应指向43.138.57.216;已有正确记录时沿用。所有公网入口继续经过雷池。
该接口来自原生APP HTTP客户端,无法执行网页JavaScript或拖动验证码。对这个API站点或精确路径 `/v1/feedback`、`/healthz` 避免开启浏览器人机验证、网页登录跳转、动态JS注入和等待室。根据版本提供的站点/路径配置范围操作,保留适用于API的攻击检测及频率限制;不要通过全站攻击白名单解决误拦。
图片、视频请求使用multipart/form-data。雷池此站点应允许约21MiB的HTTP请求体,回源等待时间设为至少60秒(如支持,可与OpenResty设置一致为180秒)。服务自己仍限制最多3个附件、单个8MiB、合计20MiB。雷池HTTP请求体限额不足时会在服务之前返回413;浏览器挑战可能返回HTML甚至200,APP会拒绝把它当作提交成功。
## 6. 验证整条链路及邮件
在服务器执行,或用手机浏览器访问这个完整健康检查地址:
```sh
curl --fail --max-time 15 https://app.glowtps.cn/healthz
```
应直接返回 `{"status":"ok"}`。curl默认校验证书,正常验证时不加 `-k`。根地址 `/` 不是健康检查页面,出现404不能据此判断反馈服务失败。
然后从TPSchedule的“我的→反馈与共建”提交一条“部署测试”,检查 `tps-david@glowtps.cn` 是否收到。若要同时验证重复提交保护,在服务器终端手动发送两次完全相同的测试请求:
```sh
feedback_test_id=$(openssl rand -hex 16)
curl --max-time 30 -i https://app.glowtps.cn/v1/feedback \
-F "submission_id=$feedback_test_id" \
-F 'type=problem' \
-F 'message=TPSchedule 部署测试,请忽略。'
```
保留feedback_test_id,在同一终端再次执行这条curl(不重新生成ID)。两次应确认相同ID的sent结果,邮箱只收到一封。success:true/status:sent表示SMTP接受并记入数据库,实际进入收件箱需你核对。
## 分层排查
| 哪一段失败 | 优先检查 |
|---|---|
| 本机18080/healthz失败 | Compose状态、构建、.env必填配置、容器日志 |
| 18080正常,10080加Host失败 | 站点监听10080、域名匹配、精确location、代理地址 |
| 两个本机地址正常,公网失败 | DNS、雷池证书/域名/上游/Host、人机验证、WAF记录 |
| healthz正常,POST被拦或返回HTML | 该接口的雷池挑战、攻击拦截、上传限制 |
| POST返回smtp_unavailable | 飞书客户端授权、账号、服务器出站465、发信权限 |
| POST返回429 | 测试频率、真实IP链路;服务默认每IP每小时10次/全局100次 |
| 返回delivery_uncertain | 先查邮件及投递状态,用原ID核对,不生成新ID强行重发 |
更新时保持.env、原IDEMPOTENCY_SECRET和SQLite数据卷,继续同一Compose项目。不要执行 `docker compose down -v`,它会移除重复提交记录。
## 官方参考
- [1Panel创建反向代理网站](https://docs.fit2cloud.com/1panel/user_manual/websites/website-create/)
- [雷池常见问题:上游源IP透传](https://help.waf-ce.chaitin.cn/node/01975855-cd7d-78fb-a354-d2aa65a53cce)
- [雷池人机验证使用浏览器环境](https://help.waf-ce.chaitin.cn/node/01973fc6-e1d8-7af4-b697-ab1312b8f9bd)
- [Nginx真实IP模块及可信来源](https://nginx.org/en/docs/http/ngx_http_realip_module.html)