跳转至

扫描端远程诊断

诊断使用现有服务端 HTTPS 地址、运行 Token 和心跳,无需扫描端监听端口。服务端协议、配置与 API 详见配套服务端仓库 docs/SCANNER_DIAGNOSTICS.md;先升级服务端,再升级扫描端和前端。

日志目录和启动

scanner-rs --config /etc/network-mapping-scanner/config.toml \
  --log-directory /var/log/network-mapping-scanner \
  --database /var/lib/network-mapping-scanner/scanner-state.sqlite3

父目录应事先由运行账户创建并授予写权限。日志默认是启动工作目录下 ./logs/scanner.log.YYYY-MM-DD,按 UTC 天轮转,保留最近 14 份同格式文件;相对 --log-directory 仍以工作目录为基准,启动时解析为绝对目录。Unix 日志目录设为 0700。文件日志与终端日志均保留;--debug 或 RUST_LOG 会影响事件过滤,关闭 WARN/ERROR 会减少可识别的远程错误。

服务端只能选择清单中的文件身份,不能指定其他路径、命令或 URL。只允许真实日期格式的 scanner.log.YYYY-MM-DD 普通文件;Unix 以固定目录 fd 使用 openat(O_NOFOLLOW),拒绝软链接、子目录和非普通文件。清单最多最近 128 份,因此手工保留的合规旧文件也可被列出。

采集处理时复制已完成的日志行到独立快照,保存捕获时间、源文件大小/修改时间、尾部未完成字节数和 SHA-256。追加写入不影响快照;换文件或截短会导致失败。单源文件超过 256 MiB 将拒绝采集,应控制本地日志规模。此版本仅按天轮转,没有按大小拆分每日日志。

本地持久化文件

根据 --database 路径替换扩展名创建文件。例如默认路径 ./scanner-state.sqlite3 对应:

路径 内容
scanner-state.diagnostics.sqlite3 事件 outbox、诊断任务、manifest、源清单、正常退出标记
scanner-state.diagnostics.lock SQLite 独占进程锁
scanner-state.diagnostics-spool/ UUID.snapshot 和临时 UUID.tmp

诊断数据库独立于扫描身份和结果数据库,事件按 (server_origin,scanner_id) 隔离,不将旧服务端或旧身份的积压事件发送给新身份。Unix 数据库和快照设为 0600,spool 目录设为 0700。多个扫描器进程必须分别使用不同的 --database;独占锁避免同时消费相同诊断队列。

任务与 manifest 在传输前持久化。每个任务最多 16 个源文件、1 GiB 完整行内容;spool 在创建一份文件后检查总量,超过 1 GiB 拒绝该任务并清理快照,单份复制期间可能临时多占用至多 256 MiB。成功、失败或过期任务对应的快照会清理;启动和工作循环清理没有未完成 manifest 引用的快照及临时文件。清理使用数据库中所有身份的未完成 manifest,保留尚未过期的其他身份快照。

cache clear 只处理原有扫描结果缓存,不删除诊断 SQLite 和队列。诊断已完成任务元数据保留约一天,未确认事件最多保留 90 天;清理在工作循环执行,不延长服务端采集任务的 30 分钟期限。

错误捕获与重试

文件日志 formatter 为 WARN/ERROR 添加 event_id,所有日志带当前 boot_id;同一 event_id 同时用于本地日志和实时上传。只捕获允许的诊断字段,并从当前 task span 补充关联任务 ID;不会上传任意配置对象。诊断模块自身的网络重试日志不递归进入 outbox。

日志写入线程把事件放入容量 1024 的内存通道,专用线程写独立 SQLite(synchronous=FULL)。持久化 outbox 最多 4096 条;满时优先淘汰最旧 WARN,无法腾出空间则丢弃新事件。内存队列满或持久化失败也计入 dropped_events;心跳报告本次运行累计丢弃数和 degraded 状态,管理页可提示采集原始日志补充证据。事件出队前已持久化,但进程突然退出可能丢失尚在内存通道中的事件,不能承诺零丢失。

事件 worker 每批读取最多 50 条,只在服务端宣布启用诊断且连接状态为 Online 时发送。收到 confirmed[] 后才删除对应事件。目录/日志任务由另一 worker 执行,独立于心跳续租和结果上传;按 4 MiB 查询已确认块并发送缺失块,服务端最终校验完整文件摘要。网络/超时、408、429、5xx 或暂时缺块使用 3–60 秒带抖动退避,尊重 Retry-After;已经关闭的任务停止传输。普通文件和协议错误报告采集失败。

旧服务端缺少诊断字段时默认关闭事件上传,不循环访问不存在的接口。断线、维护或 Token 失效时本地已持久化事件保留;身份恢复且服务端重新允许诊断后继续上传。采集任务到期会终止,需管理员重新创建。

启动和退出异常

已确定身份并初始化诊断后,masscan/zgrab2/Nuclei 路径或模板启动检查失败会报告 scanner_engine_validation_failed,设置 accepting_tasks=false,保留心跳、目录和日志采集。修复配置后重启扫描器即可重新校验。诊断 worker 意外退出会触发停止信号,避免继续领取任务而没有诊断消费者。

panic hook 和最终 fatal 错误会写结构化日志;main 持有日志 guard 到最终错误写完。正常退出尽量在 5 秒内刷新事件捕获通道,取消未完成 HTTP 传输但保留任务/快照,分别等待两个后台 worker 最多 5 秒,再标记正常退出。下次启动可根据数据库标记报告 unclean_restart。成功启动会报告 ScannerOperationRecovered/startup,通信操作恢复成功也产生恢复事件。

配置无法加载、日志目录无法建立、身份无法确定、数据库初始化失败等更早的异常不能可靠远程上报。SIGKILL/OOM/断电只能在下一次启动推断未正常退出,无法仅凭这一标记判断真正原因。原始日志保持原文,常见凭据和 URL 只在实时字段上传及服务端归一化时脱敏,原始文件应按敏感诊断资料管理。

运维排查和验证

现象 处理
页面提示不支持诊断 确认三端已升级,等待新扫描端第一次心跳
目录清单失效/文件变化 刷新目录并重新选择;不要在采集中覆盖源日志
文件过大 控制当日日志量,保留完整 JSON 行后重新创建采集请求
上传反复退避 检查服务端健康、HTTPS、运行 Token、配额和限流
丢弃事件数增长 检查诊断 SQLite/spool 的磁盘空间和权限;及时采集原始日志
停止领取任务但可以采集 修复启动检查错误并重启
采集过期 恢复连接后重新创建任务;旧请求不会无限延期
cargo fmt --check
cargo clippy --all-targets
cargo test

新增测试覆盖目录白名单、软链接/替换拒绝、追加中的完整行快照、事件脱敏、独立 SQLite 重启恢复/身份隔离,以及任务 manifest 重启恢复与过期清理。服务端专项 PostgreSQL 测试覆盖分块续传、校验、去重与下载权限。真实环境仍应检查反向代理上传限制至少容纳 4 MiB 分块、服务端 60 秒块超时,以及断电/进程终止后的实际持久化行为。