feat: always send RDP/SSH login success to Telegram via SAC
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -18,15 +18,16 @@ from app.services.ingest import ingest_event
|
|||||||
from app.services.problems import maybe_create_problem
|
from app.services.problems import maybe_create_problem
|
||||||
from app.services.schema_validate import validate_event_payload
|
from app.services.schema_validate import validate_event_payload
|
||||||
from app.services.notify_dispatch import (
|
from app.services.notify_dispatch import (
|
||||||
|
AUTH_LOGIN_SUCCESS_TYPES,
|
||||||
|
DAILY_REPORT_EVENT_TYPES,
|
||||||
LIFECYCLE_EVENT_TYPE,
|
LIFECYCLE_EVENT_TYPE,
|
||||||
|
notify_auth_login,
|
||||||
notify_daily_report,
|
notify_daily_report,
|
||||||
notify_event,
|
notify_event,
|
||||||
notify_lifecycle,
|
notify_lifecycle,
|
||||||
notify_problem,
|
notify_problem,
|
||||||
)
|
)
|
||||||
|
|
||||||
DAILY_REPORT_EVENT_TYPES = frozenset({"report.daily.ssh", "report.daily.rdp"})
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/events", tags=["events"])
|
router = APIRouter(prefix="/events", tags=["events"])
|
||||||
logger = logging.getLogger("sac.ingest")
|
logger = logging.getLogger("sac.ingest")
|
||||||
|
|
||||||
@@ -76,6 +77,8 @@ def post_event(
|
|||||||
notify_daily_report(event, db=db)
|
notify_daily_report(event, db=db)
|
||||||
elif event.type == LIFECYCLE_EVENT_TYPE:
|
elif event.type == LIFECYCLE_EVENT_TYPE:
|
||||||
notify_lifecycle(event, db=db)
|
notify_lifecycle(event, db=db)
|
||||||
|
elif event.type in AUTH_LOGIN_SUCCESS_TYPES:
|
||||||
|
notify_auth_login(event, db=db)
|
||||||
else:
|
else:
|
||||||
notify_event(event, db=db)
|
notify_event(event, db=db)
|
||||||
if problem is not None and problem_created:
|
if problem is not None and problem_created:
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ from app.services.notification_severity import severity_meets_minimum
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
LIFECYCLE_EVENT_TYPE = "agent.lifecycle"
|
LIFECYCLE_EVENT_TYPE = "agent.lifecycle"
|
||||||
|
DAILY_REPORT_EVENT_TYPES = frozenset({"report.daily.ssh", "report.daily.rdp"})
|
||||||
|
AUTH_LOGIN_SUCCESS_TYPES = frozenset({"rdp.login.success", "ssh.login.success"})
|
||||||
|
|
||||||
|
|
||||||
def _event_telegram_via_agent(event: Event) -> bool:
|
def _event_telegram_via_agent(event: Event) -> bool:
|
||||||
@@ -81,3 +83,14 @@ def notify_lifecycle(event: Event, *, db: Session | None = None) -> None:
|
|||||||
if not should_notify_event(event, db):
|
if not should_notify_event(event, db):
|
||||||
return
|
return
|
||||||
_dispatch_lifecycle_channels(event, db=db, policy=policy)
|
_dispatch_lifecycle_channels(event, db=db, policy=policy)
|
||||||
|
|
||||||
|
|
||||||
|
def notify_auth_login(event: Event, *, db: Session | None = None) -> None:
|
||||||
|
"""Успешный удалённый вход RDP/SSH — всегда в Telegram SAC (info вне min_severity).
|
||||||
|
|
||||||
|
При UseSAC=dual агент шлёт TG сам (telegram_via=agent) — SAC не дублирует.
|
||||||
|
"""
|
||||||
|
policy = get_effective_notification_policy(db)
|
||||||
|
if not should_notify_event(event, db):
|
||||||
|
return
|
||||||
|
_dispatch_lifecycle_channels(event, db=db, policy=policy)
|
||||||
|
|||||||
@@ -89,6 +89,62 @@ def test_notify_event_skipped_by_cooldown():
|
|||||||
mock_tg.notify_event.assert_not_called()
|
mock_tg.notify_event.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_notify_auth_login_bypasses_min_severity():
|
||||||
|
event = Event(
|
||||||
|
event_id="00000000-0000-4000-8000-000000000601",
|
||||||
|
host_id=1,
|
||||||
|
occurred_at=datetime(2026, 5, 31, 18, 36, tzinfo=timezone.utc),
|
||||||
|
category="auth",
|
||||||
|
type="rdp.login.success",
|
||||||
|
severity="info",
|
||||||
|
title="RDP login event 4624",
|
||||||
|
summary="papatramp from 192.168.160.3",
|
||||||
|
details={"telegram_via": "sac"},
|
||||||
|
payload={},
|
||||||
|
)
|
||||||
|
policy = NotificationPolicyConfig(
|
||||||
|
min_severity="warning",
|
||||||
|
use_telegram=True,
|
||||||
|
use_webhook=False,
|
||||||
|
use_email=False,
|
||||||
|
source="db",
|
||||||
|
)
|
||||||
|
with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy):
|
||||||
|
with patch.object(notify_dispatch, "should_notify_event", return_value=True):
|
||||||
|
with patch.object(notify_dispatch, "telegram_notify") as mock_tg:
|
||||||
|
notify_dispatch.notify_auth_login(event)
|
||||||
|
mock_tg.notify_event.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_notify_auth_login_skips_telegram_when_via_agent():
|
||||||
|
event = Event(
|
||||||
|
event_id="00000000-0000-4000-8000-000000000602",
|
||||||
|
host_id=1,
|
||||||
|
occurred_at=datetime(2026, 5, 31, 18, 36, tzinfo=timezone.utc),
|
||||||
|
category="auth",
|
||||||
|
type="ssh.login.success",
|
||||||
|
severity="info",
|
||||||
|
title="SSH login",
|
||||||
|
summary="user from 10.0.0.1",
|
||||||
|
details={"telegram_via": "agent"},
|
||||||
|
payload={},
|
||||||
|
)
|
||||||
|
policy = NotificationPolicyConfig(
|
||||||
|
min_severity="warning",
|
||||||
|
use_telegram=True,
|
||||||
|
use_webhook=True,
|
||||||
|
use_email=False,
|
||||||
|
source="db",
|
||||||
|
)
|
||||||
|
with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy):
|
||||||
|
with patch.object(notify_dispatch, "should_notify_event", return_value=True):
|
||||||
|
with patch.object(notify_dispatch, "telegram_notify") as mock_tg:
|
||||||
|
with patch.object(notify_dispatch, "webhook_notify") as mock_wh:
|
||||||
|
notify_dispatch.notify_auth_login(event)
|
||||||
|
mock_tg.notify_event.assert_not_called()
|
||||||
|
mock_wh.notify_event.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
def test_notify_lifecycle_bypasses_min_severity():
|
def test_notify_lifecycle_bypasses_min_severity():
|
||||||
event = Event(
|
event = Event(
|
||||||
event_id="00000000-0000-4000-8000-000000000501",
|
event_id="00000000-0000-4000-8000-000000000501",
|
||||||
|
|||||||
Reference in New Issue
Block a user