Linux audit (auditd) source and LogStream internal logs
Audit source (source_type=audit), off until enabled in Settings > Sources: - follows /var/log/audit/audit.log (path configurable, AUDIT_LOG), with rotation (end of audit.log.1 read first) and an offset checkpoint in /data/audit-state.json; last AUDIT_BACKFILL (1h) read on first enable - groups the records of an event by msg=audit(time:serial), closed on EOE, after 2 s without a record, or once the log has moved 2 s past it - fields audit_type, audit_serial, auid, user (enriched name, host /etc/passwd or uid), exe, res, key, audit_raw; app = audit_type, host = node= / AUDIT_HOST / /host/etc/hostname; summary "who: what [key] → result"; warning when res=failed / success=no, info otherwise - hexadecimal values (proctitle, cmd, exec arguments…) decoded Internal logs (source_type=logstream, host=logstream, app=component): - logEvent() for VictoriaLogs unreachable / back, lost batches, spooled batches refused, syslog listener errors, Docker API and container streams lost, failing sources, start and stop - on by default from warning; switch and minimum level in Settings > Sources (/data/selflog.json) - bounded queue, never waits for the Store, same event at most once per 30 s with a count of the repeats: a failing Store cannot loop UI: violet (audit) and turquoise (LogStream) left edge and tint, AUDIT / LS badge before the app, raw audit record under the summary (folded in compact density until the row is opened), Audit and LogStream in the source filter, FR/EN translations. docker-compose: optional /var/log/audit, /etc/passwd and /etc/hostname mounts. README (EN/FR): host prerequisites (log_group, ACL for UID 10001). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f3373e1f66
commit
076c01aac4
20 files changed
+1948
-36
No files matched your search
@@ -40,6 +40,9 @@ type config struct {
|
||||
hostFill time.Duration
|
||||
kmsgDevice string
|
||||
kmsgFill time.Duration
|
||||
auditLog string
|
||||
auditHost string
|
||||
auditFill time.Duration
|
||||
batchSize int
|
||||
queueSize int
|
||||
flushEvery time.Duration
|
||||
@@ -119,6 +122,9 @@ func main() {
|
||||
hostFill: getenvDuration("HOST_LOGS_BACKFILL", time.Hour),
|
||||
kmsgDevice: getenv("DMESG_DEVICE", "/dev/kmsg"),
|
||||
kmsgFill: getenvDuration("DMESG_BACKFILL", time.Hour),
|
||||
auditLog: getenv("AUDIT_LOG", "/var/log/audit/audit.log"),
|
||||
auditHost: os.Getenv("AUDIT_HOST"),
|
||||
auditFill: getenvDuration("AUDIT_BACKFILL", time.Hour),
|
||||
batchSize: getenvInt("BATCH_SIZE", 1000),
|
||||
queueSize: getenvInt("QUEUE_SIZE", 100000),
|
||||
flushEvery: time.Duration(getenvInt("FLUSH_MS", 1000)) * time.Millisecond,
|
||||
@@ -138,14 +144,16 @@ func main() {
|
||||
var spool *Spool
|
||||
if cfg.spoolMax > 0 {
|
||||
if spool, err = OpenSpool(filepath.Join(cfg.dataDir, "spool"), cfg.spoolMax); err != nil {
|
||||
log.Printf("disk buffer disabled: %v", err)
|
||||
log.Printf("disk buffer disabled: %v", err) // before the self-logs exist
|
||||
spool = nil
|
||||
}
|
||||
}
|
||||
store := NewStore(cfg.vlogsURL, cfg.batchSize, cfg.queueSize, cfg.flushEvery, spool)
|
||||
// The store stops after everything else, so the stop event is still stored.
|
||||
storeCtx, stopStore := context.WithCancel(context.Background())
|
||||
storeDone := make(chan struct{})
|
||||
go func() {
|
||||
store.Run(ctx)
|
||||
store.Run(storeCtx)
|
||||
close(storeDone)
|
||||
}()
|
||||
|
||||
@@ -163,6 +171,20 @@ func main() {
|
||||
hub.Publish(e)
|
||||
})
|
||||
}
|
||||
// Internal events of LogStream (host=logstream), WARNING and above unless
|
||||
// changed in Settings > Sources.
|
||||
selfLog = NewSelfLog(cfg.dataDir, func(e *Entry) {
|
||||
store.Enqueue(e)
|
||||
hub.Publish(e)
|
||||
})
|
||||
selfCtx, stopSelf := context.WithCancel(context.Background())
|
||||
selfDone := make(chan struct{})
|
||||
go func() {
|
||||
selfLog.Run(selfCtx)
|
||||
close(selfDone)
|
||||
}()
|
||||
logEvent("main", 5, "LogStream started")
|
||||
|
||||
tcpMaxConns = getenvInt("SYSLOG_TCP_MAX_CONNS", tcpMaxConns)
|
||||
if d := getenvDuration("SYSLOG_TCP_IDLE", tcpIdle); d > 0 {
|
||||
tcpIdle = d
|
||||
@@ -180,7 +202,7 @@ func main() {
|
||||
if cfg.dockerLogs {
|
||||
dm, err := NewDockerManager(cfg.dockerHost, cfg.dataDir, cfg.backfill, sink)
|
||||
if err != nil {
|
||||
log.Printf("docker logs disabled: %v", err)
|
||||
logEvent("docker", 3, "Docker logs disabled: %v", err)
|
||||
} else {
|
||||
go dm.Run(ctx)
|
||||
api.docker = dm
|
||||
@@ -193,6 +215,10 @@ func main() {
|
||||
// Kernel messages of the host (/dev/kmsg), off until enabled in Settings > Sources.
|
||||
api.dmesg = NewDmesg(cfg.kmsgDevice, cfg.dataDir, cfg.kmsgFill, sink)
|
||||
go api.dmesg.Run(ctx)
|
||||
// Linux audit log of the host (auditd), off until enabled in Settings > Sources.
|
||||
api.audit = NewAudit(cfg.auditLog, cfg.hostRoot, cfg.auditHost, cfg.dataDir, cfg.auditFill, sink)
|
||||
go api.audit.Run(ctx)
|
||||
api.selfLog = selfLog
|
||||
api.Routes(mux)
|
||||
mux.Handle("GET /", http.FileServer(http.FS(static)))
|
||||
|
||||
@@ -229,6 +255,10 @@ func main() {
|
||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatalf("http: %v", err)
|
||||
}
|
||||
logEvent("main", 5, "LogStream stopping")
|
||||
stopSelf()
|
||||
<-selfDone
|
||||
stopStore()
|
||||
<-storeDone
|
||||
log.Println("shutdown complete")
|
||||
}
|
||||
Reference in new issue
Block a user