Sistema activo en PRE · Expansion a PRO en curso

SIS Alerts

Plataforma centralizada de alertas operacionales que monitoriza buzones M365, aplica reglas de enrutamiento y notifica a Teams y Telegram

👤 Autor: JM Fernandez + Claude (Anthropic)
📅 Fecha: Abril 2026
🌐 Acceso: Azure AD App Proxy
Python 3.12 Flask SQLite WAL Microsoft Graph Teams Bot Framework Telegram Bot Azure AD App Proxy SSO REST API WinSW Windows Server

Que resuelve este proyecto

Reemplazar Power Automate como gestor del buzon de alertas operacionales, aportando visibilidad centralizada, trazabilidad completa y un canal de ingest abierto para futuras fuentes.

🔔

Recepcion y procesamiento de alertas

Polling del buzon [email protected] via Microsoft Graph cada 60 s. Deduplicacion permanente mediante tabla processed_mails.

⚙️

Reglas de enrutamiento flexibles

Multiples patrones de remitente y asunto por regla (fnmatch). Asignacion de canales, menciones de Teams y destinos Telegram por regla.

📱

Notificaciones en Teams y Telegram

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.

🔍

Panel web con ciclo de vida completo

Dashboard en tiempo real, historial con filtros, detalle HTML del email, y acciones: reconocer, resolver, ignorar, añadir notas.

🔗

API de ingest abierta

Endpoint POST /api/ingest autenticado con API key para recibir alertas de sistemas externos (Defender, SIEM, scripts propios).

🔐

Acceso SSO corporativo

Autenticacion via Azure AD App Proxy. Control de acceso por rol (admin). Sincronizacion automatica de display_name y avatar con Graph.

60
Segundos de polling interval
5
Estados de alerta
4
Niveles de prioridad
2
Tipos de canal (Teams / Telegram)
13
Tablas en la base de datos
8+
Reglas de alerta importadas

Pipeline de alerta de extremo a extremo

Cinco componentes desacoplados: origen del correo, polling Graph, procesamiento con reglas, notificacion multicanal y panel web.

📧
Buzon M365
Alertas de sistemas: VMware, AWS, Prometheus, Forti, Pingdom...
Graph API
🔄
Scheduler
Python / 60 s
Polling incremental. Dedup via processed_mails.
Reglas + DB
⚙️
Alert Processor
alert_processor.py
Match de reglas (fnmatch), INSERT en DB, dispatch a canales.
Bot Framework / Bot
📲
Notificadores
Teams + Telegram
Bot Framework REST API (Adaptive Card) y Telegram Bot API con inline keyboard.
Flujo email: Buzon M365 Graph Mail API Scheduler (60s) Alert Processor SQLite + Notificaciones
Flujo ingest: security-monitor / script / SIEM POST /api/ingest (API key) Dedup INSERT + Dispatch
Flujo web: Browser App Proxy (SSO) Flask webapp SQLite WAL
Flujo notificacion: Regla matched Teams Bot Framework Telegram Bot API Deep link /alert/ID

Organizacion del repositorio

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

Capacidades del sistema

4.1 Ciclo de vida de alertas

Los 5 estados posibles de una alerta a lo largo de su ciclo de vida:

Nueva
Notificada
Reconocida
Resuelta
Ignorada
CampoDescripcion
statusnew → notified → acknowledged → resolved / ignored. Transicion manual desde el panel.
prioritycritical, high, normal, low. Asignado por la regla o por el operador.
sourceemail (desde buzon) o el identificador del sistema externo (defender, prometheus...).
acknowledged_by / atUPN del usuario que reconocio la alerta y timestamp.
resolved_by / atUPN del resolutor y timestamp de resolucion.
notesCampo libre para anotaciones del operador.

4.2 Dashboard y panel de alertas

FuncionalidadDetalle
Feed en tiempo realAlertas activas (new + notified) ordenadas por recepcion. Auto-reload configurable.
Modal de detalleCuerpo HTML del email sanitizado, metadatos completos, historial de notificaciones, notas.
Acciones rapidasReconocer, resolver, ignorar directamente desde el feed sin navegar al detalle.
URL shareable/alert/<id> — deep link individual, responsive en movil y escritorio.
Historial de alertasVista /alerts con filtros por estado, prioridad y rango de fechas. Modal de detalle integrado.
Tema claro/oscuroToggle con persistencia en localStorage.

4.3 Reglas de alerta

FuncionalidadDetalle
Multiples patrones por reglaTabla rule_sender_patterns independiente. Una regla agrupa todos los remitentes de un mismo sistema (ej: VMware, todas sus IPs y aliases).
Patron de asuntoOpcional por patron de remitente. Matching con fnmatch (*warning*, CRITICAL:*).
Prioridad y canalesPrioridad (critical/high/normal/low) y seleccion de canales Teams/Telegram por regla.
Menciones TeamsLista de usuarios a mencionar en la notificacion Teams.
Destinos TelegramGrupos o DMs individuales asignados por regla.
Duplicar reglaBotton de clonar regla completa (patrones, menciones, canales) como punto de partida.
Orden de evaluacionCampo sort_order. La regla con numero mas bajo que haga match se aplica primero.
Importacion CSVScript import_rules.py para importacion masiva con separador de multivalores por nueva linea.

4.4 Canales de notificacion

👀

Microsoft Teams

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.

✈️

Telegram Bot

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.

CapacidadDetalle
Multiples canales del mismo tipoVarios canales Teams (distintos equipos/canales, cada uno con su team_id + channel_id) o varios bots Telegram, enrutables por regla.
Test de conexionBoton en el panel admin para verificar conectividad sin necesidad de esperar una alerta real.
Log de notificacionesTabla notification_log: canal, timestamp, status (sent/failed), respuesta del servidor.
Grupos TelegramTabla telegram_groups asociada al canal. Un canal puede tener multiples grupos/chats.
Canal por defectoSi una regla no tiene canales asignados, la notificacion se envia a todos los canales habilitados.

4.5 API de Ingest

CampoTipoDescripcion
subjectstring *Titulo de la alerta
senderstring *Identificador del remitente (email, hostname, sistema...)
prioritystringcritical / high / normal / low
body_htmlstringCuerpo HTML enriquecido, renderizado en el modal de detalle
received_atISO8601Timestamp del evento en el sistema origen
sourcestringIdentificador del sistema origen (defender, prometheus...)
external_idstringID unico en el sistema origen, para deduplicacion
rule_idintegerRegla 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>"
}

🔗 Consumidores del endpoint /api/ingest

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.

SistemasourceDescripcion
security-monitorsecurity-monitorMonitor de buzones de seguridad M365 (Defender, SIEM...). Aplica reglas glob propias antes de ingestar.
Scripts PowerShelloffboarding-review, ad-monitor...Cualquier script puede usar el endpoint con la API key. No requiere buzon de correo.
Sistemas futurosconfigurableHyperV Monitor, Azure Monitor, Prometheus... El campo rule_id permite enrutar a canales especificos por sistema.

4.6 Administracion

SeccionFuncionalidades
BuzonesAlta, edicion y habilitacion/deshabilitacion de buzones monitorizados.
ReglasCRUD completo con editor de multiples patrones, menciones y destinos Telegram.
CanalesDos secciones independientes (Teams / Telegram) con logos y test de conexion.
UsuariosLista de usuarios SSO con roles. Boton de sync individual y bulk desde Azure AD.
AjustesConfiguracion global key-value: mark_as_read, polling_interval, panel_url.
Dropdown de adminMenu desplegable en el header para acceso rapido a cualquier seccion admin.

Produccion en Windows Server

Acceso externo via Azure AD App Proxy con SSO corporativo. Arranque manual en PRE, servicio WinSW preparado para PRO.

💻 Sistema

  • Windows Server 2022
  • Python 3.12 con venv
  • SQLite WAL (sisalerts.db)
  • Certificado PFX para Graph auth

🌐 Acceso

  • Azure AD App Proxy (SSO)
  • URL: sisalerts-idealistaa82505660.msappproxy.net
  • Cabecera SSO: X-MS-CLIENT-PRINCIPAL-NAME
  • IPs proxy de confianza: lista configurable

⚙️ Servicio

  • sisalerts-service.xml (WinSW)
  • Arranque manual en PRE
  • Script deploy.ps1 para PRO
  • Log en logs/sisalerts.log

🔒 Seguridad

  • app-config.json en .gitignore
  • Validacion de IP de proxy de confianza
  • Bootstrap admin via UPN en config
  • API key para ingest externo
# 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"
}

Problemas resueltos durante el desarrollo

Cada obstaculo encontrado, diagnosticado y resuelto con una solucion definitiva.

📄

Multiples remitentes por regla — cambio de schema

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.

🔐

SSO header spoofing — validacion de proxy de confianza

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.

👤

Display name y avatar — sincronizacion con Azure AD

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.

📦

Rename de carpeta en Windows — VS Code file lock

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 deep link — navegador integrado vs sistema

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.

🔗

panel_url vacio en payload Teams

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.

🗃

Deduplicacion para ingest externo

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.

Dependencias y tecnologias

CapaTecnologiaUso
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*).