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_upylast_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_latencysi 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_portconfigurado 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
- Usuario hace click en botón “TCP Probe” en sección Heartbeat.
- Se abre modal con campo “Port” (por defecto el configurado).
- Usuario toca “Save”.
- Front llama
toggleMonitorType(..., 'tcp', true)que:- PUT al target con
tcp_enabled=true+tcp_port=.... - POST a este endpoint para trigger inmediato.
- PUT al target con
- La respuesta se muestra en un toast.
Flujo de ingest (Agente, dormido)
Cuando el Local Agent despierte (próximo release del .exe):
- Recibe
monitor_typescon “tcp” para un target. - Lee
tcp_portde la store SQLite. - Loop
sentinel/tcp_check.pyprueba el puerto periódicamente. - Escribe métricas
tcp_upytcp_latencyen store. - REST API del Agente envía al SaaS → ingest en
sentinel_ingest.py→ persistelast_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 viaget_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_statusytcp_upse 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]]
Referenciado desde
- ¿Cómo funciona la sonda TCP de monitoring introducida en v1.58.0? ¿Dónde está el modal de TCP Probe en Observatory, qué campos tiene, y cómo viaja la orden hasta el Local Agent?
- sentinel/tcp_check.py — Loop Asyncio de Sondeo TCP del Agente
- Sonda TCP — Equipos que bloquean ICMP dejan de salir "down" falsos
- tcp_service: Sonda TCP con Guardia SSRF