Skip to content

docs(security): re-verify the nine-hour window, and decide Zeek's half elsewhere (#441) - #489

Merged
Gerrrt merged 1 commit into
mainfrom
gerrrt/suricata-zeek-logging-alerts-06753e
Sep 17, 2026
Merged

Gerrrt merged 1 commit into
mainfrom
gerrrt/suricata-zeek-logging-alerts-06753e

Conversation

@Gerrrt

@Gerrrt Gerrrt commented Sep 17, 2026

Copy link
Copy Markdown
Owner

What changed

Records the outcome of #441 rather than implementing it: its Suricata half
landed five days ago in #459 and is live, and its Zeek half turns out to belong
to #437 as a metric, not here as a Loki rule. Comment and prose only — no alert
rule is added or altered, and the counts stay at 82 Prometheus + 18 Loki.

Three files:

Why

The window still holds, re-read rather than assumed. Measured again over the
full current retention — 27.7 days to 2026-09-17 22:25 UTC, five-minute buckets:

stream span gaps >1h gaps >9h longest
both interfaces (what the rule sees) 27.7 d 6 0 1.33 h
igc0.20 (Skids) 15.4 d 3 0 1.25 h
igc0.10 (Degens) 13.7 d 30 7 47.5 h

The worst aggregate silence grew from 75 to 80 minutes in five weeks, so nine
hours keeps nearly seven times the headroom DhcpLeaseLogsStopped settled on.
The aggregate design earned its keep: igc0.10 alone crossed nine hours seven
times
, so the per-interface rule #441 opened with would have paged seven times
in a fortnight.

One number is deliberately not compared, and the comment says so: 135 gaps over
thirty minutes here against 54 on 2026-09-12. That is bucket alignment and which
streams were summed, not a change in Suricata; the long tail, which is the half
that sets the window, agrees. interface is also only stamped from 2026-09-02,
so the per-interface rows cover 13.7 days against the aggregate's 27.7.

Zeek's half is not a Loki rule. #441 proposed ZeekLogsStopped against the
lab's Loki once #437 existed. #437 existing is not sufficient:

  • Run Zeek on a mirror port, and see the VLAN 30 traffic the firewall never will #437 puts Zeek's logs on alexander and never on 10.0.99.20 — ADR-0007
    requires it, ADR-0039 reaffirms it by keeping the mirror on Saruman's bridge.
  • ADR-0020 gives that stack no Alertmanager on purpose. stacks/lab/loki/ ships
    no ruler and no rules/, and its config says why: a ruler with nowhere to
    deliver evaluates rules and discards the result, which reads as coverage and
    is not.
  • ADR-0028 revisited that boundary, let guest liveness cross, kept guest
    telemetry in the lab, and named what stays uncovered.

#437 already carries the right instrument — a homelab_zeek_mirror_active
textfile gauge read on the hypervisor, crossing as guest state under ADR-0028.
So Zeek's liveness is answered where Zeek is built, and #441 is being closed
separately rather than held behind a Wave 2 dependency that itself waits on #414.

Blast radius

None. Documentation and one YAML comment; no rule, no query, no port, no
credential. The rules file is a directory bind mount, so the comment reaches the
container on the next converge — and converge is report-only, so merging deploys
nothing.

  • No change to network segmentation or firewall rules
  • No new port published to a VLAN that could not already reach the service
  • No credential added outside secrets/*.sops.yaml

Verification

  • make validate passes
./scripts/validate.sh            all checks passed (3 skipped — not the deployment checkout)
make check-loki-rules            PASS 18 Loki rule(s) and 30 dashboard expression(s) parsed, 48 evaluated
python3 scripts/check_docs.py    docs OK — 82 Prometheus + 18 Loki rules, 7 dashboards, 141 panels, 10 assertions
./scripts/lint.sh --require-all  lint passed

The live ruler, before and after — the rule is untouched and in its good state:

ids   SuricataLogsStopped   state=inactive   health=ok
  • Docs updated (docs/security.md, docs/roadmap.md)

What this does not prove. Loki rules still have no unit-test harness, so
check_loki_rules.sh shows the rule parses and the ruler evaluates it — not
that it fires.

🤖 Generated with Claude Code

…f elsewhere (#441)

#441 asked for two absence rules. SuricataLogsStopped landed on 2026-09-12 in
PR #459 and is live — the ruler reports it health=ok, state=inactive. This
records what the second half turned out to be, and re-reads the stream the
first half was sized against.

RE-VERIFIED, AND IT HOLDS. Measured again on 2026-09-17 over the full current
retention (27.7 days to 22:25 UTC, five-minute buckets, same method as the
original): the worst aggregate silence is 80 minutes against 75 five weeks ago,
six gaps exceeded an hour, none exceeded two. Nine hours keeps nearly seven
times the headroom DhcpLeaseLogsStopped settled on.

The aggregate design earned its keep in the interval. igc0.10 alone crossed
nine hours SEVEN times, worst 47.5 hours, so the per-interface rule the issue
started from would have paged seven times in a fortnight. The rule's comment
now carries both readings.

One number is not comparable and the comment says so rather than presenting a
single series: 135 gaps over thirty minutes here against 54 on 2026-09-12. That
is bucket alignment and which streams were summed, not a change in Suricata —
the long tail, which is the half that sets the window, agrees. Noted too that
`interface` is only stamped from 2026-09-02, so the per-interface figures cover
13.7 days against the aggregate's 27.7.

ZEEK'S HALF IS NOT A LOKI RULE, AND BELONGS TO #437. The issue proposed
ZeekLogsStopped as the same shape against the lab's Loki once #437 existed.
#437 existing is not sufficient, and would not make it right:

  - #437 puts Zeek's logs on alexander and never on 10.0.99.20, which ADR-0007
    requires and ADR-0039 reaffirms by keeping the mirror on Saruman's own
    bridge.
  - ADR-0020 gives that stack no Alertmanager on purpose. stacks/lab/loki/
    therefore ships no ruler and no rules/, and its config says why: a ruler
    with nowhere to deliver evaluates rules and discards the result, which
    reads as coverage and is not.
  - ADR-0028 revisited that exact boundary, let guest liveness cross and kept
    guest telemetry in the lab, and named what stays uncovered.

#437 already carries the right instrument — a homelab_zeek_mirror_active
textfile gauge read on the hypervisor, crossing as guest state under ADR-0028.
Zeek's liveness is a metric question answered where Zeek is built, so #441 ends
here rather than holding a slot behind a Wave 2 dependency that itself waits on
#414. docs/security.md states that limit next to the three it already
states; the roadmap records the whole of #441 in one entry, since it had none.

What this proves and what it does not, unchanged: Loki rules still have no
unit-test harness, so check_loki_rules.sh shows the rule parses and the ruler
evaluates it, not that it fires. No rule is added or altered here — the counts
stay at 82 + 18 and this is comment and prose only.

Verified: ./scripts/validate.sh (3 skipped, not the deployment checkout),
make check-loki-rules (18 rules + 30 dashboard expressions, 48 evaluated),
python3 scripts/check_docs.py (82 + 18, 10 assertions),
./scripts/lint.sh --require-all.

Refs #441
Refs #437

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Gerrrt
Gerrrt merged commit 417c4ae into main Sep 17, 2026
3 checks passed
@Gerrrt
Gerrrt deleted the gerrrt/suricata-zeek-logging-alerts-06753e branch September 17, 2026 23:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant