Documentation
¶
Overview ¶
Package replay reads a log's past.
A live agent joins the present and stamps each line with the moment it read it, which is the same instant and costs nothing. Reading what already happened is a different act, and a deliberate one: an operator turning up after an attack began, or asking what a rule would have done before trusting it.
Two things make it different from a live run, and both matter:
- events are dated by the line, not by the reading. journald records its own time and needs no parsing; a file needs its parser to say where its timestamp is (time_field). A file's worth of traffic replayed as if it were simultaneous would have any windowed rule ban everyone in it.
- a crossing only becomes a sanction if that sanction **would still be in force now**. Window and ban duration, not window alone: an attack five hours old that earns six hours still earns them; the same attack earning five minutes earns nothing. This is what lets someone arrive late and still stop who is behind it, and it is the part fail2ban's scan-on-start does not do.
The evaluator is the same code as the live one — that is the point of it being a pure function of an ordered event stream (SPEC §5.3).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Options ¶
type Options struct {
// Since limits the replay to lines no older than this. Zero reads
// everything the files hold.
Since time.Duration
// Now is what "still in force" is measured against. Zero means the
// clock.
Now time.Time
// Rule, when set, replays only that rule — the shape of "what would
// this one do", without the noise of every other.
Rule string
// MaxEvents caps how many hits the replay holds at once. Zero takes
// the default. Past the cap the oldest go first — a verdict that can
// still be in force is made of recent evidence — and every drop is
// counted and reported (Capped), never silent.
MaxEvents int
}
Options bound what is read and how it is judged.
type Report ¶
type Report struct {
Files []string
Lines int
Hits int
Undated int // lines whose own time could not be read
Oldest time.Time
Newest time.Time
Verdicts []Verdict
InForce int // sanctions that would still be enforced now
Lapsed int // crossings whose sanction has already run out
// NoSanction counts crossings of rules with no ban outcome — alert
// and record-only rules. They are neither in force nor lapsed:
// counting them "too old to matter" would be a lie about rules that
// never ban anything.
NoSanction int
ByRule map[string]int
// Capped counts the hits dropped to stay inside MaxEvents — the
// oldest first. When it is not zero the judgment covers the newest
// evidence only, from CappedSince onwards, and the caller says so.
Capped int
CappedSince time.Time
// Inactive lists the rules this agent cannot evaluate yet, with the
// reason — they took no part in the judgment.
Inactive []string
// Where the reading ended, for the caller that will tail the same
// sources next: the position after each file's last line, and each
// journald unit's last cursor. A live tail seeded with these joins
// the present with no gap — within one process's life, never across
// a restart.
FilePos map[string]tail.Position
Cursors map[string]string
}
Report is what a replay found.
type Verdict ¶
type Verdict struct {
Trigger eval.Trigger // the crossing itself, for a caller that will act on it
Rule string
Offender string
At time.Time // when the evidence completed, by the lines' own clock
Evidence []string // the lines that convicted, as they were written
Ends time.Time // when the sanction it earns would run out
InForce bool // whether that is still in the future
}
Verdict is one crossing and what became of it.