6.2 KiB
6.2 KiB
S29 性能、故障与安全测试报告
测试范围
- 环境:本地 KVM 开发环境,A/B/T 通过 Tailscale 联调。
- 范围:小规模 CPS、并发、录音搬运、CDR/Recording Worker 恢复、Redis/MySQL 短故障、API 越权、重放与 SIP 非法来源/超 CPS 探针。
- 约束:不做长时间满载压测,不删除 S28 验收数据,不禁用 SSH 密钥登录,不提前执行 S30。
执行记录
1. 基线健康检查
- B:
lisglosips@api、lisglosips@cdr-worker、lisglosips@recording-worker、mysql、redis-server、heplify-server、grafana-server、lisglosips-prometheus.service均 active;API/api/v2/health/ready返回ok;Prometheus 实际项目服务lisglosips-prometheus.service返回 ready。 - A:
opensips、rtpengine-daemon、rtpengine-recording-daemon、lisglosips-redis-auth-proxy、lisglosips-recording-finalize.timer均 active。 - T:
lisglosips-s28-uas、opensips均 active。
2. CPS/并发/录音
- 5 路并发呼叫:T 同时发起 5 路 S28 呼叫,均收到
100 Giving it a try、200 OK、BYE200 OK,用时约 11.92 秒;B 端生成 5 条 raw CDR、5 条 rated CDR、5 条 READY 录音,录音大小均为 55758 bytes。 - 12 路短突发:T 同时发起 12 路短持话呼叫,均收到
100 Giving it a try、200 OK、BYE200 OK,用时约 2.13 秒;B 端核对raws=12、rated=12、recordingsReady=12、totalDuration=72、录音大小55758-56078bytes。 - 修复项:T 呼叫脚本原默认固定
--local-port 50621,并发时出现OSError: [Errno 98] Address already in use;已改为默认--local-port 0并在 socket bind 后用实际端口生成 Via/Contact。复测 5 路和 12 路并发均通过。
3. Worker 与数据服务故障
- Recording Worker 故障:停止 B
lisglosips@recording-worker后发起呼叫,通话成功;恢复 worker 后自动搬运并入库录音,recordings.status=READY,大小 55758 bytes。 - CDR Worker 故障:停止 B
lisglosips@cdr-worker后发起呼叫,Redis consumer group 出现lag=1,MySQL 无 raw CDR;恢复 worker 后pending=0、lag=0,raw/rated CDR 正常落库。 - Redis 故障:停止 B
redis-server后,A 返回503 Redis Unavailable,符合保护预期;恢复 Redis 后 A 热路径仍持续返回 503,重启 Alisglosips-redis-auth-proxy无效,重启 Aopensips后恢复,复测呼叫成功。 - MySQL 故障:停止 B
mysql后发起呼叫,SIP 通话成功但 CDR 入库失败。修复后复测:MySQL 恢复并等待 pending idle 窗口后,CDR Worker 自动处理 pending Redis ID,raw/rated CDR 正常入库,无新增 deadletter。
4. Web/API 安全
- 未认证访问
/api/v2/users返回401 AUTH_REQUIRED。 - 质检角色测试用户
usr_s29_quality_only访问/api/v2/users返回403 RBAC_FORBIDDEN。 - 未认证访问录音播放
/api/v2/recordings/:id/play返回401 AUTH_REQUIRED。 - 伪造录音 ID
..etcpasswd通过认证访问播放接口返回404 RECORDING_NOT_READY,未泄露文件内容。 - 充值重放:对
cus_s28_t使用同一idempotencyKey=s29-replay-cus-s28-000001连续 POST 两次0.000001充值,两次返回同一充值 IDrch_da0bba1c7b1d44428ab92d77a55e503d;数据库核对customer_recharges.rows=1、idempotency_keys.rows=1、余额只从99.748000增加到99.748001。
5. SIP 安全探针
- 从 B
100.90.90.91向 A100.90.90.90:15060/udp发送非法来源 INVITE,Call-IDs29-illegal-1782094573-3991@lisglosips-b,客户端等待 3 秒无 SIP 响应;A OpenSIPS 最近日志未出现该 Call-ID。该结果说明非允许来源未进入当前业务路由;因无 OpenSIPS 层日志,保守记录为网络/主机边界静默丢弃证据。
缺陷与处理
| 编号 | 现象 | 处理 | 复测 |
|---|---|---|---|
| S29-D1 | T 呼叫脚本固定本地 UDP 端口,导致并发发起时端口冲突。 | 修改 infra/server-t/s28/lisglosips-s28-sip.py,默认 --local-port 0,bind 后写入实际端口到 Via/Contact,并部署到 T。 |
5 路并发和 12 路短突发均成功。 |
| S29-D2 | MySQL 短故障时 CDR Worker 将可恢复数据库连接错误 deadletter 并 ACK 原 Redis 消息,导致 CDR 不自动重试。 | packages/redis 增加 CdrRetryableError,retryable 错误不 ACK、不 deadletter,并释放幂等锁;worker-cdr 将数据库连接类错误标记为 retryable。 |
停 MySQL 后发起呼叫,恢复 MySQL 并等待 pending idle 窗口后 raw/rated CDR 自动入库,无新增 deadletter。 |
| S29-D3 | 失败 CDR 的长 event_id 超过 raw_cdrs.event_id varchar(64),触发 Prisma 长度错误并 deadletter。 |
worker-cdr 入库前对超过 64 字符的 event_id 做稳定 SHA-256 后缀压缩,原始事件仍保留在 payload 中。 |
本地 Vitest 与 build 通过;B 部署后 MySQL 故障复测无新增长度类 deadletter。 |
| S29-D4 | Redis 恢复后 A 热路径仍返回 503 Redis Unavailable,仅重启 Redis proxy 无法恢复。 |
本次执行运行时恢复:重启 A opensips,恢复呼叫。根因需在后续 OpenSIPS Redis 连接池/故障重连策略中继续排查。 |
重启 A opensips 后 T->A 呼叫恢复成功。 |
| S29-D5 | S29 期间本地/T 到 B 的 Tailscale 管理链路出现短时高延迟/丢包,SSH 偶发超时。 | 未改网络配置;采用短命令、分步执行、服务健康复核。 | 后续 SSH 与 API 健康检查恢复;记录为开发环境网络风险。 |
结论
S29 在本地 KVM 开发环境完成。单 A/S28 最小闭环在 5 路并发和 12 路短突发下能完成 SIP、CDR、计费、录音入库;Recording Worker、CDR Worker、MySQL 短故障的可恢复路径已验证,其中 MySQL 可恢复缺陷已完成代码修复并部署到 B 当前 release。
API 鉴权、RBAC、录音播放未认证保护和充值幂等重放均通过。SIP 非法来源探针未进入业务路由,但缺少 OpenSIPS 层拒绝日志,建议后续补充明确的非法来源日志/指标。
遗留风险:Redis 恢复后 OpenSIPS 热路径仍需重启 opensips 才恢复,当前只有运行时恢复与复测记录,尚未完成根因级修复;多 A、完整生产路由、多 B 高可用不属于 S29 本次范围。