mhristodor/rustinel-view
GitHub: mhristodor/rustinel-view
为 Rustinel EDR 快照打造的只读事后分析 UI,将警报与遥测事件合并为具备进程感知能力的杀伤链时间线。
Stars: 1 | Forks: 0
# rustinel-view



一个用于 [Rustinel](https://github.com/Karib0u/rustinel) EDR 快照的只读分析 UI。只需提供警报文件和事件日志;它会将它们合并到一个具备进程感知能力的时间线中,你可以在浏览器中进行过滤、透视和浏览。
它是一个辅助工具,而不是 Rustinel 的一部分。Rustinel 负责检测并写入日志。而此工具是在事后读取这些日志。它不需要实时运行的 agent,不需要搭建数据库,也不需要接入 SIEM 的 API。

## 功能
- **双源合并至单一时间线。** 检测结果和原始遥测数据会被合并,并按时间排序到单一的杀伤链中,而不是让你必须在脑海中拼凑两个独立的信息面板。
- **可同时过滤两者的查询语言。** 类 KQL 风格的查询栏,支持通配符、数值比较、引号包裹的短语以及字段存在性测试。通过统一的字段模型,一个表达式即可同时匹配警报和事件。
- **进程谱系。** 透视任意 PID 以重建其进程树:上方为祖先进程,下方为子孙子树,且在每个节点上会固定展示触发于该进程的警报。
- **突发折叠。** 具有相同键值的重复活动会被折叠成带有计数和时间跨度的一行,因此一千次相同的 DNS 查询不会掩盖掉那条真正重要的信息。
- **双层噪音过滤。** 在数据接入时进行无条件丢弃,并提供一个开关以过滤低价值的传感器干扰数据,从而确保默认视图保持清爽。
- **自带运行环境。** 单一的静态 Go 二进制文件,内置进程级 Redis。无需安装任何程序,不向磁盘写入任何数据,一旦退出数据即刻清除。
## 工作原理
二进制程序启动时会执行以下操作:
1. 启动内置进程级 Redis([miniredis](https://github.com/alicebob/miniredis)),因此无需额外安装任何组件。
2. 将 alerts JSON 和 events 日志解析并存入该存储中。
3. 预先解码一份读取快照,从而确保第一页的加载速度与第一百页一样快。
4. 提供由 HTMX 驱动的、纯粹的服务器端渲染 HTML。
数据仅在进程运行期间存在。退出二进制程序,数据即被清除。
不会向磁盘写入任何内容。
## 输入
Rustinel 会生成两个文件。这也是此处的两个输入源:
| 输入 | Rustinel 文件 | 具体内容 |
|-------|---------------|------------|
| alerts | `logs/alerts.json.` | 检测结果,每行一个 ECS NDJSON 文档 |
| events | `logs/rustinel.log.` | 运行日志;查看器会从中提取出 `normalized_json=` 遥测数据行 |
只有当 Rustinel 设置为 `trace` 级别日志时,events 输入才有数据。标准化的遥测数据行仅在 `trace` 级别下输出。在默认的 `info` 级别下,运行日志不包含 `normalized_json=` 行,因此事件时间线将为空。请参阅下文的第 2 步。
## 构建
```
go build -o rustinel-view ./cmd/rustinel-view # needs Go 1.26+
```
## 快速开始
**1. 构建程序**(如上所述)。
**2. 在 Rustinel 中开启 trace 级遥测。** 在 `config.toml` 中配置:
```
[logging]
level = "trace" # normalized events are trace-level only
directory = "logs"
```
在所有环节开启 `trace` 会产生大量冗余信息。只有 `engine` 目标需要它,因此使用目标过滤器可将其他部分保持在 `info` 级别:
```
[logging]
filter = "info,engine=trace"
directory = "logs"
```
**3. 运行 Rustinel 并触发一些它能检测到的操作。**
```
sudo ./rustinel run
```
它现在会写入 `logs/alerts.json.` 和 `logs/rustinel.log.`。
**4. 将查看器指向这两个文件。**
```
RV_ALERTS=logs/alerts.json. \
RV_EVENTS=logs/rustinel.log. \
./run.sh
```
打开 。
## 演练:一次 `whoami` 检测
Rustinel 内置了一个会在触发 `whoami` 时报警的 Sigma 演示规则。在 agent 以 trace 级别运行时,执行该命令:
```
whoami
```
你会得到:
- 位于 `logs/alerts.json.` 中的一条警报(`rule.name: "Example - Whoami Execution"`,`edr.rule.engine: Sigma`,`process.name: whoami`);
- 位于 `logs/rustinel.log.` 中相关的周边事件:`whoami` 进程的启动、其父 shell 以及附近的文件和网络活动,每一项都对应一条 `normalized_json=` 行。
加载这两个文件并打开页面。接下来你可以:
- 在杀伤链顶部找到 `whoami` 警报;
- 点击它以查看完整的 ECS 记录;
- 打开谱系视图(`GET /lineage/{pid}`)以查看是哪个 shell 派生了 `whoami` 以及该 shell 还触发了什么其他操作;
- 使用查询栏进行过滤,例如 `name:whoami` 或 `engine:Sigma severity:Low`。
谱系视图会基于单个 PID 重建进程树,将匹配的警报折叠到每个节点上,并允许你透视到任何子进程以继续深入挖掘。

## 直接运行
```
rustinel-view [flags]
```
| 参数 | 默认值 | 描述 |
|------|---------|-------------|
| `-addr` | `:8080` | HTTP 监听地址 |
| `-log-level` | `info` | `debug` \| `info` \| `warn` \| `error` |
| `-redis-port` | `0` | 内置 miniredis 端口(0 = 操作系统分配) |
| `-no-flush` | `false` | 启动时跳过 `FLUSHALL`(用于调试辅助) |
### run.sh
`run.sh` 可以一步完成构建和启动。通过环境变量传递快照路径,或者将它们放置在脚本同级目录下被 gitignore 忽略的本地 `.run.env` 文件中:
```
# .run.env
RV_ALERTS=/path/to/alerts.json
RV_EVENTS=/path/to/rustinel.log
# RV_ADDR=0.0.0.0:18080 # 在 LAN 上暴露(请先阅读安全说明)
```
```
./run.sh
```
覆盖配置项:`RV_ALERTS`、`RV_EVENTS`、`RV_ADDR`(默认为 `127.0.0.1:18080`)、`RV_LOG`。
## 安全性
本程序没有身份验证也没有 TLS。任何能够访问该端口的人都可以读取整个 EDR 快照。这就是为什么默认绑定仅限于回环地址(`127.0.0.1`)。请仅在受信任的网络中将其绑定到 `0.0.0.0` 或局域网地址。
## 查询语言
一种用于时间线的轻量级 KQL 风格语言。查询语句是由 `field:value` 测试以及纯词条子串搜索组成的布尔表达式,通过 `AND`、`OR`、`NOT` 和括号进行连接。这些字段在警报和事件之间是统一的,因此一个表达式即可同时过滤这两种类型的数据行。
字段:
```
kind pid parent_pid name image parent_image cmdline parent_cmdline
user category action severity rule engine target query record
src dst src_port dst_port proto
```
运算符:
| 形式 | 含义 |
|------|---------|
| `field:value` | 不区分大小写的匹配(IP 字段为精确匹配) |
| `field:val*` | 通配符;允许使用多个星号,例如 `image:*/tmp/*` |
| `field:>N` `>=` `<` `<=` | 数值比较,例如 `dst_port:>1024` |
| `field:*` | 该行存在此字段 |
| `NOT field:*` | 该行不存在此字段 |
| `"quoted phrase"` | 包含空格的值;`\` 用于转义下一个字符 |
| 纯词条 | 跨所有字段的子串搜索,例如 `whoami` 或 `8.8.8.8` |
通过 `AND`、`OR`、`NOT` 和括号进行组合。相邻的词条默认以 AND 方式关联,因此 `engine:Sigma severity:High` 与 `engine:Sigma AND severity:High` 效果相同。
示例:
```
category:Process AND NOT user:root
name:py* cmdline:"-c import socket"
engine:Sigma severity:High
proto:tcp dst_port:>1024 AND NOT dst:10.*
target:/etc/* action:FILE_DELETE
```
## HTTP 路由
| 路由 | 用途 |
|-------|---------|
| `GET /` | 主分析页面 |
| `GET /timeline` | 时间线数据行(HTMX 片段) |
| `GET /count` / `GET /density` | 结果计数 / 时间分桶直方图 |
| `GET /lineage/{pid}` | 指定 PID 的进程树谱系 |
| `GET /detail/{kind}/{id}` | 完整的记录详情(`a` 表示 alert / `e` 表示 event) |
| `GET /json/{kind}/{id}` | 记录的原始标准化 JSON |
| `GET /export` | 将当前结果集导出为 NDJSON |
| `GET /query/{schema,check,values}` | 查询自动补全与校验 |
## 项目结构
```
cmd/rustinel-view/ entrypoint: ingest, warm, serve
internal/ingest/ alert + event parsers
internal/store/ miniredis-backed store, query language, lineage
internal/server/ HTTP handlers, templates, embedded static assets
```
## 设计说明
以下几项设计决策确保了该查看器在处理真实快照时依然快速且安静:
- **读取操作不触碰 Redis。** miniredis 是事实数据源,但查询操作是服务于一个由原子指针维护的、不可变的、按时间排序的全量数据集解码结果。这将每次查询都要重新获取并 JSON 解码(约 200-400ms)的过程,转变为了切片扫描(约 5ms)。任何写入操作都会将指针置空,下一次读取时将自动重建;快照在启动时即被预热,因此即使是第一页也能快速加载。
- **突发折叠使用按类别的窗口。** 共享折叠键的相邻数据行会被折叠为一个带有计数和时间跨度的突发数据行。窗口大小是根据每个类别的实际突发特征定制的:警报为 10 秒,DNS 和网络为 5 秒,文件和进程活动为 2 秒。
- **噪音通过两层机制过滤。** 明确的垃圾数据(如写入 `/dev/null`)会在接入时被丢弃且永不下发到数据池,因此它会从时间线和谱系树中彻底消失。较弱的噪音(如针对 root 的 DNS 查询、所有字段均为空的事件)可通过开关进行丢弃,你也可以将其关闭以查看原始数据流。
- **谱系构建只需对快照遍历一次。** 通过 PID 和父 PID 为每一行建立索引,将向上追溯的父进程链作为一个单一的脉络进行遍历,然后对子孙节点进行 BFS 遍历并设置上限(深度 8,节点数 300),以防止 fork 炸弹撑爆页面。PID 复用的情况通过让最近可见的父进程生效来处理。
- **事件时间戳来源于日志前缀。** 遥测数据是通过截取每一行中 `normalized_json=` 之后的内容从运行日志中抓取出来的。因为内嵌的 JSON 只有秒级精度,所以排序时使用的是日志行自身前缀中的微秒级 RFC3339Nano 时间戳。
## 基于 Rustinel 构建
[Rustinel](https://github.com/Karib0u/rustinel) 是一个开源的、跨平台 EDR(支持 ETW / eBPF / Endpoint Security),具备 Sigma、YARA 和 IOC 检测能力,可输出 ECS NDJSON 警报。它采用 Apache 2.0 授权。该查看器所呈现的所有检测逻辑和遥测数据均来自 Rustinel;rustinel-view 只是读取了它的输出结果。EDR 的功劳应归于其作者 [Karib0u](https://github.com/Karib0u)。
## 授权协议
Copyright (C) 2026 Mihail Hristodor.
rustinel-view 采用 GNU Affero General Public License v3.0 授权协议。完整文本请查看 [LICENSE](LICENSE)。简而言之:你可以自由地使用、学习、修改和分享它,但如果你将修改后的版本作为网络服务运行,你必须根据相同的协议向你的用户公开你修改后的源代码。
标签:EDR, EVTX分析, Go, HTMX, Ruby工具, 安全运营, 扫描框架, 日志审计, 脆弱性评估