netzbegruenung/salt-security-agent
GitHub: netzbegruenung/salt-security-agent
LLM 驱动的 SaltStack 环境安全扫描代理,定时巡检 minion 运行状态并利用大语言模型进行威胁分析与配置比对。
Stars: 0 | Forks: 0
# Salt Security Agent
一个由 LLM 驱动的安全扫描代理,专为 Saltstack 管理的环境设计。该代理会
定期选择 Salt minion,通过 Salt CLI 获取其正在运行的进程列表,
并执行由 LLM 驱动的调查,该调查可以检查 minion 上的文件系统路径,
并将其与 Salt 仓库进行比对。
## 架构
```
Celery Beat ──► dispatch_scans (task)
│
▼
pick_next_minion (Redis sorted set)
│
▼
scan_minion (task, runs on worker)
│
├─ get_processes ──► salt cmd.run 'ps aux' (container PIDs filtered)
│
└─ run_agent (LLM tool-calling loop)
│
├─ ls_minion(path) → salt cmd.run 'ls -la '
├─ list_repo_files(path) → os.scandir(repo_path / path)
└─ read_repo_file(path) → open(repo_path / path)
```
- **Broker 与状态**:Redis
- **调度**:Celery Beat 每分钟触发一次 `dispatch_scans`;每次触发会扫描
上次扫描时间早于配置的 `scan_period` 的最旧的 minion。Worker
并发限制控制着并行扫描的数量。
- **Minion 选择**:Redis 有序集合 (`salt:scanned`) 跟踪上次扫描的时间戳。
会选择上次扫描时间早于扫描周期的、最旧被扫描的 minion;
从未被扫描过的 minion 始终符合条件。
- **LLM 通信**:通过 `httpx` 发起原始 HTTP 请求,连接到任何兼容 OpenAI 的 chat completions
endpoint。
## 环境要求
- Python 3.11+
- Redis
- Salt master(需要提供可用的 `salt` 和 `salt-key` CLI)
- 一个兼容 OpenAI 的 LLM API endpoint
## 安装
```
pip install -e .
```
## 配置
复制并编辑 `config.toml`:
```
[scanning]
parallel_hosts = 3 # Celery worker concurrency
scan_period = "daily" # how often each minion is scanned: hourly, daily, weekly, monthly
# report_directory = "/var/lib/salt-security-agent/reports" # 可选;参见 Reports 部分
[llm]
url = "https://api.openai.com/v1"
access_token = "sk-..."
model = "gpt-4o"
threat_model_path = "threat_models/default_threat_model.md"
task_path = "tasks/default_task.md"
[salt]
repo_path = "/srv/salt" # Absolute path to the Salt state repository on the master
[celery]
broker_url = "redis://localhost:6379/0"
result_backend = "redis://localhost:6379/0"
# 可选。如果存在,agent 的 send_alert tool 发出的警报也会
# 通过使用经过身份验证的 SMTP 通过电子邮件发送。省略整个部分以禁用。
[smtp]
host = "smtp.example.com"
port = 587
username = "alerts@example.com"
password = "changeme"
from_address = "alerts@example.com"
to_address = "soc@example.com"
use_starttls = true # STARTTLS; set false for implicit TLS / SMTPS (typically port 465)
```
### 威胁模型与任务
- **`threat_model_path`** — 描述需要寻找哪些内容的 Markdown 文件。默认文件位于
`threat_models/default_threat_model.md`。
- **`task_path`** — 包含提供给代理的分步指令的 Markdown 文件。
默认文件位于 `tasks/default_task.md`。
如果这两个路径不是绝对路径,将相对于当前工作目录进行解析。
#### 针对 Minion 的覆盖配置
在每次扫描之前,代理会在配置的目录中查找特定于 minion 的文件,
如果存在则使用它。解析顺序如下:
1. **精确匹配** — `/.md`。
2. **通配符匹配** — 任何 `/.md`,其中文件名主干中的 `_`
充当 `*` 通配符。例如,`keycloak_.md` 匹配 minion `keycloak01.example.com`
(模式为 `keycloak*`)。如果多个通配符文件匹配,则主干最长
的文件胜出(最为具体)。
3. **默认值** — `/default.md`。
在上述配置下,名为 `web-01.example.com` 的 minion 将依次匹配
`threat_models/web-01.example.com.md`,接着是类似 `threat_models/web_.md`
(`web*`)的任何通配符文件,最后是 `threat_models/default.md`。相同的解析规则也适用于 `tasks/`。
### 报告
默认情况下,扫描报告会输出到 worker 的标准输出 (stdout)。设置 `scanning.report_directory`
以将其持久化到磁盘上。每份报告会写入到
`{report_directory}/{isodate}/{minion_id}`(例如:`/var/lib/salt-security-agent/reports/2026-06-07/web-01`)。
父目录会自动创建;如果在同一天重新运行扫描,将覆盖该 minion 的
上一次报告。
## 用法
启动 Celery worker(负责处理实际的扫描任务):
```
salt-security-agent worker
```
启动 Celery Beat 调度器(按配置的速率分发扫描任务):
```
salt-security-agent beat
```
在两个独立的终端中运行它们(或者使用诸如 systemd 或 supervisord 之类的进程管理工具)。
`examples/systemd/` 目录下提供了 systemd 单元示例。
可选标志:
```
salt-security-agent worker --config /etc/salt-security-agent/config.toml --loglevel DEBUG
salt-security-agent beat --config /etc/salt-security-agent/config.toml --loglevel INFO
```
## 文件结构
```
salt-security-agent/
├── config.toml # Main configuration
├── pyproject.toml
├── tasks/
│ └── default_task.md # Default agent task instructions
├── threat_models/
│ └── default_threat_model.md # Default threat model
└── agent/
├── cli.py # Entry point (click)
├── config.py # Config loading (tomllib + dataclasses)
├── celery_app.py # Celery app + Beat schedule
├── tasks.py # Celery tasks
├── scheduler.py # Minion picker (Redis)
├── llm_agent.py # LLM tool-calling loop (httpx)
└── tools/
├── salt_tools.py # ls_minion, get_processes
└── repo_tools.py # list_repo_files, read_repo_file
```
## Minion 选择的工作原理
所有已接受的 minion 都是通过 `salt-key -L --out=json` 发现的。Redis 将每个
minion 上次扫描的 Unix 时间戳存储在有序集合中。`dispatch_scans` 每分钟运行一次,并且会:
1. 排除当前正在扫描的 minion(`salt:in_progress`)。
2. 过滤出*已逾期*的 minion —— 即上次扫描早于 `scan_period`,或者
从未被扫描过。
3. 从逾期集合中挑选出时间戳最旧的一个;如果没有逾期的 minion,
则此次触发不执行任何操作。
4. 将选定的 minion 添加到 `salt:in_progress` 中,并设置 1 小时的 TTL(如果
worker 崩溃则自动释放)。
5. 扫描成功后,更新时间戳并释放锁。
在有 N 个 minion 的情况下,这自然会产生大约 N 次扫描/每个扫描周期,并均匀分布在
整个周期内。突发容量受 `parallel_hosts`(worker 并发数)的限制。
标签:Celery, HTTP工具, Petitpotam, Redis, Saltstack, 安全巡检, 搜索引擎查询, 无线安全, 网络安全审计, 运行时操纵, 逆向工具