101 lines
3.6 KiB
Nginx Configuration File
Raw Normal View History

fix(security)+fix(api)+docs: 越权面修复、登录门控与状态语义对齐、文档全面校对 - 越权面(全部 before/after 实测可复现,10 条):cabinets/filter 的 project_id/organization_id 参数分支越权(客户可拿对方向柜全字段)、organization-tree 返回全量组织树、 cabin-boards/compartments/cabinets 三个 options 无归属校验、batteries 无归属(改为经 charge_orders→cabinets→projects 间接归属)、feedback 跨组织读且能改他人状态、 GET /downloads/{id} 漏 user_id 过滤(跨用户元数据泄露)、 POST /downloads/cleanup 全局删除所有组织导出(改客户侧只清自己)、 /api/nodes/* 仅需登录(客户可读内网 IP 并注销节点)→ 收紧 role_level>=2; /api/devices/stats 同样收紧;新增共享闸门 auth::require_super_admin - TCP 登录门控:下行投递改为"登录前白名单"(PRE_LOGIN_ALLOWED_ACTS),与上行同一规则 - 60 秒超时对齐协议:挂起项 TTL 120→60 + 值内嵌 deadline 精确判窗 + 超时 WARN(兼容老格式) - 下发前在线判据改连接级认证标记 device:authed:{imei}(消除"管理端显示成功、设备收不到") ⚠️ device-server 与 api-server 必须同批发布 - 协议对齐:auth_str 登记校验提到限流之前、build_spec Topology 分支 clamp(15/8)、 行号引用改小节引用、修 3 处错引小节 - 状态语义:compartments.status 只表占用,充满/故障由通道状态派生;柜级 status1/status2 与开门按钮(柜级独立动作不联动);列注释对齐(cabinets.status 补 2=禁用、 charge_orders.status 标注 2 保留未使用) - 假绿单测修复:超时 SQL 抽常量 + 纯函数孪生体双向断言(方向写反必红) - RSSI 两端统一为同源纯函数 5 档(小程序显示随之变化)+ test/rssi_signal.test.mjs - nginx:扫码入口 abstract 限流(zone=scan_abstract_zone 30r/m burst=20)+ 主配置仓库镜像 - 文档:AGENTS.md 新增「踩过的坑」7 条教训 + 部署注意事项 11 条 + 文档口径优先级; docs/API.md 接口总表 80 条与 mod.rs 逐条一致、Redis 契约按代码重写、9 处事实错误修正; 权威协议文档 v1.1.1(协议行为未变,仅修订文档自身的错误/矛盾/缺失)
2026-09-20 03:23:00 +08:00
# For more information on configuration, see:
worker_shutdown_timeout 30s;
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
# ==== 安全加固:按客户端 IP 限流 zone(必须位于 http 上下文)====
# ⛔ 本文件是线上 /etc/nginx/nginx.conf 的仓库镜像(与线上逐字节一致;改动后请双向核对 md5)。
# ⛔ 三条硬性约束(任一条违反都会让整站起不来或行为异常,历史已各踩过一次):
# 1. limit_req_zone 必须写在 http{} 内 —— conf.d/*.conf 的 location 引用了这些 zone,
# 引用未定义 zone 会让 nginx -t 直接失败;
fix(test)+fix(deploy)+docs: 探针状态语义反转修复、退出码补齐、文档校订 1) probe_export.py:任务状态语义**搞反了**(导出成功也会被判失败) - download_tasks.status 的权威语义是 0 排队 / 1 处理中 / **2 完成** / 3 失败 (db/migrate.rs:225 列注释;写入侧 charge_records.rs::mark_task_completed 写的就是 2)。 - 该脚本此前**等 status == 1(处理中)当成功、把 status == 2(完成)判成"导出失败"** —— 典型假红。已按权威语义订正,并与另外两处**本来就正确**的口径对齐: web/src/pages/charge-records/DownloadCenter.tsx::statusText、 test/schema_alignment_e2e.py(等 status in (2,3) 再断言 == 2)。 - 同批修正退出码分类:账号缺 charge_record:export(403)由 exit 1 改为 **exit 2「未验证」** —— 本项要验证的是"降权成 pms 后能否往 exports/ 落盘", 没权限根本走不到那一步,报 1 是**把夹具缺陷说成产品缺陷**(误报与假绿同样有害)。 - 允许用 PMS_PROBE_PHONE / PMS_PROBE_PASSWORD 换成有权限的账号。 - **C 项结论(由落盘物证直接判定,不依赖该脚本)**:测试环境 exports/充电记录__-.xlsx 5559 B、生产同类产物 5457 B,属主均为 pms:pms; 两环境 exports/uploads 目录均 pms:pms,两 api-server 单元均 User=pms/Group=pms ⇒ 降权进程确实能落盘。已在 README 写明并给出取证依据。 2) deploy-prod.ps1 的退出码(此前失败的探针只打印 ❌ 就继续,脚本仍以 0 退出) - 消费链路探测失败(exit 1)现在**记入失败清单**并以 exit 1 收尾; "未验证"(隧道没起)默认仍不计失败(环境问题会误报), 但新增 **-StrictVerify** 开关供 CI/无人值守使用,打开即 exit 1。 - 好处:把这条从"打印了 ❌ 却 exit 0"(仓库第 8 条假绿)里摘出来。 3) 统一退出码 0/1/2 落地到本批脚本,并对每个失败路径实测取证: - test/tcp_e2e_smoke.py:13/13 通过(含未登录业务报文被静默丢弃);先复位夹具再跑。 - test/security-hardening/ 新增 verdict.py 判据容器(未跟踪 → 本次入库); 4 个探针全部按失败→exit 1 改造,并各实测过失败路径。 - 实测结果(2026-09-20 发布后回归):smoke_prod 4/4 exit 0、 probe_prod 5/5 exit 0、probe_upload 6/6 exit 0。 4) 工具与文档 - tools/db.py 默认库由**生产 pms 改为测试 pms_dev**(可用 PMS_MYSQL_DB 覆盖, 切生产时打印醒目警告)—— "以为在测测试库、实际打生产"的隐患。 - tools/db_schema_diff.py:连不上必须是 exit 2,绝不伪装成"无漂移"的 0。 - 新增 tools/dev-tunnel.ps1(入库,SSH 隧道;口令走 PMS_SSH_PASSWORD 环境变量), 配套 tools/dev-tunnel-run.ps1 自愈启动器**保持 gitignore**(内含口令)。 - deploy/nginx/nginx.conf 镜像随线上同步(移除 stream 转发;与线上逐字节一致)。 - AGENTS.md / README.md:补「踩过的坑」第 7/8 条(掐断上游管道会杀被测进程、 打印 ❌ 却 exit 0 是假绿)、退出码约定、本地开发改 SSH 隧道、 在线判据口径订正、order_timeout 守门补齐、小程序 vue-tsc 门禁与其抓到的两个真 BUG。 发布验收(本次统一发布,按 device→api 顺序): - 测试环境与生产环境四服务 active,端口 9000/9001/3000/3001 正确,ERROR 均为 0; - 生产迁移按判据 grep '更新列注释' 实测对齐 cabinets.status 列注释; - 结构漂移 python tools/db_schema_diff.py = 0 处漂移 / exit 0(--vs-ddl 两库同样 0); - schema_alignment_e2e --env dev 全部通过 exit 0; - 生产域名 /api/pms/cabinets -> 401、/pms/ -> 200;消费链路探测通过。
2026-09-20 04:31:42 +08:00
# 2. ⛔ stream 转发已移除(2026-09-20):原 MySQL 3306 / Redis 6379 反代(本文件下方那个)
# 已整块删除,服务器不再监听库端口;开发机连库走 SSH 隧道(tools/dev-tunnel.ps1)。
# 不要在**本文件或 conf.d/*.conf** 里重新加 stream{}(全配置只能有一个,
# 写重复会报 stream directive is duplicate,整站不可用)。
fix(security)+fix(api)+docs: 越权面修复、登录门控与状态语义对齐、文档全面校对 - 越权面(全部 before/after 实测可复现,10 条):cabinets/filter 的 project_id/organization_id 参数分支越权(客户可拿对方向柜全字段)、organization-tree 返回全量组织树、 cabin-boards/compartments/cabinets 三个 options 无归属校验、batteries 无归属(改为经 charge_orders→cabinets→projects 间接归属)、feedback 跨组织读且能改他人状态、 GET /downloads/{id} 漏 user_id 过滤(跨用户元数据泄露)、 POST /downloads/cleanup 全局删除所有组织导出(改客户侧只清自己)、 /api/nodes/* 仅需登录(客户可读内网 IP 并注销节点)→ 收紧 role_level>=2; /api/devices/stats 同样收紧;新增共享闸门 auth::require_super_admin - TCP 登录门控:下行投递改为"登录前白名单"(PRE_LOGIN_ALLOWED_ACTS),与上行同一规则 - 60 秒超时对齐协议:挂起项 TTL 120→60 + 值内嵌 deadline 精确判窗 + 超时 WARN(兼容老格式) - 下发前在线判据改连接级认证标记 device:authed:{imei}(消除"管理端显示成功、设备收不到") ⚠️ device-server 与 api-server 必须同批发布 - 协议对齐:auth_str 登记校验提到限流之前、build_spec Topology 分支 clamp(15/8)、 行号引用改小节引用、修 3 处错引小节 - 状态语义:compartments.status 只表占用,充满/故障由通道状态派生;柜级 status1/status2 与开门按钮(柜级独立动作不联动);列注释对齐(cabinets.status 补 2=禁用、 charge_orders.status 标注 2 保留未使用) - 假绿单测修复:超时 SQL 抽常量 + 纯函数孪生体双向断言(方向写反必红) - RSSI 两端统一为同源纯函数 5 档(小程序显示随之变化)+ test/rssi_signal.test.mjs - nginx:扫码入口 abstract 限流(zone=scan_abstract_zone 30r/m burst=20)+ 主配置仓库镜像 - 文档:AGENTS.md 新增「踩过的坑」7 条教训 + 部署注意事项 11 条 + 文档口径优先级; docs/API.md 接口总表 80 条与 mod.rs 逐条一致、Redis 契约按代码重写、9 处事实错误修正; 权威协议文档 v1.1.1(协议行为未变,仅修订文档自身的错误/矛盾/缺失)
2026-09-20 03:23:00 +08:00
# 3. worker_shutdown_timeout 30s 不要调短 —— 调短会中断正在传输的响应。
limit_req_zone $binary_remote_addr zone=auth_login_zone:10m rate=10r/m;
limit_req_zone $binary_remote_addr zone=auth_challenge_zone:10m rate=20r/m;
limit_req_zone $binary_remote_addr zone=uploads_zone:10m rate=120r/m;
limit_req_zone $binary_remote_addr zone=scan_abstract_zone:10m rate=30r/m;
limit_req_status 429;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
server {
listen 80;
listen [::]:80;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
# Settings for a TLS enabled server.
#
# server {
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
# server_name _;
# root /usr/share/nginx/html;
#
# ssl_certificate "/etc/pki/nginx/server.crt";
# ssl_certificate_key "/etc/pki/nginx/private/server.key";
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 10m;
# ssl_ciphers PROFILE=SYSTEM;
# ssl_prefer_server_ciphers on;
#
# # Load configuration files for the default server block.
# include /etc/nginx/default.d/*.conf;
#
# error_page 404 /404.html;
# location = /40x.html {
# }
#
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# }
# }
}