Josperdo/scanlyne
GitHub: Josperdo/scanlyne
一款轻量级网络变更检测工具,通过定期 nmap 扫描和快照对比来发现网络中的异常变化,并为每个变更提供安全风险提示。
Stars: 0 | Forks: 0
# Scanlyne
具备安全上下文的网络变更检测工具。Scanlyne 运行 nmap 扫描,存储结果,并展示快照间的变更——附带关于为何该变更值得调查的简明英语说明。
## 快速开始
### Docker (推荐)
镜像中已包含 nmap。无需本地依赖。
```
git clone https://github.com/your-username/scanlyne.git
cd scanlyne
docker compose up --build
```
打开 `http://localhost:5000`。数据库和扫描输出存储在命名的 Docker 卷中,并在容器重启后保留。
**认证 (可选):** 未设置时应用为开放状态——仅适用于受信任的 LAN 环境。
```
SECRET_KEY=mysecret SCANLYNE_USERNAME=admin SCANLYNE_PASSWORD=pass docker compose up
```
或者直接在 `docker-compose.yml` 中设置它们。
**扫描您的 LAN:** 默认情况下,容器在 Docker 的 bridge 网络上运行。要从 Linux 主机访问 LAN 主机,请在 `docker-compose.yml` 中切换到 host 网络:
```
# 替换 ports: 映射为:
network_mode: host
```
不支持 Docker Desktop for Mac 或 Windows——请改用主机的 IP 范围作为扫描目标。
### 手动设置
**要求:** Python 3.10+, 已安装 nmap 并在 PATH 中。
```
git clone https://github.com/your-username/scanlyne.git
cd scanlyne
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
flask --app app:create_app run # dev server, auto-reloads
```
打开 `http://localhost:5000`。数据库和扫描输出目录会在首次运行时自动创建。
用于生产环境(后台调度程序需要单个 worker):
```
gunicorn --workers 1 "app:create_app()"
```
在部署前通过环境变量设置 `SECRET_KEY`:
```
export SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(32))")
export SCANLYNE_USERNAME=admin
export SCANLYNE_PASSWORD=yourpassword
```
## 填补的空白
大多数网络监控工具要么功能过剩,要么功能不足:
- **企业级解决方案** (Nessus, Qualys, SolarWinds) 价格昂贵,操作复杂,专为拥有专职安全人员的团队构建。
- **原始 nmap CLI** 提供了一切信息,却没给出答案。一份 500 行的 XML 转储无法告诉您 NAS 上新开放的端口是配置错误还是预期行为。
Scanlyne 介于两者之间。它面向运行自己基础设施的人群——家庭实验室用户、系统管理员、小团队——他们想要一个简单的答案:**“自上次检查以来我的网络发生了什么变化,我需要关注吗?”**
## 适用人群
- **家庭实验室用户**:跟踪虚拟机、容器和设备上的服务蔓延
- **系统管理员**:验证补丁窗口或配置更改没有留下意外的开放端口
- **安全分析师**:想要轻量级的审计跟踪,而无需搭建完整的 SIEM
- **学生**:通过动手操作工具学习网络安全概念
## 功能概览
1. **运行扫描** —— 通过 Web UI 提交目标(IP, CIDR, 主机名)和可选的 nmap 标志。结果存储在 SQLite 中,原始 XML 保留在磁盘上。
2. **保存基线** —— 在扫描反映出已知良好状态后,将其标记为该目标的基线。这是所有未来扫描的比较参考点。
3. **检测变更** —— 运行另一次扫描,然后打开变更检测 (Change Detection)。Scanlyne 对比两次扫描并展示:
- **新主机** —— 网络上出现的设备
- **移除的主机** —— 消失的设备
- **变更的主机** —— IP 相同,但端口或服务不同:
- 开放或关闭的端口
- 版本或状态发生改变的服务
4. **结合上下文进行分类** —— 每个变更都包含简短的风险提示。不是定论,只是一个起点:
```
[!] New open port: 3306/tcp — MySQL — database, verify intentional exposure
[!] Port 4444/tcp opened — Common reverse shell port
[~] Service version changed on 443/tcp — may indicate an upgrade or a substitution
[✓] Port 8080/tcp closed — previously open, now gone
```
## 示例:捕捉意外服务
您在周日为家庭服务器建立了基线。周中您更新了一些软件包。您运行另一次扫描并打开变更检测:
```
Target: 192.168.1.10
Baseline: Scan #3 (2024-11-10 14:32) → Current: Scan #7 (2024-11-13 09:15)
Changed hosts
└── 192.168.1.10
New ports
┌─────────┬──────────┬───────┬────────────────────────────────────────────────────┐
│ Port │ Protocol │ State │ Risk hint │
├─────────┼──────────┼───────┼────────────────────────────────────────────────────┤
│ 6379 │ tcp │ open │ Redis — often misconfigured with no auth │
└─────────┴──────────┴───────┴────────────────────────────────────────────────────┘
Service changes
┌──────┬──────────┬─────────────────────┬─────────────────────┬──────────────────┐
│ Port │ Protocol │ Old service │ New service │ Risk hint │
├──────┼──────────┼─────────────────────┼─────────────────────┼──────────────────┤
│ 443 │ tcp │ Apache httpd 2.4.51 │ Apache httpd 2.4.58 │ Version changed │
└──────┴──────────┴─────────────────────┴─────────────────────┴──────────────────┘
```
Redis 之前并不存在。软件包更新将其作为依赖项拉入,并且它绑定到了所有接口。值得关注。
变更检测视图会自动显示所有基线对——点击即可查看完整的差异:
## 工作流程
```
Run Scan → mark as baseline → Run Scan → Change Detection → review diff
```
1. **运行扫描** —— 输入目标和标志(例如 `-sV -T4`)。扫描异步运行;详情页面轮询完成状态。
2. **标记为基线** —— 在扫描详情页面上,将已完成的扫描提升为基线状态。添加可选标签(例如 "pre-patch", "post-change")。每个目标支持多个基线。
3. **运行另一次扫描** —— 相同目标,相同或不同的标志。
4. **变更检测** —— 应用自动显示所有基线与最新扫描的配对。点击即可查看差异。
5. **手动比较** —— 比较任意两个已完成的扫描,而不仅仅是基线对。
6. **计划任务** —— 在 `/schedules` 配置定期扫描。应用会在配置的间隔内在后台触发它们。
## 架构
```
scanlyne/
├── app.py # Flask application factory, auth, scheduler startup
├── config.py # Configuration (SECRET_KEY, DB path, scan output dir)
├── models.py # SQLAlchemy models: Scan, Host, Port, Schedule
├── scanner.py # nmap subprocess execution with input validation
├── parser.py # nmap XML → Python dict
├── diff.py # Scan comparison + risk hint generation
├── blueprints/
│ ├── scan.py # Run scans, manage baselines
│ ├── results.py # Scan history and detail views
│ ├── compare.py # Change detection — the primary view
│ └── schedules.py # Recurring scan schedule CRUD
├── templates/
│ ├── base.html
│ ├── scan/
│ ├── results/
│ ├── compare/
│ └── schedules/
└── static/
├── css/style.css
└── js/main.js
```
**数据持久化:** 通过 Flask-SQLAlchemy 使用 SQLite。无需外部数据库。扫描结果位于 `instance/scanner.db`。原始 nmap XML 存储在 `scans/` 中,并在数据库中通过路径引用。
**依赖项:** Flask, Flask-SQLAlchemy, APScheduler, gunicorn。无消息队列,无外部服务。APScheduler 在 Flask 进程内运行以进行计划扫描。
## 安全设计
Scanlyne 将 nmap 作为子进程执行。采取了几项控制措施以防止滥用:
- **目标验证** —— 正则表达式允许列表阻止 Shell 元字符(`; | & $ >` 等)
- **标志允许列表** —— 仅允许一组固定的 nmap 标志(不允许 `--script`,不允许 `--lua`)
- **无 `shell=True`** —— 子进程始终使用参数列表调用
- **可选的 HTTP Basic Auth** —— 设置 `SCANLYNE_USERNAME` 和 `SCANLYNE_PASSWORD` 环境变量以启用。默认关闭;专为受信任的 LAN 使用设计。切勿在未启用认证且未置于 HTTPS 之后的情况下将其暴露在公共互联网上。
## 已知限制
- **调度程序需要单个 worker。** 如果您使用多个 worker 运行 gunicorn,每个 worker 都会启动自己的调度程序并触发重复扫描。请使用 `--workers 1`。
- **无 HTTPS。** 如果将其暴露在 localhost 之外,请运行在反向代理(nginx, Caddy)之后。
- **仅支持 SQLite。** 适用于家庭实验室规模;不适合高并发多用户部署。
## 工作流程
```
Run Scan → mark as baseline → Run Scan → Change Detection → review diff
```
1. **运行扫描** —— 输入目标和标志(例如 `-sV -T4`)。扫描异步运行;详情页面轮询完成状态。
2. **标记为基线** —— 在扫描详情页面上,将已完成的扫描提升为基线状态。添加可选标签(例如 "pre-patch", "post-change")。每个目标支持多个基线。
3. **运行另一次扫描** —— 相同目标,相同或不同的标志。
4. **变更检测** —— 应用自动显示所有基线与最新扫描的配对。点击即可查看差异。
5. **手动比较** —— 比较任意两个已完成的扫描,而不仅仅是基线对。
6. **计划任务** —— 在 `/schedules` 配置定期扫描。应用会在配置的间隔内在后台触发它们。
## 架构
```
scanlyne/
├── app.py # Flask application factory, auth, scheduler startup
├── config.py # Configuration (SECRET_KEY, DB path, scan output dir)
├── models.py # SQLAlchemy models: Scan, Host, Port, Schedule
├── scanner.py # nmap subprocess execution with input validation
├── parser.py # nmap XML → Python dict
├── diff.py # Scan comparison + risk hint generation
├── blueprints/
│ ├── scan.py # Run scans, manage baselines
│ ├── results.py # Scan history and detail views
│ ├── compare.py # Change detection — the primary view
│ └── schedules.py # Recurring scan schedule CRUD
├── templates/
│ ├── base.html
│ ├── scan/
│ ├── results/
│ ├── compare/
│ └── schedules/
└── static/
├── css/style.css
└── js/main.js
```
**数据持久化:** 通过 Flask-SQLAlchemy 使用 SQLite。无需外部数据库。扫描结果位于 `instance/scanner.db`。原始 nmap XML 存储在 `scans/` 中,并在数据库中通过路径引用。
**依赖项:** Flask, Flask-SQLAlchemy, APScheduler, gunicorn。无消息队列,无外部服务。APScheduler 在 Flask 进程内运行以进行计划扫描。
## 安全设计
Scanlyne 将 nmap 作为子进程执行。采取了几项控制措施以防止滥用:
- **目标验证** —— 正则表达式允许列表阻止 Shell 元字符(`; | & $ >` 等)
- **标志允许列表** —— 仅允许一组固定的 nmap 标志(不允许 `--script`,不允许 `--lua`)
- **无 `shell=True`** —— 子进程始终使用参数列表调用
- **可选的 HTTP Basic Auth** —— 设置 `SCANLYNE_USERNAME` 和 `SCANLYNE_PASSWORD` 环境变量以启用。默认关闭;专为受信任的 LAN 使用设计。切勿在未启用认证且未置于 HTTPS 之后的情况下将其暴露在公共互联网上。
## 已知限制
- **调度程序需要单个 worker。** 如果您使用多个 worker 运行 gunicorn,每个 worker 都会启动自己的调度程序并触发重复扫描。请使用 `--workers 1`。
- **无 HTTPS。** 如果将其暴露在 localhost 之外,请运行在反向代理(nginx, Caddy)之后。
- **仅支持 SQLite。** 适用于家庭实验室规模;不适合高并发多用户部署。标签:Docker, Flask, HTTP/HTTPS抓包, IT运维, Nmap, Python, Socks5代理, 占用监测, 变更检测, 基线监控, 安全防御评估, 密码管理, 局域网扫描, 开源安全工具, 异常检测, 快照对比, 态势感知, 插件系统, 数据统计, 无后门, 服务发现, 端口扫描, 网络安全, 虚拟驱动器, 请求拦截, 资产管理, 逆向工具, 逆向工程平台, 防御性安全, 防御绕过, 隐私保护