HalcyonVector/India-BGP-Hijack-Monitor

GitHub: HalcyonVector/India-BGP-Hijack-Monitor

该项目是一个针对印度主要 ISP 的实时 BGP 劫持与路由泄露检测系统,通过基线比对与 RPKI 交叉验证来标记异常前缀起源。

Stars: 0 | Forks: 0

# 印度 BGP 劫持/泄露监控器 [![tests](https://static.pigsec.cn/wp-content/uploads/repos/cas/6b/6b52945adbf8d9e421fe243515ae54cfbd3da263f16b1eabda37cdc0b797b8eb.svg)](https://github.com/HalcyonVector/India-BGP-Hijack-Monitor/actions/workflows/test.yml) 监控 9 大印度网络(Airtel、Jio、BSNL、ACT Fibernet、Vodafone Idea、Tata Communications、Sify、RailTel)的实时 BGP 路由通告。如果某个前缀被通常不拥有它的 ASN 通告,系统将对其进行标记;在存在 ROA 的情况下与 RPKI 进行交叉检查;并通过 FastAPI 后端和仪表板提供服务。基于免费、无需密钥的公开数据构建 —— RIPE RIS Live 和 RIPEstat。 ![仪表板截图](https://raw.githubusercontent.com/HalcyonVector/India-BGP-Hijack-Monitor/main/docs/screenshot.png) *在免费层级重启后立即从部署实例截取——每张卡片上的“RPKI 覆盖率尚未采样”是该时刻的真实状态,而不是 bug:Render 的免费层级没有持久化磁盘,因此 `rpki_coverage.py` 的结果在每次重新部署/重启时都会重置(参见已知限制)。上述“发现”中 40%-100% 的数据来自完整的采样运行,而非此特定快照。* ## 发现 运行该项目得出的真实数据,而非预测: - **RPKI 覆盖率因 ISP 而异,范围在 40%→100%。** 对每个 ASN 抽取 30 个前缀与实时 RIPEstat 数据进行比对:Bharti Airtel 的主区块(AS24560)的 ROA 覆盖率仅为 **40%**,而 Jio、BSNL、ACT Fibernet 和 Tata Communications 均达到了 **100%**。同一个国家,同样的监管环境,RPKI 的采用率却大相径庭。 - **真实流量中零误报。** 已针对真实的 BGP 更新(处理了数千条消息)进行实时测试,未出现任何错误的劫持标记。 - **检测逻辑经过双重验证**:针对构造的劫持测试用例通过了 5/5 的单元测试(证明它*能够*捕获真实劫持),加上实时流量精度测试(证明它不会对正常流量产生误报)——测试期间未在实时环境中观测到真实的劫持,这一区别已被明确指出,而非含糊其辞。 ## 免责声明 这是一个教育/作品集项目,并非生产级安全工具。检测覆盖范围是局部的(每个被跟踪 ASN 约 8 个最大区块,而非完整覆盖——参见已知限制)。已针对真实 BGP 流量验证了精度(在实时运行中零误报),并针对构造的劫持测试用例验证了逻辑正确性(5/5 单元测试)——由于测试期间未发生真实劫持,因此未针对真实劫持进行验证。本项目中的任何地方都不包含凭证、付费 API 或出站告警。 ## 功能 ### 核心检测 - 实时订阅 RIPE RIS Live,按前缀而非 AS 路径过滤 —— 劫持者的路径永远不会包含受害者的 ASN,因此路径过滤会漏掉真实的劫持 - 基线比对:每个被跟踪 ASN 当前通告的前缀(源自 RIPEstat)作为事实依据;由不同源 ASN 发起的实时通告将被标记 - 更明细检测:`moreSpecific: true` 订阅可以捕获从正常 /16 区块中劫持出的 /24,而不仅仅是精确前缀不匹配的情况 - 对存在 ROA 的每个标记事件进行 RPKI 交叉检查;明确的“invalid”结果会将严重性升级为 critical - 每个事件中观测到的劫持者 ASN 都会解析为其持有者组织名称(源自 RIPEstat as-overview),而不是仅留作一个单纯的数字 - `baseline_drift.py` / `/api/baseline-drift` 读取每日提交的基线摘要的 git 历史记录,并报告随时间推移每个 ASN 前缀计数的变化情况 ### 后端与 API - FastAPI,只读模式(`/api/status`、`/api/asns`、`/api/events`),启用 CORS - SQLite:`baseline_prefixes`、`events`、`monitor_status` —— 无外部数据库 - 事件标记为 `info` / `warning` / `critical` ### 前端 - 状态磁贴(基线前缀、被跟踪 ASN、已处理消息、已标记事件),每 15 秒自动刷新 - 基于距离上一条处理消息的时间,显示运行中/未运行状态徽章 - 被跟踪 ASN 网格、可过滤的事件表 ### RPKI 覆盖率 - `rpki_coverage.py` 对每个 ASN 最多抽取 30 个基线前缀作为样本,并报告拥有 ROA 的比例 —— 实际数字范围从 40%(Airtel 的主区块)到 100%(Jio、BSNL、ACT、Tata Communications) - 在仪表板上按每个 ASN 显示为“X% 已被 ROA 覆盖(样本量 N)” —— 明确标注为采样结果,而非穷举 ### 自动化 - `.github/workflows/test.yml` —— CI 在每次 push 时运行检测逻辑测试 - `.github/workflows/refresh-baseline.yml` —— 每日定时任务针对实时 RIPEstat 重建基线,针对其重新运行测试,并将摘要(`docs/baseline-summary.json`)提交回代码库 - `.github/workflows/refresh-rpki-coverage.yml` —— 每周定时任务重新采样 RPKI 覆盖率并提交 `docs/rpki-coverage-summary.json` - `run_monitor.bat` —— 崩溃自动重启包装器,以便监控器可以作为持久的 Windows 后台服务运行(参见 `docs/scheduling.md`),而不是仅能在手动演示期间运行 ### 验证 - `test_detection_logic.py` —— 使用带有伪造错误源场景的真实 Airtel/Jio 区块进行了 5 项测试 - `test_api.py` —— 针对实时 FastAPI 端点本身进行了 7 项检查(响应结构、严重性过滤、限制验证、空历史记录处理),而不仅仅是检测逻辑 - 监控器已针对真实 BGP 流量运行,并确认能够处理真实消息且零误报 ## 技术栈 | 层级 | 技术 | 详情 | |-------|-----------|---------| | 后端 | FastAPI + Uvicorn | 只读 REST API,OpenAPI 文档位于 `/docs` | | 实时数据 | RIPE RIS Live | 免费的 WebSocket BGP 数据源,服务端前缀过滤 | | 基线数据 | RIPEstat API | 免费、无需密钥 | | WebSocket 客户端 | websocket-client | | | HTTP 客户端 | requests | | | 数据库 | SQLite (`sqlite3`) | | | 前端 | 原生 HTML/CSS/JS | 无框架 | | API 测试 | httpx2 + FastAPI TestClient | 真实端点测试,而不仅仅是检测逻辑 | | CI | GitHub Actions | 在 push 时运行两套测试套件;每日/每周的数据刷新定时任务 | ## 前置条件 - Python 3.11+ - 无需 API 密钥、无需账户、无需付费服务 ## 快速开始 ``` pip install -r requirements.txt python db/store.py # create schema python backend/detector/baseline.py # build the real baseline (~21k prefixes, 9 ASNs) python backend/detector/test_detection_logic.py # verify detection logic python backend/detector/monitor.py # run the live monitor (Ctrl+C to stop) ``` 在单独的终端中: ``` python -m uvicorn backend.api:app --port 8000 python -m http.server 8090 --directory frontend # then open http://localhost:8090 ``` | 服务 | URL | |---------|-----| | 仪表板 | http://localhost:8090 | | API | http://localhost:8000 | | API 文档 | http://localhost:8000/docs | ## 部署 免费、实时部署:Render(后端)+ GitHub Pages(前端)。完整的分步指南在 [docs/deployment.md](docs/deployment.md) 中 —— 包括为什么 Vercel 不符合此应用的需求(持久 WebSocket + SQLite)、`render.yaml` Blueprint 配置,以及免费层级休眠的解决方法。 ## 检测原理 1. **基线** —— 从 RIPEstat 获取每个被跟踪 ASN 当前通告的前缀。 2. **实时监控** —— 订阅按前缀过滤(每个 ASN 约 8 个最大的区块,`moreSpecific: true`)的 RIS Live。 3. **比对** —— 比对观测到的源 ASN 与基线的预期所有者。不匹配则生成事件,并在存在 ROA 的情况下进行 RPKI 检查。 4. **服务** —— FastAPI 读取 SQLite;仪表板每 15 秒轮询一次。 ## 项目结构 ``` India-BGP-Hijack-Monitor/ ├── README.md ├── LICENSE ├── requirements.txt ├── .gitignore ├── render.yaml # Render Blueprint: single free web service ├── run_monitor.bat # Restart-on-crash wrapper for a persistent monitor ├── .github/workflows/ │ ├── test.yml # CI: runs detection tests on push │ ├── refresh-baseline.yml # CI: daily baseline rebuild + summary commit │ └── refresh-rpki-coverage.yml # CI: weekly RPKI coverage resample + commit │ ├── backend/ │ ├── api.py # FastAPI: status, asns, events, baseline-drift │ │ # (+ optional inline monitor thread for deploy) │ ├── test_api.py # Tests the live endpoints themselves │ └── detector/ │ ├── targets.py # 9 tracked Indian ASNs │ ├── baseline.py # Fetches announced prefixes per ASN │ ├── monitor.py # RIS Live listener, flags origin mismatches │ ├── rpki_coverage.py # Sample-based RPKI ROA coverage per ASN │ ├── baseline_drift.py # Per-ASN prefix drift from git history │ └── test_detection_logic.py # Unit tests against fabricated fixtures │ ├── db/ │ ├── schema.sql │ └── store.py │ ├── frontend/ │ └── index.html # Deploy-aware API URL (localhost vs. Render) │ └── docs/ ├── limitations.md ├── scheduling.md # Running the monitor as a Windows background service ├── deployment.md # Render + GitHub Pages deployment guide ├── baseline-summary.json # Committed daily by refresh-baseline.yml └── rpki-coverage-summary.json # Committed weekly by refresh-rpki-coverage.yml ``` ## API 端点 | 方法 | 端点 | 描述 | |--------|----------|-------------| | GET | `/api/status` | 已处理消息数、总事件数、基线大小、被跟踪 ASN 数量、最后消息时间 | | GET | `/api/asns` | 被跟踪的 ASN 及其每个 ASN 的基线前缀计数和 RPKI 覆盖率 | | GET | `/api/events` | 最近的事件,可选 `?severity=` 和 `?limit=`(默认 50,最大 500) | | GET | `/api/baseline-drift` | 最旧和最新提交的基线摘要之间每个 ASN 的前缀计数漂移 | ## 配置 无需环境变量或 API 密钥。`backend/detector/targets.py` 以纯字典的形式保存被跟踪的 ASN 列表。 ## 架构 ``` ┌──────────────────────────────┐ │ Dashboard (Port 8090) │ │ Vanilla HTML/CSS/JS │ │ Polls API every 15s │ └──────────┬────────────────────┘ │ REST (fetch) ┌──────────▼────────────────────┐ │ FastAPI (Port 8000) │ │ /api/status /asns /events │ └──────────┬────────────────────┘ │ ┌──────────▼────────────────────┐ │ SQLite │ │ baseline_prefixes / events /│ │ monitor_status │ └──────────▲────────────────────┘ │ ┌────────────────┼────────────────┐ │ │ │ ┌──────────▼────────┐ ┌────▼─────────┐ ┌─────▼──────────┐ │ baseline.py │ │ monitor.py │ │ RPKI check │ │ RIPEstat │ │ RIS Live WS │ │ (RIPEstat) │ │ announced-prefixes │ │ prefix-filtered│ │ on flagged events│ └──────────────────────┘ └──────────────┘ └─────────────────┘ ``` ## 数据源 | 来源 | 类型 | 需要密钥? | 提供的内容 | |--------|------|----------|-------------------| | RIPE RIS Live | 实时 BGP 流 | 否 | 实时 BGP UPDATE 消息,服务端前缀过滤 | | RIPEstat announced-prefixes | 基线 | 否 | 每个 ASN 当前通告的前缀 | | RIPEstat rpki-validation | 佐证 | 否 | ROA 验证状态(valid/invalid/unknown) | | RIPEstat as-overview | 归属 | 否 | ASN 的持有者组织名称(用于命名观测到的劫持者) | ## 测试 ``` python backend/detector/test_detection_logic.py # detection logic: 5 fixture-based checks python backend/test_api.py # API endpoints: 7 checks against the real FastAPI app ``` 检测逻辑:合法精确匹配、合法子前缀、构造的精确前缀劫持、构造的子前缀劫持、正确忽略无关前缀。API 测试:响应结构、严重性过滤、限制验证(拒绝超过最大值而不是静默截断)、空历史记录处理。两者都不需要实时流量或 API 密钥。两者都会在每次 push 时在 CI 中运行。 ## 可用脚本 | 脚本 | 命令 | 描述 | |--------|---------|--------------| | 初始化 schema | `python db/store.py` | 创建 SQLite schema | | 构建基线 | `python backend/detector/baseline.py` | 获取当前通告的前缀 | | 运行检测测试 | `python backend/detector/test_detection_logic.py` | 验证检测逻辑 | | 运行 API 测试 | `python backend/test_api.py` | 验证实时 FastAPI 端点 | | RPKI 覆盖率 | `python backend/detector/rpki_coverage.py` | 采样每个 ASN 的 RPKI ROA 覆盖率(约 5 分钟) | | 基线漂移 | `python backend/detector/baseline_drift.py` | 从 git 历史记录中获取每个 ASN 的前缀计数漂移 | | 实时监控(定时) | `python backend/detector/monitor.py --max-seconds 60` | 监控真实 BGP 流量 60 秒 | | 实时监控(持久) | `run_monitor.bat` | 无限期运行并自动重启(参见 `docs/scheduling.md`) | | 后端 | `python -m uvicorn backend.api:app --port 8000` | 启动 API 服务器 | | 前端 | `python -m http.server 8090 --directory frontend` | 提供仪表板服务 | ## 故障排除 **仪表板无数据显示:** 确认后端是否可访问 —— `curl http://127.0.0.1:8000/api/status`。如果失败,请使用 `python -m uvicorn backend.api:app --port 8000` 启动它。 **`baseline.py` 返回某 ASN 的 0 个前缀:** 重新运行脚本;如果情况持续,请直接在 `https://stat.ripe.net/AS` 检查该 ASN。 **监控器已连接但从未标记任何内容:** 符合预期 —— 劫持很少见。使用 `test_detection_logic.py` 确认逻辑本身是否正常。 **端口已被占用:** 在不同的端口上运行并更新 `frontend/index.html` 中的 `API` 常量: ``` python -m uvicorn backend.api:app --port 8001 python -m http.server 8091 --directory frontend ``` ## 已知限制 完整列表见 [docs/limitations.md](docs/limitations.md): - 订阅覆盖范围是每个 ASN 约 8 个最大的区块,而非完整覆盖 - RPKI 覆盖率因 ASN 不同而差异显著(在 30 个样本检查中为 40%-100%),且覆盖率统计本身是一个采样结果,而非穷举 - 验证了精度,而非真实事件的召回率(召回率已通过测试用例验证) - 基线通过 CI 每日自动刷新,但这仅会更新代码库的摘要 —— 你仍然需要在本地为自己的运行实例重新运行 `baseline.py` - 无出站告警 —— 仅限仪表板 - 监控器现在可以持久运行(`run_monitor.bat` + 任务计划程序),但它仍然是单点收集 —— 单台机器,无冗余 - Render 的免费层级没有持久化磁盘,因此重启会清除 SQLite DB —— 基线和 RPKI 覆盖率都会自动自我修复(启动时播种/重建,无需手动步骤),但累积的事件历史记录会按设计重置(不会从静态文件中播种虚假的“历史”事件) ## 项目统计 | 指标 | 数量 | |--------|-------| | 被跟踪 ASN | 9 | | 基线前缀 | 20,737 | | API 端点 | 3 | | 检测逻辑单元测试 | 5 | | 后端 Python 文件 | 7 | | 前端文件 | 1 | | CI 工作流 | 2 | | RPKI 覆盖率范围(已采样) | 40%-100% | ## 作者 **Sagnik** GitHub: [@HalcyonVector](https://github.com/HalcyonVector) ## 许可证 MIT License — 见 [LICENSE](./LICENSE)。
标签:AV绕过, BGP监控, Docker 部署, FastAPI, RPKI验证, SQLite, 网络运维, 路由劫持检测, 逆向工具