HalcyonVector/India-BGP-Hijack-Monitor
GitHub: HalcyonVector/India-BGP-Hijack-Monitor
该项目是一个针对印度主要 ISP 的实时 BGP 劫持与路由泄露检测系统,通过基线比对与 RPKI 交叉验证来标记异常前缀起源。
Stars: 0 | Forks: 0
# 印度 BGP 劫持/泄露监控器
[](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。

*在免费层级重启后立即从部署实例截取——每张卡片上的“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, 网络运维, 路由劫持检测, 逆向工具