feat(go): expose retained incident views
This commit is contained in:
parent
eff31b3a82
commit
6355fb403d
6 changed files with 166 additions and 7 deletions
|
|
@ -10,8 +10,11 @@ import (
|
|||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
|
@ -25,6 +28,7 @@ import (
|
|||
"codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/eventlog"
|
||||
"codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/events"
|
||||
"codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/health"
|
||||
"codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/incident"
|
||||
"codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/model"
|
||||
"codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/schema"
|
||||
"codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/sdnotify"
|
||||
|
|
@ -57,6 +61,8 @@ func run() error {
|
|||
rulesList := flag.Bool("rules-list", false, "emit the active event-rule catalog as JSON and exit")
|
||||
rulesShow := flag.Int("rules-show", 0, "emit one event rule by SID as JSON and exit")
|
||||
correlateFile := flag.String("correlate", "", "correlate a JSON array of incident summaries and exit")
|
||||
incidentsList := flag.Bool("incidents-list", false, "list retained Go incidents as JSON and exit")
|
||||
incidentShow := flag.String("incident-show", "", "show one retained Go incident timeline as JSON and exit")
|
||||
ebpfExec := flag.Bool("ebpf-exec", false, "enable the native execve eBPF source (fails open to polling)")
|
||||
ebpfSyscall := flag.Bool("ebpf-syscall", false, "enable the native security-syscall eBPF source (fails open to polling)")
|
||||
checkHealth := flag.Bool("health", false, "check the state heartbeat as JSON and exit")
|
||||
|
|
@ -91,6 +97,19 @@ func run() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
if *incidentsList || *incidentShow != "" {
|
||||
directory := *snapshotDir
|
||||
if directory == "" {
|
||||
directory = *stateDir
|
||||
}
|
||||
if directory == "" {
|
||||
directory = "/var/lib/enodia-sentinel-go"
|
||||
}
|
||||
if *incidentsList {
|
||||
return writeIncidentList(directory, os.Stdout)
|
||||
}
|
||||
return writeIncidentView(directory, *incidentShow, os.Stdout)
|
||||
}
|
||||
|
||||
cfg, err := config.Load(*configPath)
|
||||
if err != nil {
|
||||
|
|
@ -307,6 +326,62 @@ func run() error {
|
|||
return runner.Run(ctx, *once, emit)
|
||||
}
|
||||
|
||||
func writeIncidentList(directory string, output io.Writer) error {
|
||||
index, err := incident.LoadIndex(directory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items := make([]*incident.Record, 0, len(index))
|
||||
for _, item := range index {
|
||||
items = append(items, item)
|
||||
}
|
||||
sort.Slice(items, func(i, j int) bool { return items[i].LastTS > items[j].LastTS })
|
||||
return json.NewEncoder(output).Encode(items)
|
||||
}
|
||||
|
||||
func writeIncidentView(directory, id string, output io.Writer) error {
|
||||
index, err := incident.LoadIndex(directory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
item := index[id]
|
||||
if item == nil {
|
||||
return fmt.Errorf("no such incident: %s", id)
|
||||
}
|
||||
timeline := make([]map[string]any, 0, len(item.Snapshots))
|
||||
snapshots := make([]snapshot.Report, 0, len(item.Snapshots))
|
||||
for _, name := range item.Snapshots {
|
||||
path := filepath.Join(directory, strings.TrimSuffix(name, ".log")+".json")
|
||||
report, err := snapshot.LoadReport(path)
|
||||
if err != nil {
|
||||
timeline = append(timeline, map[string]any{"snapshot": name, "time": "?", "missing": true})
|
||||
continue
|
||||
}
|
||||
snapshots = append(snapshots, report)
|
||||
signatures := make([]string, 0, len(report.Alerts))
|
||||
pids := make([]int, 0, len(report.Processes))
|
||||
seen := map[string]bool{}
|
||||
for _, alert := range report.Alerts {
|
||||
if !seen[alert.Signature] {
|
||||
seen[alert.Signature] = true
|
||||
signatures = append(signatures, alert.Signature)
|
||||
}
|
||||
}
|
||||
for _, process := range report.Processes {
|
||||
pids = append(pids, process.PID)
|
||||
}
|
||||
timeline = append(timeline, map[string]any{
|
||||
"snapshot": name, "time": report.Time, "severity": report.Severity,
|
||||
"signatures": signatures, "pids": pids,
|
||||
})
|
||||
}
|
||||
sort.Slice(timeline, func(i, j int) bool { return timeline[i]["time"].(string) < timeline[j]["time"].(string) })
|
||||
return json.NewEncoder(output).Encode(map[string]any{
|
||||
"schema": "enodia.incident.view.v1", "incident": item,
|
||||
"timeline": timeline, "snapshots": snapshots,
|
||||
})
|
||||
}
|
||||
|
||||
func monitorExecSource(
|
||||
ctx context.Context,
|
||||
source execEventReader,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue