add rule generalization, sized LLM defaults, ops docs, and UI polish

Feedback rules can now be suggested for merging when a human has already
reviewed the same pattern multiple times (never guesses about new values).
LLM defaults sized for constrained VRAM instead of llama3.3/llama3.1.
Adds systemd units, a state backup script, and reverse-proxy auth guidance
for production deployment. Findings view is collapsible and sorts
suggested-review rows to the top for a quicker overview.
This commit is contained in:
larssand
2026-09-23 13:04:53 +02:00
parent c3e482f9ab
commit bf1109be81
14 changed files with 446 additions and 14 deletions

View File

@@ -186,7 +186,8 @@ stored under `graylog_stream_profiles` in `state/fgai-config.json`.
The Settings page also shows recommended stream profiles built from observed
field coverage and cardinality. These recommendations use deterministic
discovery first, then can optionally be refined by a local Ollama profile advisor
model such as `qwen3:8b` or `qwen3:14b`. Enable `Ollama profile advisor` and set
model such as `qwen2.5:3b-instruct` (default) or a larger model on more capable
hardware. Enable `Ollama profile advisor` and set
`Profile advisor model` in Settings. Advisor output must be valid JSON and is
validated against fields actually seen in the stream before it can be applied.
Unknown fields, raw message fields, internal `fgai_*` fields, and unknown
@@ -671,16 +672,28 @@ fgai suggest-blocks --logs logs/fg_syslog.jsonl --execute --expiry-minutes 60
Optional local LLM summary through Ollama:
```bash
ollama pull llama3.3
ollama pull qwen2.5:7b-instruct
fgai analyze-logs --logs logs/fg_syslog.jsonl --llm --llm-timeout 300
```
For slower machines or large models:
For slower machines, pick a smaller model instead of raising the timeout:
```bash
OLLAMA_MODEL=llama3.1 OLLAMA_TIMEOUT=300 fgai analyze-logs --logs logs/fg_syslog.jsonl --llm
OLLAMA_MODEL=qwen2.5:3b-instruct OLLAMA_TIMEOUT=300 fgai analyze-logs --logs logs/fg_syslog.jsonl --llm
```
**Sizing the model to your GPU**: the defaults (`qwen2.5:7b-instruct` for the
dashboard assessment, `qwen2.5:3b-instruct` for the profile advisor) target a
6-8 GB VRAM card at Q4 quantization with some headroom for context. On a
memory-constrained card (6 GB or less, e.g. a mobile/low-power GPU), drop both
to `qwen2.5:3b-instruct` or smaller — tight VRAM causes Ollama to spill onto
CPU, which shows up as slow or timed-out assessments rather than an obvious
out-of-memory error. On a workstation-class GPU with 16 GB+ VRAM, a larger
model such as `qwen3:14b` for the dashboard assessment will give noticeably
better analysis quality. The profile advisor only ever classifies a handful
of ambiguous fields per call (see the Graylog MCP section below), so it stays
small and fast even on constrained hardware without much quality loss.
## Optional FortiGate Input
## Synthetic Windows Test Input
@@ -723,7 +736,7 @@ end
- `FORTIGATE_VERIFY_TLS`: `true` or `false`, defaults to `true`.
- `FGAI_ALLOWLIST`: comma-separated IPs/CIDRs never to block.
- `OLLAMA_HOST`: defaults to `http://127.0.0.1:11434`.
- `OLLAMA_MODEL`: defaults to `llama3.1`.
- `OLLAMA_MODEL`: defaults to `qwen2.5:7b-instruct`. Size this to your GPU's VRAM — see the sizing note above.
- `OLLAMA_TIMEOUT`: Ollama request timeout in seconds, defaults to `180`.
- `FGAI_LLM`: set to `1` to enable dashboard Ollama analyst notes.
- `FGAI_LLM_INTERVAL`: seconds between dashboard LLM notes, defaults to `300`.
@@ -743,3 +756,70 @@ The agent separates detection from enforcement:
- Source IPs must be globally routable and outside the allowlist.
- Blocking requires `--execute`.
- The FortiGate API call is limited to the quarantine/banned user monitor endpoint.
## Production Deployment
`start.sh` is convenient for a single machine you're logged into, but has no
supervision (a crashed process just stays dead) and no boot-time start.
### Running under systemd
Unit files for the monitor, dashboard, and optional local syslog listener are
in `systemd/`, with full setup and update instructions in
[`systemd/README.md`](systemd/README.md). They mirror `start.sh`'s exact
process arguments, add `Restart=on-failure`, and run under a dedicated
`signalscope` system user with `ProtectSystem=strict`/`ProtectHome=true`
sandboxing rather than as root.
### Backup and Restore
Everything SignalScope needs to restore — runtime configuration, feedback
rules, behavior baselines, incident state, and caches — lives under `state/`.
Nothing else needs backing up; logs and exports are reproducible/disposable.
```bash
scripts/backup_state.sh # writes to ./backups/signalscope-state-<timestamp>.tar.gz
scripts/backup_state.sh /path/to/backups # or a custom destination
```
SQLite databases (`fgai-baseline.sqlite3`, `signalscope-history.sqlite3`,
`signalscope-status-cache.sqlite3`) are backed up with `sqlite3 .backup`,
which takes a consistent snapshot even while the monitor is actively writing
to them, rather than a plain file copy that can capture a torn/inconsistent
write mid-transaction. JSON files (`fgai-config.json`,
`signalscope-feedback.json`, `signalscope-incidents.json`,
`threat-intel-cache.json`) are already written atomically by SignalScope
(temp file + rename) and are copied directly.
To restore: stop the monitor and dashboard, extract the archive's contents
over `state/`, then restart.
### Exposing the Dashboard Beyond Loopback
The dashboard has **no built-in authentication** — its entire access-control
model is "only accept connections from `127.0.0.1`" (`start.sh`'s and the
systemd unit's default `--host 127.0.0.1`). API routes that change state
(`POST /api/config`, `/api/feedback`, `/api/feedback/bulk`, incident actions,
and more) additionally check that the client's socket address is loopback,
regardless of what `Host` header a request carries.
If you need to reach the dashboard from another machine, do not change
`--host` to `0.0.0.0` directly — put an authenticating reverse proxy in front
of it instead and keep SignalScope itself bound to loopback. For example,
with Caddy:
```
signalscope.internal.example.com {
basicauth {
analyst $2a$14$... # generate with: caddy hash-password
}
reverse_proxy 127.0.0.1:8088
}
```
Or nginx with `auth_basic` / an `auth_request` subrequest to whatever SSO the
rest of your infrastructure already uses. Either way, terminate TLS at the
proxy and keep the token/credential prompts outside SignalScope's own process
— since state-changing routes only trust the *proxy's* loopback connection,
not the original client, the proxy is the only thing standing between a
public request and dashboard write access once `--host` is opened up.