165 lines
7.0 KiB
Markdown
165 lines
7.0 KiB
Markdown
<h1 align="center">syscall_monitor</h1>
|
||
|
||
<p align="center">
|
||
<strong>基于 eBPF 的 Linux 系统调用计数监控器,附带 Flask 可视化界面</strong>
|
||
</p>
|
||
|
||
<p align="center">
|
||
<img src="https://img.shields.io/badge/python-3-blue?style=flat-square&logo=python&logoColor=white" alt="python">
|
||
<img src="https://img.shields.io/badge/flask-3.1-000000?style=flat-square&logo=flask&logoColor=white" alt="flask">
|
||
<img src="https://img.shields.io/badge/bcc-0.29-orange?style=flat-square" alt="bcc">
|
||
<img src="https://img.shields.io/badge/platform-linux-lightgrey?style=flat-square&logo=linux&logoColor=white" alt="platform">
|
||
<img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="license">
|
||
</p>
|
||
|
||
## 📖 项目简介
|
||
|
||
`syscall_monitor` 在 Linux 内核 `raw_syscalls:sys_enter` 跟踪点上挂载一段 eBPF 程序,按系统调用号在内核态做哈希计数;用户态由 Python 每秒读取一次快照,并在 Flask Web 页面上展示配置项中关心的系统调用调用次数。
|
||
|
||
由于挂载 eBPF 需要内核能力(CAP_BPF / root),整个程序必须以 root 权限运行。
|
||
|
||
## ✨ 主要特性
|
||
|
||
- 内核态全量计数,用户态按需展示:BPF 程序对所有系统调用号自增计数,配置变化无需重新挂载探针。
|
||
- 可在 Web 页面动态增删需要监控的系统调用名称,无需重启进程。
|
||
- `/api/counts` JSON 接口供前端每秒轮询,页面实时刷新。
|
||
- 后台采集线程 + 配置文件原子写入(`tmp` → `replace`),并发读写不丢更新。
|
||
- 提供 `setup.sh` / `run.sh` / `stop.sh` 三个部署脚本,配套 Gitea Actions CI/CD 工作流。
|
||
|
||
## 🧰 技术栈
|
||
|
||
| 类别 | 选型 | 来源 |
|
||
|------|------|------|
|
||
| 运行环境 | Linux(需 root,要求内核支持 eBPF/tracepoints) | [main.py](main.py), [collector/syscall_tracer.py](collector/syscall_tracer.py) |
|
||
| 语言 | Python 3 | [requirements.txt](requirements.txt) |
|
||
| Web 框架 | Flask 3.1 + Jinja2 | [requirements.txt](requirements.txt), [web/app.py](web/app.py) |
|
||
| 内核采集 | bcc 0.29(BPF Compiler Collection) | [requirements.txt](requirements.txt), [collector/syscall_tracer.py](collector/syscall_tracer.py) |
|
||
| 前端 | 服务端模板 + 原生 JS `fetch` 轮询 | [web/templates/](web/templates/) |
|
||
| CI/CD | Gitea Actions(self-hosted runner) | [.gitea/workflows/](.gitea/workflows/) |
|
||
|
||
## 📁 项目结构
|
||
|
||
```text
|
||
syscall_monitor/
|
||
├── main.py # 程序入口,校验 root 后启动 Flask
|
||
├── collector/
|
||
│ ├── __init__.py
|
||
│ └── syscall_tracer.py # eBPF 程序与采集线程
|
||
├── web/
|
||
│ ├── __init__.py
|
||
│ ├── app.py # Flask 应用工厂与路由
|
||
│ ├── static/style.css
|
||
│ └── templates/
|
||
│ ├── index.html # 实时监控页
|
||
│ └── config.html # 监控项配置页
|
||
├── config/
|
||
│ └── monitors.json # 需要展示的 syscall 名称列表
|
||
├── requirements.txt
|
||
├── setup.sh # 创建 venv(--system-site-packages)并装依赖
|
||
├── run.sh # nohup 后台启动,pid 写入 .pid
|
||
├── stop.sh # 根据 .pid 停止进程
|
||
├── .gitea/
|
||
│ ├── README.md # CI/CD 详细说明
|
||
│ └── workflows/
|
||
│ ├── ci.yml # 语法检查 + venv 构建
|
||
│ └── cd.yml # rsync 部署 + 健康检查
|
||
└── LICENSE
|
||
```
|
||
|
||
## 🧬 模块关系
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
Kernel[Linux Kernel<br/>raw_syscalls:sys_enter] -->|tracepoint| BPF[BPF_HASH counts]
|
||
BPF -->|每秒读取| Tracer[SyscallTracer<br/>后台线程]
|
||
Tracer -->|快照| Flask[Flask app]
|
||
Config[(config/monitors.json)] --> Tracer
|
||
Config --> Flask
|
||
Flask -->|/| IndexPage[实时监控页]
|
||
Flask -->|/api/counts| IndexPage
|
||
Flask -->|/config| ConfigPage[配置页]
|
||
```
|
||
|
||
## 🚀 快速开始
|
||
|
||
### 环境要求
|
||
|
||
- Linux 主机,root 权限
|
||
- Python 3 + venv
|
||
- `bcc` / `python3-bpfcc` 等内核工具及对应内核头文件
|
||
|
||
参考安装命令(来自 [.gitea/README.md](.gitea/README.md)):
|
||
|
||
```bash
|
||
sudo apt install -y python3-venv python3-bpfcc bpfcc-tools rsync curl linux-headers-$(uname -r)
|
||
```
|
||
|
||
### 安装
|
||
|
||
```bash
|
||
./setup.sh
|
||
```
|
||
|
||
脚本会创建启用 `--system-site-packages` 的 `.venv`(让 apt 安装的 bcc 在虚拟环境中可见),并按 `requirements.txt` 安装依赖。
|
||
|
||
### 配置
|
||
|
||
监控项保存在 [config/monitors.json](config/monitors.json) 的 `syscalls` 字段中,默认包含:`openat`、`read`、`write`、`execve`、`close`。
|
||
|
||
也可在 Web 配置页通过表单新增或移除监控项,文件会被原子化写入。
|
||
|
||
### 运行
|
||
|
||
```bash
|
||
sudo ./run.sh
|
||
```
|
||
|
||
默认监听 `0.0.0.0:5000`,可通过环境变量覆盖:
|
||
|
||
| 变量 | 默认值 | 说明 |
|
||
|------|--------|------|
|
||
| `HOST` | `0.0.0.0` | Flask 监听地址 |
|
||
| `PORT` | `5000` | Flask 监听端口 |
|
||
|
||
启动日志位于 `logs/app.log`,进程号写入 `.pid`。
|
||
|
||
### 停止
|
||
|
||
```bash
|
||
sudo ./stop.sh
|
||
```
|
||
|
||
## 🔌 接口
|
||
|
||
| 方法 | 路径 | 处理函数 | 说明 |
|
||
|------|------|----------|------|
|
||
| GET | `/` | `index` | 实时监控首页,前端每秒轮询 `/api/counts` 刷新 |
|
||
| GET | `/api/counts` | `api_counts` | 返回 `{syscall 名: 累计次数}` 的 JSON |
|
||
| GET | `/config` | `config_page` | 监控项配置页 |
|
||
| POST | `/config` | `config_page` | 表单字段 `action=add/remove`、`name=<syscall>`,PRG 后重定向回配置页 |
|
||
|
||
来源:[web/app.py](web/app.py)。
|
||
|
||
## 🚢 部署
|
||
|
||
仓库内附带的 Gitea Actions 工作流面向 **self-hosted Linux runner**:
|
||
|
||
- [.gitea/workflows/ci.yml](.gitea/workflows/ci.yml):push / PR 到 `main` 触发,做 Python 语法检查并在临时 venv 中安装依赖。
|
||
- [.gitea/workflows/cd.yml](.gitea/workflows/cd.yml):推送 `v*` tag 或手动触发 `workflow_dispatch`。流程依次为:停旧实例 → `rsync --delete` 同步代码到固定目录 → `setup.sh` → `run.sh` → 进程存活校验 → HTTP 健康检查 → 输出启动日志。
|
||
|
||
完整的 runner 准备步骤(系统包安装、部署目录创建、免密 sudo 配置等)见 [.gitea/README.md](.gitea/README.md)。
|
||
|
||
## 🛠️ 开发说明
|
||
|
||
- `main.py` 中 `app.run(..., use_reloader=False)` 是关键:开启 Flask 自动重载会让主进程被 fork,导致 eBPF 探针被加载两次而冲突。
|
||
- BPF 程序在内核里对**所有**系统调用号计数,过滤逻辑放在 Python 端 `_refresh_snapshot()` 里,因此修改配置无需重建 BPF 程序。
|
||
- `SyscallTracer` 通过模块级 `_tracer` 单例保证 BPF 程序在进程内只挂载一次。
|
||
|
||
## 🔒 安全与隐私
|
||
|
||
运行该程序需要 root / CAP_BPF 权限,会读取整机所有进程的系统调用频率信息,请勿在不受信任的环境或未脱敏的多租户主机上长期开启。监听地址默认 `0.0.0.0:5000`,对外暴露前请自行加上反向代理与访问控制。
|
||
|
||
## 📄 许可证
|
||
|
||
本项目以 MIT License 发布,详见 [LICENSE](LICENSE)。
|