feat: proactive host_silence scan for stale agent.heartbeat (0.8.3)
Background scan every 5 min opens rule:host_silence and notifies without waiting for the next ingest event. CLI job + optional systemd timer. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -122,18 +122,26 @@ def evaluate_privilege_spike(db: Session, event: Event) -> RuleMatch | None:
|
||||
)
|
||||
|
||||
|
||||
def evaluate_host_silence(db: Session, event: Event) -> RuleMatch | None:
|
||||
if event.type == HEARTBEAT_TYPE:
|
||||
return None
|
||||
def host_silence_match_for_host(
|
||||
db: Session,
|
||||
host_id: int,
|
||||
*,
|
||||
hostname: str | None = None,
|
||||
now: datetime | None = None,
|
||||
) -> RuleMatch | None:
|
||||
"""Сформировать RuleMatch host_silence, если последний heartbeat устарел."""
|
||||
settings = get_settings()
|
||||
hb = last_heartbeat_at(db, event.host_id)
|
||||
ref = now or _now()
|
||||
hb = last_heartbeat_at(db, host_id)
|
||||
if hb is None:
|
||||
return None
|
||||
if agent_status(hb, stale_minutes=settings.sac_heartbeat_stale_minutes, now=_now()) != "stale":
|
||||
if agent_status(hb, stale_minutes=settings.sac_heartbeat_stale_minutes, now=ref) != "stale":
|
||||
return None
|
||||
host = db.get(Host, event.host_id)
|
||||
hostname = host.hostname if host else f"host#{event.host_id}"
|
||||
age_min = int((_now() - hb.replace(tzinfo=timezone.utc if hb.tzinfo is None else hb.tzinfo)).total_seconds() // 60)
|
||||
if hostname is None:
|
||||
host = db.get(Host, host_id)
|
||||
hostname = host.hostname if host else f"host#{host_id}"
|
||||
hb_utc = hb.replace(tzinfo=timezone.utc) if hb.tzinfo is None else hb.astimezone(timezone.utc)
|
||||
age_min = int((ref - hb_utc).total_seconds() // 60)
|
||||
return RuleMatch(
|
||||
rule_id=RULE_HOST_SILENCE,
|
||||
correlation_type="host.silence",
|
||||
@@ -146,6 +154,14 @@ def evaluate_host_silence(db: Session, event: Event) -> RuleMatch | None:
|
||||
)
|
||||
|
||||
|
||||
def evaluate_host_silence(db: Session, event: Event) -> RuleMatch | None:
|
||||
if event.type == HEARTBEAT_TYPE:
|
||||
return None
|
||||
host = db.get(Host, event.host_id)
|
||||
hostname = host.hostname if host else None
|
||||
return host_silence_match_for_host(db, event.host_id, hostname=hostname)
|
||||
|
||||
|
||||
def evaluate_immediate_high(event: Event) -> RuleMatch | None:
|
||||
"""Одиночные high/critical и известные типы (ban, mass bruteforce)."""
|
||||
problem_types = frozenset({"ssh.ip.banned", "ssh.bruteforce.mass"})
|
||||
|
||||
Reference in New Issue
Block a user