Descripción general
Sesión 107 de la Auditoría Suprema del dominio monitoring: auditoría y remediación de la sub-área 2 (protocolos de sondeo — SNMP, ping, HTTP). Hallazgos: 36 confirmados (4 ALTA, 17 MEDIA, 15 BAJA). Se corrigen todos los hallazgos de seguridad (ALTA) y las correcciones de bajo riesgo / correctness en una tanda única (Regla 22).
Contexto
CreaRack monitoriza dispositivos de cliente a través del Local Agent (para objetivos privados) y mediante sondeos del servidor (para objetivos públicos). Estos sondeos ejecutan HTTP (descarga de página), ping (latencia ICMP) y SNMP (métricas de interfaz) desde el propio servidor CreaRack. La auditoría halló que los sondeos no validaban correctamente sus destinos.
Hallazgos corregidos (ALTA)
1. SSRF en probes HTTP (monitoring/services/http_service.py)
Vulnerabilidad: El endpoint /targets/{id}/http/check validaba target.ip_address (público) pero sondeaba config["http_url"] (controlado por usuario). Un atacante podría:
- Apuntar a
http://169.254.169.254/...(metadata cloud) http://127.0.0.1:8428/(VictoriaMetrics interno)http://valkey:6379/(caché del servidor)- Postgres, bases de datos locales
Corrección: Nuevo guard compartido net_guard.py que:
- Resuelve el hostname a IP
- Bloquea loopback, link-local, RFC1918, CGNAT, metadata, esquemas no-http
- Aplica en
http_service.check/check_async/_check_ssl_cert(protege todos los callers, incl. batch)
2. Endpoints SNMP sin gate de permisos (monitoring/api/snmp.py)
Vulnerabilidad: Los 3 endpoints SNMP (snmp/test, snmp/interfaces, snmp/poll) no tenían require_perm(request, "observatory", "edit"). Cualquier usuario autenticado (incluido readonly) podía dispararlos.
Corrección: Añadido require_perm a los 3 endpoints. Usuario sin permiso recibe 403.
3. Escaneo SNMP de red interna (monitoring/api/snmp.py)
Vulnerabilidad: snmp/test e snmp/interfaces no tenían el guard is_private_ip que ya usa snmp/poll. Permitían sondear IPs internas arbitrarias (vector de escaneo de red privada).
Corrección: Añadido is_private_ip() a los 2 endpoints faltantes. Devuelven {"skipped": True, "reason": "private_ip"} sin tocar la red.
4. Inyección PromQL en grupos (monitoring/services/group_service.py)
Vulnerabilidad: query_group_metric interpolaba metric, tenant_id y target_ids en la query MetricsQL sin validar. Un metric malicioso (cpu"}} or snmp_bandwidth_in_mbps{tenant_id="99) podría romper la query y leer datos de otro tenant (cross-tenant read).
Corrección: Validación contra allowlist _VALID_METRIC_NAME (regex ^[a-zA-Z_:][a-zA-Z0-9_:]*$) para metric; .isdigit() para tenant_id y target_ids. La query no puede romperse.
Correctness + fuga de información
snmp/poll devuelve estado inválido
Problema: En caso de fallo SNMP, devolvía {"status": "error"} — pero "error" no está en STATUS_CHOICES del modelo.
Corrección: Ahora devuelve "down" (estado válido).
DoS de memoria en HTTP probe
Problema: El probe descargaba el body completo sin límite. Un servidor malicioso o un archivo grande podía agotar la RAM del worker.
Corrección:
- Lectura en streaming (no en memoria)
- Cap de 2 MB (
MAX_RESPONSE_BYTES) - Timing con
time.monotonic()(nodatetime.now()) datetimeaware (sinutcnow()deprecado)
Excepción SNMP silenciosa
Problema: get_single, walk, get_interface_traffic y test_connection tragaban excepciones sin logging. Invisible para debugging.
Corrección:
get_single,walk,get_interface_traffic→logger.debug("SNMP ... failed: %s", e)test_connection→logger.warning()+ devuelve mensaje genérico al cliente (nostr(exc))
Log expone credenciales SNMP v3
Problema: network/services/device_discovery/snmp_auth.py registraba el username SNMP v3.
Corrección: Log solo muestra auth= y priv=, sin username.
Backlog (Etapa 3 — Regla 22)
Carve-outs documentados para tanda futura:
- Probes síncronos → async/Huey: SNMP walks, ping y HTTP bloquean el worker ASGI.
- Cifrado de credenciales SNMP v3: Hoy en texto plano en el
configdel modelo. - IP-pinning anti-DNS-rebinding:
net_guardhoy resuelve + rechaza; el fix de “pinning” (usar la IP resuelta en el transport, no volver a resolver) es una defensa más fuerte. - Defensa en profundidad en
ping_service: El caller ya guarda; considerar guard también en el servicio. - Delegación en webhook validator:
validate_webhook_url(ITSM sub-área 4) podría delegar ennet_guard. - Correlation service: Subnet IPv6, N+1 KnownIssue.
- ThreadPoolExecutor por request en group_service: Hoy global; considerar por request para seguridad en multi-tenant.
Testing
- 15 tests nuevos en
tests/api/test_monitoring.py:- Gate de permisos SNMP (readonly → 403, admin → skip private)
- Guard SSRF (bloquea metadata, loopback, RFC1918, esquema)
http_service.checkno conecta a destinos internos- Inyección PromQL rechazada en
query_group_metric
- 253 verde en
tests/api/(1 skip preexistente) - Sin migraciones necesarias
Cambios a nivel de API
Endpoints SNMP: nuevos esquemas de respuesta
snmp/test, snmp/interfaces:
- Si
is_private_ip()→{"success": False, "skipped": True, "reason": "private_ip"}(test) /{"target_id": ..., "interfaces": [], "skipped": True, "reason": "private_ip"}(interfaces) - Requiere
observatory:edit
snmp/poll:
- En fallo SNMP:
{"status": "down", "error": "..."}(no"error"solo) - Requiere
observatory:edit
HTTP check: error es genérico
http_service.check/check_async: En caso de excepción o bloqueo SSRF, devuelve {"status": "down", "error": "HTTP check failed"} (no expone el mensaje de excepción interno).
Véase también
- [[entity—monitoring—service—net-guard]]
- [[entity—monitoring—api—snmp]]
- [[entity—monitoring—service—http-service]]
- [[entity—monitoring—service—group-service]]
- [[entity—monitoring—service—snmp-service]]
- [[concept—saas—multi-tenancy]]
- [[concept—security—authorization]]