Plataforma centralizada de alertas operacionales que monitoriza buzones M365, aplica reglas de enrutamiento y notifica a Teams y Telegram
Reemplazar Power Automate como gestor del buzon de alertas operacionales, aportando visibilidad centralizada, trazabilidad completa y un canal de ingest abierto para futuras fuentes.
Polling del buzon [email protected] via Microsoft Graph cada 60 s. Deduplicacion permanente mediante tabla processed_mails.
Multiples patrones de remitente y asunto por regla (fnmatch). Asignacion de canales, menciones de Teams y destinos Telegram por regla.
Teams via Bot Framework REST API con Adaptive Card (sin Power Automate). Telegram Bot con inline keyboard y deep link a /alert/ID. Multiples canales por tipo, enrutables por regla.
Dashboard en tiempo real, historial con filtros, detalle HTML del email, y acciones: reconocer, resolver, ignorar, añadir notas.
Endpoint POST /api/ingest autenticado con API key para recibir alertas de sistemas externos (Defender, SIEM, scripts propios).
Autenticacion via Azure AD App Proxy. Control de acceso por rol (admin). Sincronizacion automatica de display_name y avatar con Graph.
Cinco componentes desacoplados: origen del correo, polling Graph, procesamiento con reglas, notificacion multicanal y panel web.
processed_mails.Backend Python y frontend Jinja2 + JS bien separados. Un unico entry point que arranca Flask y el scheduler.
sisalerts/ config/ app-config.json — Configuracion local (gitignored) app-config.example.json — Plantilla con todos los campos documentados db/ database.py — Conexion SQLite WAL, helpers de query schema.sql — Schema completo con FKs y checks de integridad services/ alert_processor.py — Orquesta: match de reglas, INSERT, dispatch a canales graph_mail.py — Polling Microsoft Graph Mail API con cert auth scheduler.py — Loop de polling en background thread teams_graph_notifier.py — Envio a canales Teams via Bot Framework REST API (Adaptive Card) telegram_notifier.py — Envio a grupos/DMs Telegram via Bot API webapp/ app.py — App Flask, registro de blueprints, init DB auth.py — SSO via App Proxy, auto-sync display_name/avatar routes/ admin.py — Vistas admin: buzones, reglas, canales, usuarios, ajustes api.py — API REST completa + endpoint /api/ingest dashboard.py — Dashboard, historial de alertas, detalle individual static/ admin.js — CRUD para todas las entidades de administracion dashboard.js — Feed de alertas, modal de detalle, acciones de ciclo de vida global.css — Estilos globales, tema claro/oscuro, componentes templates/ base.html — Layout con navbar, dropdown admin, confirm dialog dashboard.html — Feed de alertas activas con filtros y acciones alerts.html — Historial con filtros avanzados y modal de detalle alert_detail.html — Pagina individual linkable (/alert/ID), responsive admin_rules.html — Gestion de reglas con multiples patrones de remitente admin_channels.html — Canales Teams y Telegram en secciones separadas admin_users.html — Usuarios SSO, roles, Telegram username, sync Azure admin_mailboxes.html — Buzones monitorizados admin_settings.html — Ajustes globales (key-value) run_webapp.py — Entry point: inicia Flask + scheduler en background sisalerts-service.xml — Descriptor WinSW para instalar como servicio Windows deploy.ps1 — Script de deploy a produccion DEPLOY.md — Guia de despliegue y configuracion
Los 5 estados posibles de una alerta a lo largo de su ciclo de vida:
| Campo | Descripcion |
|---|---|
status | new → notified → acknowledged → resolved / ignored. Transicion manual desde el panel. |
priority | critical, high, normal, low. Asignado por la regla o por el operador. |
source | email (desde buzon) o el identificador del sistema externo (defender, prometheus...). |
acknowledged_by / at | UPN del usuario que reconocio la alerta y timestamp. |
resolved_by / at | UPN del resolutor y timestamp de resolucion. |
notes | Campo libre para anotaciones del operador. |
| Funcionalidad | Detalle |
|---|---|
| Feed en tiempo real | Alertas activas (new + notified) ordenadas por recepcion. Auto-reload configurable. |
| Modal de detalle | Cuerpo HTML del email sanitizado, metadatos completos, historial de notificaciones, notas. |
| Acciones rapidas | Reconocer, resolver, ignorar directamente desde el feed sin navegar al detalle. |
| URL shareable | /alert/<id> — deep link individual, responsive en movil y escritorio. |
| Historial de alertas | Vista /alerts con filtros por estado, prioridad y rango de fechas. Modal de detalle integrado. |
| Tema claro/oscuro | Toggle con persistencia en localStorage. |
| Funcionalidad | Detalle |
|---|---|
| Multiples patrones por regla | Tabla rule_sender_patterns independiente. Una regla agrupa todos los remitentes de un mismo sistema (ej: VMware, todas sus IPs y aliases). |
| Patron de asunto | Opcional por patron de remitente. Matching con fnmatch (*warning*, CRITICAL:*). |
| Prioridad y canales | Prioridad (critical/high/normal/low) y seleccion de canales Teams/Telegram por regla. |
| Menciones Teams | Lista de usuarios a mencionar en la notificacion Teams. |
| Destinos Telegram | Grupos o DMs individuales asignados por regla. |
| Duplicar regla | Botton de clonar regla completa (patrones, menciones, canales) como punto de partida. |
| Orden de evaluacion | Campo sort_order. La regla con numero mas bajo que haga match se aplica primero. |
| Importacion CSV | Script import_rules.py para importacion masiva con separador de multivalores por nueva linea. |
Via Bot Framework REST API directamente al canal, sin Power Automate. Adaptive Card construida en Python con prioridad, remitente, asunto, body colapsable y boton “Ver en SIS Alerts”. Soporta @menciones nativas. La Teams App (teams-app/) se instala en el equipo con permiso RSC ChannelMessage.Send.Group.
Bot API con inline keyboard. Botones: “Ver en SIS Alerts” (deep link /alert/ID) y acciones directas. Soporte de grupos y DMs individuales. Modo HTML para formato.
| Capacidad | Detalle |
|---|---|
| Multiples canales del mismo tipo | Varios canales Teams (distintos equipos/canales, cada uno con su team_id + channel_id) o varios bots Telegram, enrutables por regla. |
| Test de conexion | Boton en el panel admin para verificar conectividad sin necesidad de esperar una alerta real. |
| Log de notificaciones | Tabla notification_log: canal, timestamp, status (sent/failed), respuesta del servidor. |
| Grupos Telegram | Tabla telegram_groups asociada al canal. Un canal puede tener multiples grupos/chats. |
| Canal por defecto | Si una regla no tiene canales asignados, la notificacion se envia a todos los canales habilitados. |
| Campo | Tipo | Descripcion |
|---|---|---|
subject | string * | Titulo de la alerta |
sender | string * | Identificador del remitente (email, hostname, sistema...) |
priority | string | critical / high / normal / low |
body_html | string | Cuerpo HTML enriquecido, renderizado en el modal de detalle |
received_at | ISO8601 | Timestamp del evento en el sistema origen |
source | string | Identificador del sistema origen (defender, prometheus...) |
external_id | string | ID unico en el sistema origen, para deduplicacion |
rule_id | integer | Regla a aplicar (canales y menciones). Opcional. |
# Autenticacion: header X-Api-Key POST /api/ingest X-Api-Key: tu-api-key-aqui { "subject": "[CRITICAL] Host down: web-prod-01", "sender": "[email protected]", "priority": "critical", "source": "prometheus", "external_id": "alert_12345", "rule_id": 3, "body_html": "<p>...</p>" }
El endpoint /api/ingest es agnostico al origen. Cualquier sistema autenticado puede crear alertas directamente en SIS Alerts sin pasar por el buzon de correo. El campo source identifica el sistema de origen en el panel.
| Sistema | source | Descripcion |
|---|---|---|
| security-monitor | security-monitor | Monitor de buzones de seguridad M365 (Defender, SIEM...). Aplica reglas glob propias antes de ingestar. |
| Scripts PowerShell | offboarding-review, ad-monitor... | Cualquier script puede usar el endpoint con la API key. No requiere buzon de correo. |
| Sistemas futuros | configurable | HyperV Monitor, Azure Monitor, Prometheus... El campo rule_id permite enrutar a canales especificos por sistema. |
| Seccion | Funcionalidades |
|---|---|
| Buzones | Alta, edicion y habilitacion/deshabilitacion de buzones monitorizados. |
| Reglas | CRUD completo con editor de multiples patrones, menciones y destinos Telegram. |
| Canales | Dos secciones independientes (Teams / Telegram) con logos y test de conexion. |
| Usuarios | Lista de usuarios SSO con roles. Boton de sync individual y bulk desde Azure AD. |
| Ajustes | Configuracion global key-value: mark_as_read, polling_interval, panel_url. |
| Dropdown de admin | Menu desplegable en el header para acceso rapido a cualquier seccion admin. |
Acceso externo via Azure AD App Proxy con SSO corporativo. Arranque manual en PRE, servicio WinSW preparado para PRO.
# app-config.example.json — parametros clave { "port": 8081, "sso": { "header_upn": "X-MS-CLIENT-PRINCIPAL-NAME", "trusted_proxy_ips": ["10.80.6.21", "10.80.6.22"] }, "graph": { "auth_mode": "certificate", "certificate_thumbprint": "FC40...", "mailbox": "[email protected]" }, "polling_interval_seconds": 60, "ingest_api_key": "generate-a-strong-random-secret", "panel_url": "https://sisalerts-idealistaa82505660.msappproxy.net" }
Cada obstaculo encontrado, diagnosticado y resuelto con una solucion definitiva.
El modelo inicial tenia un unico sender_pattern y subject_pattern por regla. Para agrupar todos los remitentes de un mismo sistema (VMware, Oracle, etc.) se necesitaba una relacion 1:N.
Solucion: nueva tabla rule_sender_patterns con CASCADE DELETE, migracion de datos con script migrate_sender_patterns.py, y reescritura de _match_rule() para iterar patrones via fnmatch.
La cabecera X-MS-CLIENT-PRINCIPAL-NAME se puede falsificar si se accede al servidor directamente sin pasar por el App Proxy.
Solucion: lista trusted_proxy_ips en config. Solo se acepta la cabecera SSO si la request viene de una IP de la lista. Peticiones directas reciben 403.
El display_name se guardaba manualmente al crear el usuario, quedando obsoleto rapidamente.
Solucion: eliminacion del campo editable. Sync automatico lazy en cada login via GET /users/{upn}?$select=displayName y boton de sync masivo POST /api/app-users/sync-azure.
git mv sismonitor sisalerts fallaba con “Permission denied” porque VS Code tenia un watcher activo sobre el directorio.
Solucion: el rename lo hizo el usuario manualmente desde el Explorador de Windows (que si puede), luego se reregistro en git con git add sisalerts/ && git add -u sismonitor/. Git detecto el rename por similitud de contenido y lo registro como R (rename).
Telegram Android abre por defecto los enlaces en su navegador interno, no en el del sistema. No hay forma de forzar el navegador externo desde un boton de mensaje de bot. Solucion: asumida como limitacion del cliente Telegram. El boton funciona correctamente; el usuario puede optar por abrir en el navegador del sistema desde el propio navegador integrado de Telegram.
El endpoint de test de canal llamaba a send_alert_card() sin pasar panel_url, resultando en un alert_url vacio en la Adaptive Card.
Solucion: el test de canal ahora carga panel_url desde la funcion _get_panel_url(), igual que el procesador de alertas.
El campo mail_id de alerts es UNIQUE y se usa para dedup de emails. El ingest externo puede no proporcionar un ID unico.
Solucion: si se envia external_id, se usa directamente como mail_id. Si no, se genera un hash SHA-256 de sender+subject+received_at. Ambos se registran en processed_mails para dedup permanente.
| Capa | Tecnologia | Uso |
|---|---|---|
| Backend runtime | Python 3.12 |
Webapp Flask + scheduler en background thread. Un unico proceso. |
| Web framework | Flask + Blueprints |
HTTP server, rutas organizadas en blueprints (admin, api, dashboard). |
| Base de datos | SQLite (WAL mode) |
13 tablas. WAL permite lecturas concurrentes sin bloqueo durante escrituras. |
| Microsoft Graph | requests + cert auth |
Mail.Read para polling de buzon. User.Read para display_name y avatares. |
| Teams | Bot Framework REST API | Adaptive Card construida en Python via teams_graph_notifier.py. Token con scope api.botframework.com. Teams App con RSC ChannelMessage.Send.Group. |
| Telegram | Bot API (requests) |
sendMessage con parse_mode HTML e inline_keyboard. Grupos y DMs. |
| Frontend | Jinja2 + Vanilla JS | Templates server-side. JS sin frameworks ni build step. |
| Auth | Azure AD App Proxy | Cabecera SSO inyectada por el proxy. Validacion de IP de confianza en backend. |
| Servicio | WinSW | Descriptor XML para instalar como servicio Windows en produccion. |
| Patron matching | fnmatch |
Matching de remitente y asunto con wildcards Unix-style (*warning*). |