Monitors overview¶
Fenrir runs nine monitors in parallel. Each one is a small async task that produces RawEvent objects on a single shared queue. The rule engine consumes from the queue, classifies, and persists.
The 9 monitors¶
| Monitor | Type | Source | Cadence | Typical events |
|---|---|---|---|---|
auth_monitor |
log tailer | /var/log/auth.log |
real-time | SSH success/fail, sudo, brute force |
honeypot_monitor |
log tailer | /var/log/nginx/honeypot.log |
real-time | Hits on fake admin paths |
fail2ban_monitor |
log tailer | /var/log/fail2ban.log |
real-time | Bans, unbans, repeat offenders |
nginx_monitor |
log tailer | /var/log/nginx/access.log |
real-time | Suspicious paths, scanners |
ufw_monitor |
log tailer | /var/log/ufw.log |
real-time | Firewall drops, port scans |
kernel_monitor |
log tailer | /var/log/kern.log |
real-time | USB, OOM, segfault, AppArmor deny |
package_monitor |
log tailer | /var/log/dpkg.log |
real-time | apt install/upgrade/remove |
baseline_monitor |
snapshotter | ss, systemctl, /etc/passwd, find |
tunable | New port, new service, new user, new setuid |
cve_monitor |
snapshotter | apt list --upgradable |
tunable | Pending -security upgrades |
Two flavors of monitor¶
Log tailers¶
These extend BaseMonitor and read a single file via LogTailer (an aiofiles-based tail -f with rotation detection). They:
- Open the file, seek to end-of-file (so previously-existing lines are NOT replayed).
readline()in a loop. New lines are passed to the monitor'sparse_line()method.- Detect log rotation (inode change) and re-open transparently.
Latency: typically < 1 second from log write to RawEvent on the queue.
Periodic snapshotters¶
These run a snapshot function on a timer, save the current state to a JSON file, compare to the previous snapshot, and emit one event per delta. They:
- Sleep for
intervalseconds (configurable per monitor). - Call all snapshotter functions (e.g.
snapshot_listening_ports,snapshot_services). - Diff against the saved JSON state file in
data/. - Emit one
RawEventper added/removed entry. - Save the new state.
The first run after install only establishes the baseline — no alerts are produced. Drift detection starts on run #2.
Event flow¶
Monitor.parse_line(line) → RawEvent(source=..., parsed_data={...})
↓
event_queue.put()
↓
rule_engine.classify(raw)
↓
ThreatEvent(severity, ...)
↓
┌──────┴──────┐
↓ ↓
DB write HIGH/CRITICAL?
↓
Investigation
Telemetry per monitor¶
Each monitor exposes:
name— short identifierlog_path(or[periodic:<seconds>s]for snapshotters)events_count— number ofRawEventproduced since startup
You can see the count on the web dashboard under "Monitor health" (coming soon — for now grep the journal: journalctl -u p3guardian | grep events_count).
Disabling a monitor¶
Comment out the line in p3guardian/app.py:
self.monitors = [
AuthMonitor("auth", settings.auth_log, self.event_queue),
# HoneypotMonitor("honeypot", ...), ← disabled
Fail2banMonitor("fail2ban", settings.fail2ban_log, self.event_queue),
...
]
Then systemctl restart p3guardian. Future Fenrir versions will move this to a config flag.
Adding a custom monitor¶
Subclass BaseMonitor (for tail-based) or follow the periodic-monitor pattern (see cve_monitor.py as a template). Implement parse_line() (returns RawEvent | None). Add a corresponding handler in analyzers/rule_engine.py for your parsed_data["type"] values. Register the monitor in app.py.
Read on:
- Log monitors — auth, honeypot, fail2ban, nginx, ufw, kernel
- Package monitor
- Baseline monitor
- CVE monitor