CreaRack-SL

POST /api/monitoring/targets/{target_id}/tcp/check — Sonda TCP Bajo Demanda

Propósito

Endpoint de sondeo TCP manual para destinos públicos. Permite a los operadores verificar si un puerto específico (SSH:22, HTTPS:443) acepta conexiones en un target de monitoreo, útil para equipos que bloquean ICMP.

Operación: tcp_check(request, target_id: int) en monitoring/api/operations.py.

Entrada

Path

POST /api/monitoring/targets/{target_id}/tcp/check

Parámetros

  • target_id (path): ID del MonitoringTarget.
  • Headers: Bearer token con permiso observatory:edit.

Nota: la configuración del puerto (tcp_port) y habilitación (tcp_enabled) ya están en el target; aquí solo se gatilla el chequeo.

Lógica

1. Validación de permiso y existencia

require_perm(request, "observatory", "edit")
org, target = get_target_or_404(request, target_id)

2. Validación de configuración

if not target.tcp_port:
    return 400, {"error": "Target has no tcp_port configured"}

3. Manejo de IP privada

if is_private_ip(target.ip_address):
    return {
        "status": "up" if target.last_tcp_up else ("down" if target.last_tcp_up is not None else "unknown"),
        "port": target.tcp_port,
        "skipped": True,
        "reason": "private_ip",
    }
  • Razón: la LAN la sondea el Local Agent, no el SaaS. Retornamos el último resultado conocido.
  • Estados: “up” (last_tcp_up=True), “down” (last_tcp_up=False), “unknown” (aún sin dato).

4. Ejecución de sondeo (IP pública)

result = check_tcp(target.ip_address, target.tcp_port)
now = timezone.now()

target.last_tcp_up = result.status == "up"
target.last_tcp_check = now
target.last_status = target.resolve_reachability(target.last_packet_loss)
target.last_check = now
target.save()
  • Invoca servicio tcp_service.check_tcp().
  • Persiste resultado en last_tcp_up y last_tcp_check.
  • Recalcula estado global: resolve_reachability() combina ICMP (ping_loss) con TCP.
  • Refresca last_check (para sincronía con checks SNMP/HTTP).

5. Métricas y notificación

if result.latency_ms is not None:
    MetricSample.objects.create(
        target=target, timestamp=now, metric_type="tcp_latency", value=result.latency_ms
    )

broadcast_metrics_sync(
    target.id,
    {"status": target.last_status, "tcp_up": target.last_tcp_up, "tcp_latency_ms": result.latency_ms},
    now.isoformat(),
)
  • Guarda métrica tcp_latency si resultado es UP.
  • Notifica via WebSocket (broadcast_metrics_sync) a Observatory en tiempo real.

Salida

Éxito (200)

{
  "status": "up",
  "port": 22,
  "latency_ms": 12.45,
  "error": null,
  "timestamp": "2026-07-17T10:51:06Z"
}
  • status: “up” (puerto acepta) o “down” (rechaza/timeout/error).
  • latency_ms: millisegundos del handshake (solo si up).
  • error: mensaje de error si aplica (timeout, invalid port, SSRF blocked, etc.).
  • timestamp: ISO format del momento de la prueba.

IP privada (200)

{
  "status": "up",
  "port": 22,
  "skipped": true,
  "reason": "private_ip"
}

Errores

  • 400: No hay tcp_port configurado en el target.
  • 404: Target no encontrado o no autorizado (RLS).
  • 500: Excepción al probar (ej. DNS failure, excepciones de socket).

Comportamiento desde la UI

Trigger manual

  1. Usuario hace click en botón “TCP Probe” en sección Heartbeat.
  2. Se abre modal con campo “Port” (por defecto el configurado).
  3. Usuario toca “Save”.
  4. Front llama toggleMonitorType(..., 'tcp', true) que:
    • PUT al target con tcp_enabled=true + tcp_port=....
    • POST a este endpoint para trigger inmediato.
  5. La respuesta se muestra en un toast.

Flujo de ingest (Agente, dormido)

Cuando el Local Agent despierte (próximo release del .exe):

  1. Recibe monitor_types con “tcp” para un target.
  2. Lee tcp_port de la store SQLite.
  3. Loop sentinel/tcp_check.py prueba el puerto periódicamente.
  4. Escribe métricas tcp_up y tcp_latency en store.
  5. REST API del Agente envía al SaaS → ingest en sentinel_ingest.py → persiste last_tcp_up/last_tcp_check.

Guardias y seguridad

  • SSRF: check_tcp() rechaza direcciones privadas.
  • Rate limiting: heredado del router Ninja (puede añadirse de forma granular si se abusa).
  • Permisos: requiere observatory:edit (multi-tenancy RLS via get_target_or_404).
  • Timeout: 3.0s, evita DOS.

Impacto en estado

  • Semántica de up/down: La llamada a resolve_reachability() cambia el estado global del target si TCP está fresco y ICMP está caído 100%.
  • Dashboard: changes en last_status y tcp_up se sincronizan via WS → Observable recalcula color, alertas, etc.

Testeo

  • tests/api/test_tcp_probe.py: cobertura de campos, validación, SSRF guard, socket real, estado combinado.

Véase también

  • [[entity—monitoring—service—tcp-service]]
  • [[entity—monitoring—model—monitoring-target]]
  • [[entity—terminal—service—sentinel-tcp-check]]
  • [[feature—monitoring—sonda-tcp]]
  • [[concept—monitoring—cns]]