Volver a la wiki

Auditoría Suprema · Monitoring sub-área 1 (targets+métricas+ingesta) — fixes s106

Contexto

Cuarto dominio de la Auditoría Suprema (ver [[concept—onboarding—mapa-maestro-ecosistema-crearack]]). monitoring es el más grande del producto (16.7k LOC) → troceado en 6 sub-áreas, 1 por sesión. Esta página documenta la sub-área 1 (targets + métricas + ingesta, 2.821 LOC) auditada y arreglada en s106 (PR #64, mergeado, CI verde).

Resultado de la auditoría: 47 hallazgos crudos → 44 dedup → 36 confirmados (6 ALTA / 12 MEDIA / 18 BAJA), 8 rechazados por verificación adversarial. Datos: auditoria-suprema/monitoring-sa1.{md,json}.

Decisión de Edu: “todo de una vez” (Regla 22, patrón [[feature—blueprints—auditoria-s105]]).

Patrón raíz: Broken Access Control sistémico

grep require_perm|has_permission|is_admin en TODA la app monitoring = 0 ocurrencias antes de s106. Ni lecturas, ni mutaciones, ni acciones de red estaban gateadas; el único portero era la autenticación global (config/urls.py:_global_api_auth) + el aislamiento por org de get_target_or_404. Un usuario readonly (rol DEFAULT, observatory:view) podía crear/editar/borrar targets y disparar polling activo.

Fixes de seguridad (ALTA)

  1. Gate de permisos — core.utils.require_perm(request, "observatory", view/edit) en targets.py, operations.py, batch.py, metrics.py. Lecturas → view; mutaciones y acciones de red (ping/SNMP/HTTP/deep-discover) → edit. Mismo helper reutilizable creado en s105.
  2. Credenciales SNMP fuera de la respuesta — MonitoringTargetOut serializaba el config completo (con snmp_community, snmp_v3_auth_key, snmp_v3_priv_key). Ahora resolve_config devuelve MonitoringTarget.public_config (strip por substring: community|password|auth_key|priv_key|secret|credential) + flag snmp_configured. El PUT (update_target) hace merge defensivo: re-inyecta los secretos existentes que el cliente no reenvía (porque GET no los expone), evitando que un round-trip los borre.
  3. Inyección PromQL — metrics.py construía el metric_name VM concatenando claves de config['monitoring_oids']/['fast_poll_oids'] (controladas por el usuario) e interpolaba sin sanear en metrics_reader.get_generic_metric (f'last_over_time({metric_name}{{tenant_id="..."}}...')). Como el único aislamiento multi-tenant en VictoriaMetrics es el label tenant_id dentro de la query, una clave con } rompía el selector → lectura cross-tenant. Ahora valida contra allowlist _VALID_METRIC_NAME = ^[a-zA-Z_:][a-zA-Z0-9_:]*$; claves inválidas se descartan + log.
  4. Guard org=None — require_org() en common.py lanza HttpError(403) en lugar del 500/AttributeError (que filtraba str(exc) por el handler global) cuando el principal no tiene organización (heatmap/health/CRUD bajo Agent JWT).

Bug nuevo descubierto al verificar: routing batch 405

batch_router se registraba después de targets_router en monitoring/api/__init__.py. Como Ninja usa segmentos de path string, /targets/batch-ping|batch-snmp|batch-http quedaban ensombrecidas por /targets/{target_id} y devolvían 405 Method Not Allowed (POST inexistente en esa ruta) → los endpoints batch eran inalcanzables. Reordenado batch_router antes de targets_router. El gate añadido a batch ahora es efectivo.

Lección: las rutas literales de un router deben registrarse antes que las paramétricas de otro router montado en el mismo prefijo.

Fixes correctness / limpieza

Tests

16 nuevos en tests/api/test_monitoring.py: autorización (readonly→403 en crear/borrar/batch), no-fuga de credenciales SNMP en GET, merge defensivo en PUT, aislamiento batch cross-org, guard de inyección PromQL. 238 verde en tests/api/ (1 skip preexistente). Sin migraciones (solo properties/methods). ruff check + format limpios.

Backlog Etapa 3 (carve-outs Regla 22 / fuera de sub-área)

Relacionado

Véase también

Subir