diff --git a/patterns/default.yaml b/patterns/default.yaml index 521b695..6f99461 100644 --- a/patterns/default.yaml +++ b/patterns/default.yaml @@ -81,6 +81,53 @@ patterns: severity: INFO description: IP address change or DHCP event + # ── System / journald patterns ───────────────────────────────────────────── + + - name: systemd_fail + pattern: "(Failed to start|failed with result|entered failed state|start request repeated too quickly|Main process exited)" + severity: ERROR + description: systemd service failed to start or crashed + + - name: oom_kill + pattern: "(Killed process|oom.kill|oom_kill_process|Out of memory: Kill|memory cgroup out of memory)" + severity: CRITICAL + description: Kernel OOM killer terminated a process + + - name: disk_hw_error + pattern: "(ata[0-9]|sd[a-z]|nvme[0-9]).*(error|failed|reset|timeout|exception|EH|FAILED COMMAND)" + severity: ERROR + description: Storage device hardware error or reset + + - name: fs_error + pattern: "(EXT4-fs error|XFS.*error|BTRFS.*error|I/O error|blk_update_request.*error|buffer I/O error)" + severity: ERROR + description: Filesystem or block I/O error + + - name: kernel_error + pattern: "(kernel: BUG|kernel panic|Oops:|general protection fault|Call Trace|RIP:.*[0-9a-f]{16})" + severity: CRITICAL + description: Kernel bug, panic, or oops — system may be unstable + + - name: ssh_brute + pattern: "(Failed password|Invalid user|authentication failure|Connection closed by authenticating user).*(sshd|ssh)" + severity: WARN + description: SSH authentication failure — possible brute force + + - name: container_crash + pattern: "(container.*exited|oci runtime.*error|podman.*error|docker.*error|container.*killed|OCI.*failed)" + severity: ERROR + description: Container runtime error or unexpected exit + + - name: smart_error + pattern: "(smartd|SMART.*error|reallocated sector|pending sector|uncorrectable sector|Current_Pending_Sector)" + severity: CRITICAL + description: SMART disk health warning — potential drive failure + + - name: nfs_error + pattern: "(nfs.*error|nfs.*timeout|RPC.*timed out|nfs4.*server.*not responding|mount.*nfs.*failed)" + severity: ERROR + description: NFS mount or RPC timeout + # Add device/service-specific patterns below this line: - name: qbit_tracker_error diff --git a/patterns/sources.yaml b/patterns/sources.yaml index 1421c7d..0bc89b5 100644 --- a/patterns/sources.yaml +++ b/patterns/sources.yaml @@ -1,4 +1,6 @@ # Turnstone log sources — edit this file to add or remove services. +# NOTE: the system-journal entry requires export_journal.sh to run on the HOST +# before the container ingest step. See crontab setup instructions in the README. # Run ingest manually: # sudo podman exec turnstone python scripts/ingest_corpus.py \ # --sources /patterns/sources.yaml --db /data/turnstone.db @@ -8,6 +10,10 @@ # services that are temporarily down. sources: + # ── System journal (exported by export_journal.sh on the host) ──────────── + - id: system-journal + path: /data/journal-export.jsonl + # ── Download ───────────────────────────────────────────────────────────── - id: qbittorrent path: /opt/qbittorrent/config/data/logs/qbittorrent.log diff --git a/scripts/export_journal.sh b/scripts/export_journal.sh new file mode 100644 index 0000000..8493c1f --- /dev/null +++ b/scripts/export_journal.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Export recent journald entries to a JSONL file the Turnstone container can ingest. +# +# Run this on the HOST before the container ingest step. The output file lands in +# /opt/turnstone/data/ which is bind-mounted at /data/ inside the container. +# +# Priority filter 0-5 (emerg→notice) skips debug/info noise while keeping +# all warnings, errors, and service lifecycle events. +# +# Usage (standalone): +# sudo bash /opt/turnstone/scripts/export_journal.sh +# +# Typical cron (combined with ingest — see crontab comment below): +# */15 * * * * bash /opt/turnstone/scripts/export_journal.sh && \ +# podman exec turnstone python scripts/ingest_corpus.py \ +# --sources /patterns/sources.yaml --db /data/turnstone.db \ +# >> /var/log/turnstone-ingest.log 2>&1 + +set -euo pipefail + +OUT=/opt/turnstone/data/journal-export.jsonl + +# 20-minute window (slightly wider than the 15-min cron interval) ensures no +# gaps between runs. Ingest is idempotent via entry_id hash, so overlap is safe. +journalctl \ + --output=json \ + --priority=0..5 \ + --since "20 minutes ago" \ + --no-pager \ + > "${OUT}" + +echo "Exported $(wc -l < "${OUT}") journal entries to ${OUT}"