kev365/openrelik-worker-opensearch
GitHub: kev365/openrelik-worker-opensearch
OpenRelik 的数据处理 worker,用于将工作流结果自动索引到 OpenSearch 并上传至 Timesketch 取证分析平台。
Stars: 1 | Forks: 0
# openrelic-worker-opensearch
用于将 workflow 结果索引到 OpenSearch 并上传至 Timesketch 的 OpenRelik worker。通过单个 Docker 镜像提供两个 Celery 任务。
## 搜索指南
有关查询已上传数据的技巧,请参阅:
- [在 OpenSearch Dashboards 中搜索](docs/searching-opensearch.md)
- [在 Timesketch 中搜索](docs/searching-timesketch.md)
## 任务
### OpenSearch 上传
通过 bulk API 将来自上游 workflow 文件的文档索引到 OpenSearch 索引中。
- 自动检测文件格式:JSONL、JSON、CSV、TSV、Parquet 及其 gzip 压缩变体。
- 添加 OpenRelik 上下文字段(`openrelik_workflow_id`、`openrelik_source_file`、`openrelik_source_path`)。
- 可选的时间线功能:根据 datetime 字段将行展开为包含 `datetime` 和 `timestamp_desc` 列、适用于时间线的文档。
- 可选地在 OpenSearch Dashboards 中进行索引别名关联和创建 index pattern。
### Timesketch 上传
将文档上传到 Timesketch,并完成 sketch 和 timeline 的注册。
- 创建或复用 sketch 和 timeline。
- 从源数据列中自动检测 `datetime`、`message` 和 `timestamp_desc` 字段。
- 支持包含多个 datetime 字段的时间线处理(每一行的每个字段代表一个事件)。
- 在上传前将时间戳转换为 ISO8601 UTC 格式。
- 针对多文件上传的 timeline 名称前缀模式,或用于将文件合并为单个 timeline 的合并模式。
通过 `upload_method` 可以使用两种上传方法:
| 方法 | 工作原理 | 适用场景 |
|---|---|---|
| `stream`(默认) | 通过 `ImportStreamer` 将文档流式传输至 Timesketch | 默认适用于所有情况。Timesketch 负责管理索引及其 mapping |
| `direct_index` | 使用选定的 mapping 批量索引到 OpenSearch 中,然后通过 `generate_timeline_from_es_index` 将完成的索引注册到 Timesketch | 适用于宽表数据源(大约 300+ 个字段),或需要显式指定字段类型的情况 |
#### 为什么会有 `direct_index`
在 `stream` 路径中,Timesketch 会根据每个文档的顶层键数量自行设定索引字段限制。有两个因素会导致这种估算偏低:每个字符串都会同时映射 `keyword` 和 `wildcard` 子字段,因此一个源字段会占用三个 mapping 条目而不是一个;此外,嵌套对象会被计算为单个键,而 OpenSearch 会映射每一个叶节点。在 OpenSearch 默认限制总计 1000 个字段的情况下,实际的上限大约只有 333 个源字段。
超过此上限时,上传请求可能会被接受,但实际上不会建立任何索引。详见 [google/timesketch#3889](https://github.com/google/timesketch/issues/3889)。
`direct_index` 通过直接在此处创建索引来规避此问题,这样 mapping 及其 `index.mapping.total_fields.limit` 将来自于你选定的 mapping 文件,而不是自动推断出来的。它还允许你固定那些值不一致的字段类型,而不是让 OpenSearch 根据恰好最先被索引的文档进行推断,从而导致后续文档被拒绝。
注意:
- `direct_index` 直接写入 OpenSearch,因此 `opensearch_url` **必须指向 Timesketch 读取数据的同一个集群**,否则注册的 timeline 将是空的。
- 索引名称派生格式为 `ts__`。如果该索引已存在,注册将会失败,因为 Timesketch 不会接管它已经知道的索引。
- 如果有任何文档被拒绝,任务将失败且不会注册 timeline,而不是提供一个悄悄遗漏事件的 timeline。
## 环境变量
### OpenSearch 变量
| 变量 | 描述 |
|---|---|
| `OPENSEARCH_URL` | OpenSearch URL(默认:`https://opensearch:9200`) |
| `OPENSEARCH_USERNAME` | Basic auth 用户名(可选) |
| `OPENSEARCH_PASSWORD` | Basic auth 密码(可选) |
| `OPENSEARCH_API_KEY` | API key 认证(可选,优先级高于 Basic auth) |
| `OPENSEARCH_VERIFY_CERTS` | TLS 证书验证(默认:`true`) |
| `OPENSEARCH_DASHBOARDS_URL` | 用于创建 index pattern 的 Dashboards URL(可选,例如 `http://opensearch-dashboards:5601`) |
### Timesketch 变量
| 变量 | 描述 |
|---|---|
| `TIMESKETCH_SERVER_URL` | Timesketch API URL(例如 `http://timesketch-web:5000`) |
| `TIMESKETCH_SERVER_EXTERNAL_URL` | 供浏览器访问的 URL,用于报告中的 sketch 链接(如果与 `TIMESKETCH_SERVER_URL` 不同)(可选) |
| `TIMESKETCH_USERNAME` | Timesketch 用户名(可按任务覆盖) |
| `TIMESKETCH_PASSWORD` | Timesketch 密码(可按任务覆盖) |
## 任务配置
### OpenSearch 上传配置
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| `index_name` | text | 是 | 用于存储文档的 OpenSearch 索引名称 |
| `mapping` | select | 否 | 索引 mapping:`dynamic`(默认)、内置名称(例如 `kstrike_ual`)或 `custom`(workflow 中的 .mapping 文件) |
| `delimiter` | select | 否 | 源文件分隔符。`auto` 会根据文件扩展名自动检测。对于 CSV/TSV 请手动设置 |
| `source_timezone` | select | 否 | 源时间戳的时区。在索引前会转换为 UTC |
| `datetime_fields` | text | 否 | 用于时间线处理的逗号分隔时间戳字段(例如 `created_at,modified_at`)。每一行将按字段展开为单独的文档 |
| `datetime_format` | select | 否 | 时间戳格式。`auto` 会尝试自动解析 |
| `index_alias` | text | 否 | 在索引完成后将别名关联到索引 |
| `create_index_pattern` | checkbox | 否 | 在 OpenSearch Dashboards 中创建 index pattern(需要设置 `OPENSEARCH_DASHBOARDS_URL` 环境变量) |
| `batch_size` | text | 否 | 每次 bulk 请求的文档数(默认:1000,最大:10000) |
| `opensearch_url` | text | 否 | OpenSearch URL 覆盖(默认:`http://opensearch:9200`) |
| `username` | text | 否 | Basic auth 用户名 |
| `password` | text | 否 | Basic auth 密码 |
| `api_key` | text | 否 | API key 认证(优先级高于 Basic auth) |
| `disable_verify_certs` | checkbox | 否 | 跳过 TLS 证书验证 |
### Timesketch 上传配置
**Sketch:**
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| `sketch_name` | text | 否 | 新 sketch 的名称(如果设置了 sketch ID 则忽略此项) |
| `sketch_id` | text | 否 | 用于添加 timeline 的现有 sketch ID |
| `timeline_name` | text | 否 | timeline 名称的前缀(例如 `case42` 会变成 `case42 - filename.txt`)。启用合并后,将用作确切的 timeline 名称 |
| `merge_timelines` | checkbox | 否 | 使用上述名称将所有文件上传到一个 timeline 中 |
**字段映射:**
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| `ts_datetime_field` | text | 否 | 逗号分隔的时间戳字段名称(例如 `created_at,modified_at`)。如果源数据包含 `datetime` 列则会自动检测 |
| `ts_message_fields` | text | 否 | 将拼接为 Timesketch `message` 的逗号分隔字段(例如 `user,action,resource`)。如果源数据包含 `message` 列则会自动检测 |
**源格式:**
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| `delimiter` | select | 否 | 源文件分隔符。`auto` 会根据文件扩展名自动检测 |
| `ts_datetime_format` | select | 否 | 时间戳格式。`auto` 会尝试自动解析。值将被转换为 ISO8601 |
| `source_timezone` | select | 否 | 源时间戳的时区。在上传前会转换为 UTC |
**连接:**
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| `timesketch_url` | text | 是 | Timesketch API URL(默认:`http://host.docker.internal`) |
| `timesketch_username` | text | 是 | Timesketch 用户名 |
| `timesketch_password` | text | 是 | Timesketch 密码 |
**上传方法:**
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| `upload_method` | select | 否 | `stream`(默认)或 `direct_index`。详见 [Timesketch 上传](#timesketch-upload) |
| `mapping` | select | 否 | `direct_index` 使用的索引 mapping。在 `stream` 路径下被忽略 |
| `opensearch_url` | text | 否 | 仅限 `direct_index`。必须是 Timesketch 读取数据的同一个集群。回退至 `OPENSEARCH_URL` |
| `auth_mode` | text | 否 | 仅限 `direct_index`。可选 `none`、`basic` 或 `apikey`。留空则会根据环境自动检测 |
| `username` | text | 否 | 仅限 `direct_index`。OpenSearch 用户名,回退至 `OPENSEARCH_USERNAME` |
| `password` | text | 否 | 仅限 `direct_index`。OpenSearch 密码,回退至 `OPENSEARCH_PASSWORD` |
## 内置 Mapping
| 名称 | 描述 |
|---|---|
| `kstrike_ual` | KStrike UAL 解析器输出,包含带类型的日期字段(`insertdate`、`lastaccess`) |
| `github_audit` | GitHub 审计日志。针对该格式的字段广度将 `total_fields.limit` 提高至 3000,镜像 Timesketch 自有的 `keyword`/`wildcard` 字符串模板以确保搜索行为完全一致,并将 `permission` 固定为 `keyword` 类型,因为它在大多数记录中是字符串,而在少数记录中是布尔值 |
内置 mapping 会从 `src/mappings/*.json` 中自动发现。它们为导出任务以及当 `upload_method` 为 `direct_index` 时的 Timesketch 任务定义了 OpenSearch 索引字段类型。使用 `dynamic` 让 OpenSearch 自动检测类型,或者使用 `custom` 上传你自己的 `.mapping` 文件。
mapping 文件可以在 `mappings` 旁边包含一个 `settings` 块;它将被直接传递给索引创建过程,这就是提高 `total_fields.limit` 的实现方式。
## 本地开发
```
# 使用 test 和 timesketch extras 安装依赖
uv sync --group test --extra timesketch
# 运行测试
uv run pytest -v
```
标签:Docker, OpenRelik, Python, Timesketch, 安全取证, 安全防御评估, 数据导入, 无后门, 日志索引, 请求拦截, 逆向工具