侧边栏壁纸
博主头像
龍騰博客 博主等级

行动起来,活在当下

  • 累计撰写 200 篇文章
  • 累计创建 31 个标签
  • 累计收到 12 条评论

目 录CONTENT

文章目录

ScanWatch:外部扫描感知

管理员
2026-11-05 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

路由器和群辉把日志发到 SER8,ScanWatch 在地图上实时标出谁在扫你、从哪来、扫了什么,按危险程度着色,可查今天 / 近 7 天 / 近 30 天 / 全部,高危事件推送到飞书。

公网扫描 ─► RouterOS 防火墙日志 ┐
群辉 DSM 登录失败日志 ─────────┴─► syslog UDP 5140 ─► ScanWatch(SER8 docker)
                                                      ├ 解析 + 定位(GeoIP/ip2region) + 打分
                                                      ├ SQLite 存档(默认 90 天)
                                                      ├ 看板 http://192.168.0.188:9110(地图 + WebSocket 实时)
                                                      └ 飞书告警卡片 + 每日摘要

界面

  • 态势页:世界地图 + 中国省份地图,来源点按危险等级着色,红 / 橙弧线飞向"你在这里";新事件实时飞一条弧线。下面是各地区来源 IP 卡片。

  • 数据看板:独立 IP / 总访问次数 / 被拦截三张卡片;"攻击流程"(打过来多少 → 摸到服务端口 → 被拦截 / 放行);访问量趋势(可缩放);被探测最多的端口;中国境内 / 境外对比;可搜索、筛选、排序的 IP 明细表,点"展开明细"看每一条记录。

  • 浅色 / 深色模式;"遮"按钮把 IP 后两段打码(截图分享用);可选密码登录(只读)。

  • 地图是离线的(ECharts 自带世界和中国省份地图),不依赖外网底图服务器。

它能看到什么

类型

来源

说明

扫描

路由器防火墙日志 SCAN-IN

公网主动打到路由器自身的新连接,被路由器拦截

命中转发

路由器防火墙日志 PORTFWD

公网连接命中端口转发,放行进了内网,至少判"高"

路由器登录失败

路由器日志

Winbox / SSH / Web / API 登录失败,3 次起判"严重"

NAS 登录失败

群辉 DSM 连接日志

DSM / SSH / SMB 等登录失败,来自公网 3 次起"严重";来自内网 5 次起"高"、20 次起"严重"

看不到的:内网设备之间的流量,以及 DSM 登录成功的记录。

危险等级

颜色

等级

判定

绿

低

探测普通端口

黄

中

探测常见服务端口(80、443、53、5060、554 等)

橙

高

探测管理 / 高危端口(22、3389、445、8291、6379 等),命中你在用的端口(WATCH_PORTS),命中端口转发,或登录失败

红

严重

在"高"的基础上叠加:10 分钟内扫描 ≥ 8 个端口、≥ 100 次连接,或 1 小时内登录失败 ≥ 3 次

表格里的分类:高 / 严重 = 恶意扫描,中 = 可疑行为,低 = 低风险探测。规则在 app/scoring.py,想调直接改。


部署步骤

第 1 步:上传到 SER8

scp scanwatch.zip 用户名@192.168.0.188:~/
ssh 用户名@192.168.0.188
unzip scanwatch.zip && cd scanwatch

第 2 步:下载 IP 库

./scripts/get-geoip.sh

一次下载两个库:DB-IP Lite(全球坐标,用来在地图上定位)和 ip2region(中国境内的省 / 市 / 运营商)。下载失败的话,脚本会提示手动下载的文件名和存放位置。

第 3 步:改配置

cp .env.example .env
nano .env

必改的几项:

  • ROUTER_IP:路由器的内网 IP(Winbox 里 /ip address print 看 bridge1 上的地址)。填了以后只接受路由器和 NAS 发来的日志。

  • NAS_IP:群辉的内网 IP,已填 192.168.0.88。

  • DASHBOARD_PASSWORD:看板访问密码。留空则不需要登录。

  • IGNORE_IPS:你自己的 VPS 公网 IP,否则你用 nmap 自测会被记成攻击。

  • HOME_LAT / HOME_LON:可以留空,会按路由器日志里的公网 IP 自动定位到市。

第 4 步:启动

docker compose up -d --build
docker compose logs -f

日志里看到 syslog 监听 udp/5140、GeoIP 库已加载、ip2region 库已加载 就是成功。Ctrl+C 退出日志。

如果 SER8 本机开了防火墙,放行 TCP 9110(Web)和 UDP 5140(syslog),来源限制为 192.168.0.0/24。

第 5 步:配置路由器

先确认 routeros/setup.rsc 里的 SER8 地址 192.168.0.188 和外网接口 pppoe-out1 和你的环境一致,然后在 Winbox 终端里整段粘贴执行。它会:把 firewall 和 critical 日志发到 SER8;加两条带限速的日志规则(记录公网打到路由器的新连接、命中端口转发的连接),并移到各自链的最前面。

第 6 步:配置群辉 DSM(NAS 登录失败上地图)

  1. DSM → 日志中心 → 日志发送(不同版本菜单名略有出入)。

  2. 新增 syslog 服务器:服务器 192.168.0.188,端口 5140,协议 UDP,日志格式 BSD (RFC 3164)。

  3. 日志类型勾选连接(登录成功 / 失败都在这一类),其他可不选。

  4. 保存后,故意在 DSM 登录页输错一次密码,几秒内看板应出现一条 "NAS 登录失败"。

DSM 实际发出的日志长这样,ScanWatch 就是按它解析的:

Connection: User [admin] from [1.2.3.4] failed to sign in to [DSM] via [password] due to authorization failure.

DSM 如果是中文界面,日志文字不同,程序会用关键词(失败 / 登录 + 方括号 + IP)做宽松匹配。万一没识别,打开 http://192.168.0.188:9110/api/debug/unparsed 能看到最近 50 条没识别的原始日志,把它们发给我就能调整规则。

第 7 步:配置飞书告警(可选)

  1. 飞书群 → 设置 → 群机器人 → 添加机器人 → 自定义机器人,复制 Webhook 地址。

  2. 安全设置里如果勾了"签名校验",复制密钥;没勾就不用。

  3. 填进 .env:FEISHU_WEBHOOK=...、FEISHU_SECRET=...,可顺手改 DASHBOARD_URL。

  4. 让配置生效,然后发一条测试:

docker compose up -d
docker compose exec scanwatch python -m app.tools.test_feishu

群里收到红色测试卡片就成功了。注意改了 .env 要用 up -d 而不是 restart,后者不会重新读取配置。

推送规则(防止刷屏):

  • 扫描类事件达到 ALERT_MIN_SEV(默认 4=严重)才推;命中端口转发、NAS 登录失败达到 3(高)就推。

  • 同一个来源 IP 30 分钟内只推一次,等级升高时例外。

  • 每小时最多 20 条,超过后只推一条"已限流"提示,之后只记录不推送。

  • 每天 8 点推送"过去 24 小时摘要"(DAILY_DIGEST_HOUR=-1 关闭)。

第 8 步:验证

用模拟数据看界面:

docker compose exec scanwatch python -m app.tools.send_test 127.0.0.1 5140

浏览器打开 http://192.168.0.188:9110。看完清掉模拟数据:

docker compose down && rm -f data/scanwatch.db* && docker compose up -d

用真实流量验证:在你的 VPS 上扫一下家里公网 IP(先把这个 VPS 的 IP 从 IGNORE_IPS 去掉),几秒内页面出现一条来自 VPS 所在地的记录。

常用命令

docker compose logs -f              # 运行日志
docker compose up -d                # 改了 .env 后让配置生效
docker compose up -d --build        # 改了代码后重新构建
docker compose restart              # 换了 data/geoip 里的库后重启
curl localhost:9110/healthz         # received 在涨说明在收日志,parsed 是识别并入库的条数

排错

现象

检查

顶部提示"还没有收到路由器日志"

路由器没发出来,或 SER8 防火墙拦了 UDP 5140。路由器上 /system logging print 看配置;ROUTER_IP 填错会被丢弃,先留空测试

received 在涨,parsed 不涨

路由器日志前缀不对:/log print where message~"SCAN-IN" 看有没有日志;NAS 日志看 /api/debug/unparsed

地图上没点

没加载 GeoIP 库(页面会提示),或该 IP 在库里没有坐标

中国来源没有省 / 市 / 运营商

没加载 ip2region,重新运行 ./scripts/get-geoip.sh

飞书收不到

docker compose logs scanwatch 里搜"飞书";Webhook 地址错、开了签名但没填密钥、或 SER8 访问不了飞书

一直有自己的流量被记录

把端口或 IP 加进 IGNORE_PORTS / IGNORE_IPS

查一个 IP 的底细(判断攻击动机)

点任意 IP 打开详情,再点最上面的 IP 地址,会展开归属信息:位置、运营商、机构、ASN、网段、注册机构、反向解析域名、是否数据中心 / 代理 / 移动网络、滥用举报邮箱,并根据它扫的端口、尝试的账号、归属类型给出"可能的动机"提示(例如:专扫 RouterOS、SMB 勒索入口、已知的测绘机构可忽略、云主机批量扫描等)。提示是推断,仅供参考。

  • 联网部分用 ip-api.com(HTTP)和 rdap.org,每个 IP 缓存 6 小时。SER8 访问不了这些站点时,只会少几项,本地库信息照常显示。

  • 不想联网:.env 里设 IPINFO_ONLINE=0。

把扫描数据变成防御:封禁命令生成器

在"数据看板"的 IP 明细表里:

  1. 勾选想封的 IP(或不勾,直接按当前筛选条件批量生成);也可以在某个 IP 的详情里点"生成封禁命令"。

  2. 点 生成封禁命令,选最低等级、封禁时长(1 天 / 7 天 / 30 天 / 永久),需要的话勾"按 /24 网段封禁"。

  3. 点复制命令,到 Winbox 新建终端粘贴执行;命令很多时点下载 .rsc,上传到路由器后 /import file-name=文件名.rsc。

  4. 命令末尾自带"检查"和"撤销这一批"两条命令,每一批有独立标签,可以单独撤销。

ScanWatch 只生成文本,不连接、不修改路由器。 前提是路由器上已有丢弃这个名单的规则:

/ip firewall raw add chain=prerouting in-interface=pppoe-out1 src-address-list=blocklist action=drop comment="drop blocklist"

防误封保护(避免批量封禁封到自己):

  • 内网 / 保留地址、你的公网 IP、NAS、路由器、IGNORE_IPS 里的地址一律不会出现在命令里。

  • 按条件批量生成时,默认排除中国境内来源,以及命中过端口转发的来源(可能是你自己或家人在外访问摄像头)。这两项可以手动取消勾选。

  • 手选的 IP 不受上面两项限制(你明确选了),但受保护地址仍会被跳过。

  • 按 /24 封网段时,只要网段里含受保护地址,就自动退回成只封单个 IP。

  • 重复添加不会报错,已在名单里的地址自动跳过。

  • 第一次使用,建议先只勾一个 IP、时长选 1 天,执行后用文末的检查命令确认。

升级定位算法后:重算历史数据

新版以 ip2region 的省份为准定位中国境内来源(两个 IP 库对同一个 IP 判断不一致时,不再出现"点在北京、弹窗写上海")。已存的历史事件要跑一次:

docker compose exec scanwatch python -m app.tools.regeo

数据量与合并

互联网上的扫描器经常对同一个端口每秒打好几次。为了不撑爆数据库,同一来源 + 同一端口 + 同一类型的事件,60 秒内只存一行,并累计次数(明细里会显示"合并了 N 次",所有统计数字仍然是真实次数)。COALESCE_SECONDS 可调,设 0 关闭合并。等级升高、登录失败、飞书告警不受合并影响,照常即时处理。

注意

  • syslog 是明文 UDP,没有身份验证,ROUTER_IP / NAS_IP 只是简单的来源过滤。

  • 看板只放在内网或 WireGuard 里,不要做端口转发到公网,即使设了密码也一样。

  • GeoIP 精度是城市级,运营商出口、VPN、云服务器的位置不一定是攻击者真实位置。

  • 数据在 data/scanwatch.db,备份直接复制。保留天数由 RETENTION_DAYS 控制。

0

评论区