Incidentedraftcreado Fri Sep 04#monitoring#network#security#ssrf#reliability#observability#django#audit-suprema
Cuándo
04-09-2026 · commit 98ddfd3a (PR #496, v1.104.0). Tercer dominio de la cola MEDIA/BAJA de la Auditoría Suprema 2 (task #286) — primer PR de tres para monitoring (bloque A: sondas y targets; quedan B —CNS e insights— y C —ITSM y notificaciones—). Implementación en Sonnet con las decisiones fijadas de antemano; revisión de Fable 5.1 con dos correcciones: SNMP v1 no existe ni en el SaaS ni en el Agente y no debía aceptarse al validar, y un presupuesto de sonda agotado antes del primer intento debía dar un error de presupuesto explícito, no el “no validated IPs” que sugiere otra causa.
Síntomas visibles
Doce hallazgos MEDIA en el mismo commit:
PUT /targets/{id}devolvía 500 genérico (no 400) al desmarcar las 4 sondas de un target — el código no estaba declarado en elresponsede ninja.- El ajuste “Smoothing” de Chart Defaults se guardaba pero no tenía efecto:
_get_smoothing_levelleía una clave de Valkey que nadie escribe desde la migración aui_prefs. hourssin acotar producía 500 (nan/inf) en/vm/pingy/vm/stats, y un valor como1e9llegaba tal cual a VictoriaMetrics.- Las claves de OID (
snmp_extras_*,snmp_fast_*,VendorProfile.monitoring_oids) no se validaban contra el patrón del ingest — una clave con mayúsculas o:producía una métrica que no nacía nunca, y el alta de perfil de fabricante no tenía ninguna validación. - El ping y el chequeo HTTP manuales pisaban la semántica de
last_status: un equipo con ICMP bloqueado pero sonda TCP viva se marcabadown, y un 500 de la web marcaba el equipo entero como caído. - El techo de 30s del SNMP síncrono era inerte en el camino
asyncio.run(el que usan las vistas síncronas bajo daphne) — solo aplicaba en la rama async. - El
timeout_msconfigurado en el target nunca llegaba al SNMP:from_configleía una clave de config que nadie escribe, y en la práctica siempre se usaban 5s fijos. - Observatory y Wireless compartían la misma clave de sweep en
deep_discover_job_key; lanzar un barrido mientras corría el otro devolvíaalready_runningcon el total ajeno. - El comando de servidor
ping_targetssondaba IPs privadas (terreno del Agente, no del servidor) y persistía resultadosBlocked destinationdel guard como si fuesen una medida real. - La sonda HTTP no tenía presupuesto agregado: un dominio con 8 registros A caídos podía consumir ~80s de un hilo síncrono, y
try_each_ipreintentaba ante CUALQUIER excepción, no solo fallos de conexión. - El config del target (
http_method,http_headers,http_expected_codes,snmp_port,snmp_interface,snmp_version,timeout_ms) no validaba sus claves conocidas — valores inválidos salían a la red sin comprobar. - Batería de evasión SSRF incompleta en
net_guard:198.18.0.0/15(benchmarking),224.0.0.0/4(multicast) ylocalhost.(forma FQDN con punto final) se colaban por no estar en las listas de rangos/hosts bloqueados.
Causa raíz
No hay un patrón único — es la cola de una auditoría grande, agrupable en:
- Validación ausente en el borde de la API: el config del target, las claves de OID y
hoursviajaban del front a la red/BD sin ninguna comprobación de forma o rango. - Config que no llega al servicio que lo necesita:
timeout_msy el ajuste de smoothing existían en el modelo/ui_prefspero el código de lectura seguía apuntando a una fuente vieja (clave hardcodeada o clave de Valkey obsoleta). - Estados distintos colapsados en uno solo: “no se puede sondear con este método” (ICMP bloqueado, presupuesto agotado) y “el equipo está caído” se trataban como el mismo hecho, generando falsos positivos de caída.
- Guard SSRF con enumeración incompleta y comparación por forma, no por destino: mismo patrón de fondo que la Tanda 9 (IPv6 mapeada) — un rango no listado o una forma alternativa del mismo hostname (
localhost.con punto final) se cuela hasta que se enumera explícitamente. - Retry sin límites: sin cota de tiempo ni de tipo de excepción, cualquier fallo — incluido uno de programación — se consideraba “prueba la siguiente IP”.
Fix aplicado
Commit 98ddfd3a (PR #496):
monitoring/api/targets.py:PUTdeclara el400enresponsecon el motivo del rechazo.metrics_reader_base.py:_get_smoothing_levelleeget_ui_pref(org, "chart_defaults");_clamp_hoursse aplica en_get_cache_key,_hours_to_promql_durationy_calculate_step.monitoring/api/schemas.py+network/api/common.py: las claves de OID (alta manual yVendorProfile.monitoring_oids) pasan porDYNAMIC_METRIC_RE; nuevo validador de config del target (método HTTP en{GET, HEAD}, cabeceras≤20sinHost/Authorization/Cookie/Content-Length/Transfer-Encoding,expected_codesno vacío en 100..599,snmp_port/snmp_interface,snmp_versionen{v2c, v3},timeout_ms), aplicado en POST/PUT/bulk/PATCH.monitoring/api/operations.py: ping usaresolve_reachability; el chequeo HTTP deja de tocarlast_statusdel host.monitoring/api/common.py:run_asyncenvuelve la corrutina enasyncio.wait_fortambién en el caminoasyncio.run.snmp_service.py+monitoring/api/snmp.py:from_configrecibetimeout_ms=target.timeout_ms, acotado 100..120000 ms.deep_discover_service.py+ 4 llamadores +terminal/api/sentinel_ingest.py:deep_discover_job_key(org_id, page)compone la clave en un único sitio; Wireless usa clave propia.monitoring/management/commands/ping_targets.py: salta targets de IP privada y no persiste resultadosBlocked destinationdel guard.http_service.py+net_guard.py: presupuesto compartidoPROBE_BUDGET_SECONDS=12.0yMAX_PROBE_IPS=3;try_each_ip/try_each_ip_asyncganan undeadliney solo reintentan anteOSError/ssl.SSLError/httpx.ConnectError/httpx.ConnectTimeout(antes: cualquierException); si el presupuesto se agota sin ningún intento,_raise_after_looplanzaTimeoutErrorexplícito en vez delRuntimeErrorgenérico de “no validated IPs”.net_guard.py:BLOCKED_IP_NETWORKSgana198.18.0.0/15y224.0.0.0/4; nuevo helper_strip_trailing_dotnormaliza hostnames tipolocalhost.antes de comparar contraBLOCKED_HOSTNAMES.- 66 tests nuevos en
tests/api/test_audit_monitoring_a.py. Medido en PROD antes de mergear: 0 datos guardados (240 targets, 79 perfiles de fabricante con 263 OIDs) chocan con las validaciones nuevas.
Lecciones
- Un guard de red que enumera rangos/hosts bloqueados necesita revisión periódica: cada forma alternativa de representar el mismo destino (un rango no listado, un hostname con punto final) es una vía de evasión hasta que se añade explícitamente — no es una lista que se “cierra” una vez.
- Un retry entre IPs solo debe disparar ante fallos de CONEXIÓN; tratar cualquier excepción como reintentable oculta errores de programación y deja que una sonda fallida consuma tiempo sin límite.
- Un campo de configuración que vive en el modelo pero cuyo lector sigue apuntando a otra fuente (clave hardcodeada, caché vieja) es indistinguible de no tener el campo — hay que trazar el dato hasta la llamada de red o lectura real.
- “No se puede sondear con este método” y “el equipo está caído” son estados distintos; colapsarlos genera alertas falsas y erosiona la confianza en el dashboard.
Preventivos futuros
- Quedan 20 hallazgos de
monitoringen la cola: bloque B (CNS e insights, 11) y bloque C (ITSM y notificaciones, 15, incluidos los del ingest del deep-discovery y de Wireless/UPS) — próximos PRs de esta misma serie. - Ayuda de usuario sin cambios:
crearack--monitoring--ping-httpy--dashboardsya describían el comportamiento correcto; el código se pone a la altura de la ayuda.
Véase también
- [[incident—20260901—auditoria-suprema-2-tanda9-ssrf-ipv6-xss-stencils]]
- [[incident—20260831—auditoria-suprema-2-tanda1-monitoring-racks]]
- [[entity—monitoring—service—net-guard]]
- [[entity—monitoring—service—http-service]]
- [[entity—monitoring—service—tcp-service]]
- [[entity—network—service—device-config]]
- [[concept—seguridad—ssrf-mitigacion]]
Referenciado desde
- Auditoría Suprema 2 · Cola config-ia: IA del workspace, drivers CNS y saneado de prompts
- Auditoría Suprema 2 · Cola frontend: CSP del portal de cliente, escapes y CSRF same-origin
- Auditoría Suprema 2 · Cola monitoring D: rendimiento de deep-discovery y de Wireless/UPS
- Auditoría Suprema 2 · Cola network: DeviceTask sin agente que no caduca y comandos de la whitelist por vendor inalcanzables
- Auditoría Suprema 2 · Cola racks: librería de stencils, backup/restore, plantillas y rastro de auditoría
- Auditoría Suprema 2 · Cola terminal: Agente 2.27.0 (guard de destino, CORS vivo, DPAPI fail-closed) e ingest honesto