Volver a la wiki

Remate de la sonda TCP: supresión inteligente de falsos positivos (v1.63.4)

Resumen ejecutivo

Dos remates de la validación E2E de la sonda TCP del 22-07:

  1. Fleco 3 (principal): Equipos legítimamente solo-TCP (con ICMP bloqueado por firewall) ya no generan falsas alarmas de “pérdida total de conectividad”. Si el Agente reporta ping_loss=100% pero la sonda TCP fresca confirma el target vivo, el CNS suprime el insight (HTTP 429) sin gastar llamada de IA.

  2. Fleco 1 (menor): Campo puerto del modal TCP Probe ya no miente — cuando activas la sonda con el campo vacío, se guarda 22 (el valor sugerido en el placeholder) en lugar de rechazar silenciosamente o quedar indefinido.

Fleco 2 (contador tcp_cycles en /info) + fix de raíz en el propio Agente → próxima versión del .exe.

Problema que se resuelve

Contexto: Equipos solo-TCP en centros de datos modernos

Muchos firewalls corporativos y edge bloquean ICMP intencionalmente (ping). Un equipo puede estar perfectamente operativo vía SSH/RDP/HTTP (TCP) pero “invisible” a ICMP.

El falso positivo de v1.63.3

El Agente reportaba fielmente loss=100% en cada ciclo de ping del equipo, porque el ping estaba bloqueado. El CNS creaba un insight “device unreachable” → enviaba IA → llenaba Observatory de ruido rojo. Además, ese equipo consumía rápidamente el límite de 10 insights/hora (rate limit).

Confirmación: Leyendo monitoring/anomaly_detector.py, el ciclo era:

En el test E2E del 22-07 no se vio porque el rate limit ya estaba saturado por otro lado.

Solución

Lógica en receive_insight() (monitoring/api/insights.py, línea 92-103)

# Un equipo legítimamente solo-TCP (ICMP bloqueado por firewall) reporta
# loss=100% en cada ciclo de ping del Agente aunque esté vivo; si la sonda
# TCP fresca lo da up, el "total loss" es falso positivo — se suprime antes
# de gastar la llamada de IA y de llenar el CNS de ruido. Se responde 429
# (no un 4xx nuevo) porque la flota 2.16.x desplegada lo trata como
# rate-limit benigno; un status desconocido contaría como error del reporter.

try:
    ping_loss = float(data.snmp_data.get("ping_loss"))
except (TypeError, ValueError):
    ping_loss = None

if ping_loss is not None and ping_loss >= 100 and target.resolve_reachability(ping_loss) == "up":
    return 429, {"error": "suppressed: total ICMP loss but a fresh TCP probe reports the device up"}

Semántica:

Degradación parcial NO se suprime: loss < 100% es un síntoma real (paquetes se pierden pero llegan algunos) → sí crea insight aunque TCP esté vivo.

UX: Modal TCP Probe — campo Puerto (ObservatorySettings.js, línea 185-190)

Antes: placeholder "22" sugería un default inexistente. Si activabas la sonda con el campo vacío, el formulario pedía “Please enter a valid port” sin aclarar cuál era la expectativa.

Ahora:

// El placeholder del campo es "22": si activas la sonda con el campo vacío,
// 22 es el valor que se guarda de verdad (el placeholder no puede mentir).
const port = parseInt(document.getElementById('tcp-settings-port').value) || (enabled ? 22 : null);

Si enabled=true y el campo está vacío → port=22 se guarda. La validación backend (validate_tcp en schema) rechazará solo puertos inválidos (fuera de 1-65535).

Tests

Nuevos en tests/api/test_tcp_probe.py (clase TestInsightTcpSuppression):

  1. test_total_loss_with_fresh_tcp_suppressed: Loss=100%, TCP check <5s → HTTP 429, no se crea insight
  2. test_total_loss_with_stale_tcp_creates_insight: Loss=100%, TCP check >1h old → HTTP 200, sí se crea insight
  3. test_partial_loss_with_fresh_tcp_not_suppressed: Loss=30%, TCP up → HTTP 200, sí se crea insight (es real)

Todos verdes en local con settings idénticos al CI.

Integración con MonitoringTarget

El modelo MonitoringTarget proporciona:

Línea de tiempo

Impacto de usuario

Notas técnicas

Véase también

Subir