Descripción
Cierre de cuatro amenazas críticas en el Agente local (terminal/agent/.exe) detectadas en la Auditoría Suprema (Sesión 124). Los cambios son internos al ejecutable — no alteran el contrato HTTP con el frontend ni requieren cambios en la UI. El Agent se recompila a mano (build_agent.bat) y se publica como GitHub Release asset con bump de versión 2.0.22.
Cambios de seguridad
R4: SSH host-key TOFU (anti-MITM en la LAN)
Problema: El Agent conectaba a dispositivos de red con known_hosts=None / auth_strict_key=False — la verificación de identidad SSH estaba desactivada. Un atacante en la red local podía interponer un proxy (MITM) y capturar credenciales o inyectar comandos.
Solución: Nuevo módulo network/host_keys.py implementa Trust-On-First-Use (TOFU):
- Primera conexión a un host: registra la clave pública del dispositivo en un fichero
known_hostscompatible con asyncssh y Scrapli. - Conexiones posteriores: rechaza si la clave ha cambiado (detect impostor).
- Degradación segura: si no hay registro previo y no se puede captar la clave, solo entonces acepta sin verificación — nunca rompe conectividad anterior.
Aplicado en:
network/ssh.py(SSHBridge terminal + Scrapli exec + fallback asyncssh)routes/network.py(endpoint/network/ssh-show)
Impacto: primer acceso a un nuevo dispositivo es igual que antes; accesos posteriores quedan protegidos.
R5: Credenciales SNMP cifradas en reposo (DPAPI)
Problema: Campos snmp_community / snmp_v3_auth_key / snmp_v3_priv_key se guardaban en texto plano en metrics.db (SQLite del Agent en el PC del cliente). Los tokens JWT ya iban cifrados con DPAPI, pero las credenciales de dispositivo no.
Solución: Nuevo módulo core/crypto.py cifra estos campos con DPAPI (el mismo mecanismo que protege los JWTs, atado a la cuenta Windows):
encrypt_secret(value)→ cifra antes de escribir en BD (encore/store.py::upsert_target)decrypt_secret(value)→ descifra transparente al leer (enget_target_list)- Tolerante a legacy: DBs previas que tengan credenciales en claro siguen funcionando. Las credenciales se re-cifran automáticamente en el siguiente
upsertdesde el SaaS.
Impacto: las credenciales están protegidas por la cuenta Windows del cliente; si alguien logra acceso físico o fichero, no puede descifrarlas sin las claves del usuario.
R6: Trap receiver con allowlist + caps
Problema: El receptor SNMP de traps (UDP 0.0.0.0:162) aceptaba y persistía traps de cualquier origen. Riesgos: DoS de disco (llenar metrics.db con spam), spoof de telemetría (inyectar avisos falsos como si vinieran de equipos monitorizados).
Solución: Mejoras en trap_receiver.py:
- Allowlist por IP origen: solo persiste traps que vienen de targets en la lista de monitorización.
- Cap de ritmo por fuente: limita cuántos traps se aceptan de una IP en un período.
- Guarda de tamaño de datagrama: rechaza datagramas anormalmente grandes.
- Contadores nuevos en stats:
traps_dropped_unknown,traps_rate_limited,traps_oversized.
Impacto: la telemetría SNMP queda inmune a DoS y spoofing.
CVE asyncssh #81 + otras librerías
asyncssh 2.22.0 → 2.23.0(GHSA-g794-3fmp-753h)aiohttp 3.13.4 → 3.14.0(GHSA-hg6j-4rv6-33pg, GHSA-jg22-mg44-37j8)PyJWT 2.12.0 → 2.13.0(PYSEC-2026-175…179)
Actualizadas en requirements-agent.lock. Cierra el cron rojo agent-pip-audit (activo desde s101).
Limpieza arquitectónica
12 módulos de dead code removidos (Regla 13 — código inerte del monolito v1):
saas_connector.py,auth_manager.py,sync_manager.py,offline_cache.py,timeseries_store.pyinstaller.py,scrapli_manager.py,ssh_bridge.py,network_utils.pydeep_discovery_handler.py,logging_handler.py,windows_integration.py
Verificación: subgrafo cerrado (solo se importaban entre sí; 0 importadores vivos). Sus líneas también se quitaron de build_agent.bat y del README.
Reducción de bloat en el .exe: menos código bundled = ejecutable más pequeño y surface de ataque menor.
Validación
- Tests:
tests/agent/test_agent_hardening_b1.py(9 test cases). Cargan módulos por ruta para evitar deps Windows-only y colisiones con el app Djangocore. - Smoke test manual: tras recompilar con
build_agent.bat, validar que el Agent arranca y conecta a dispositivos de prueba. - No hay migraciones: SQLite del Agent se autoconfigura.
Lo que queda (Fase B — coordinar aparte)
- Raíz 3 (Autenticación de la API HTTP local): añadir contraseña a la API local del Agent + cerrar CORS — cambia el contrato con ~40 ficheros JS del frontend.
- Hardening backend diferido:
UniqueConstraintPrimary y otros cambios en modelos Django.
Véase también
- [[entity—terminal—service—agent-crypto]]
- [[entity—terminal—service—agent-host-keys]]
- [[concept—security—dpapi-windows]]
- [[concept—network—ssh-tofu]]
- [[concept—snmp—trap-validation]]