76 lines
1.9 KiB
Go
76 lines
1.9 KiB
Go
// Package audit maintains an append-only record of connect/disconnect
|
|
// events (independent of journald, which may rotate) and answers "who is
|
|
// online right now" — which for jump/forward-only sessions is not visible
|
|
// via `who`/`loginctl`, since those rely on pty allocation that this tool
|
|
// deliberately disables.
|
|
package audit
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/gofrs/flock"
|
|
)
|
|
|
|
// Event is one line of the audit trail.
|
|
type Event struct {
|
|
Time time.Time `json:"time"`
|
|
User string `json:"user"`
|
|
Action string `json:"action"` // "connect", "disconnect", "admin"
|
|
Detail string `json:"detail,omitempty"`
|
|
SrcIP string `json:"src_ip,omitempty"`
|
|
}
|
|
|
|
// Append adds one event to the JSONL audit log at path, under a file lock
|
|
// so the interactive TUI and the background journal-tailer (see tail.go)
|
|
// can write concurrently without interleaving corrupt lines.
|
|
func Append(path string, ev Event) error {
|
|
fl := flock.New(path + ".lock")
|
|
if err := fl.Lock(); err != nil {
|
|
return fmt.Errorf("lock audit log: %w", err)
|
|
}
|
|
defer fl.Unlock()
|
|
|
|
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o640)
|
|
if err != nil {
|
|
return fmt.Errorf("open audit log: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
data, err := json.Marshal(ev)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = f.Write(append(data, '\n'))
|
|
return err
|
|
}
|
|
|
|
// ReadRecent returns the last n events from the audit log, oldest first.
|
|
func ReadRecent(path string, n int) ([]Event, error) {
|
|
f, err := os.Open(path)
|
|
if os.IsNotExist(err) {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
var all []Event
|
|
sc := bufio.NewScanner(f)
|
|
sc.Buffer(make([]byte, 64*1024), 1024*1024)
|
|
for sc.Scan() {
|
|
var ev Event
|
|
if err := json.Unmarshal(sc.Bytes(), &ev); err == nil {
|
|
all = append(all, ev)
|
|
}
|
|
}
|
|
if len(all) > n {
|
|
all = all[len(all)-n:]
|
|
}
|
|
return all, sc.Err()
|
|
}
|