From d2ae32a47a99ef26a11cfd1626ee2270fab02d1e Mon Sep 17 00:00:00 2001 From: Xavier Rodriguez Date: Tue, 9 Dec 2025 12:49:14 +0100 Subject: [PATCH] Remove legacy configuration and scripts for Cisco MAC Monitor - Deleted old configuration file (config.yaml) containing device and monitoring settings. - Removed outdated Docker Compose file (docker-compose.yml) for container setup. - Eliminated entrypoint script (entrypoint.sh) that handled container initialization and command processing. - Cleared example output JSON file (example_output.json) used for testing. - Removed test suite script (test_suite.sh) that validated the project structure and functionality. - Deleted Zabbix integration script (zabbix_integration.sh) for external script execution. - Removed Zabbix template examples (zabbix_template_examples.md) for monitoring setup. - Updated permissions for run.sh and test_new_format.py scripts. --- build.sh | 0 cisco_mac_http_service.py | 0 no/COMPATIBILITY_FIXES.md | 194 ------------- no/DOCKER_USAGE.md | 72 ----- no/Dockerfile | 58 ---- no/ESTRUCTURA_ORGANIZADA.md | 106 -------- no/HTTPS_SECURE_GUIDE.md | 308 --------------------- no/HTTP_SERVICE_GUIDE.md | 240 ---------------- no/IMPLEMENTACION_COMPLETADA.md | 122 --------- no/PROMPT | 8 - no/README.md | 382 -------------------------- no/cisco_mac_monitor.py | 370 ------------------------- no/debug_parser.py | 23 -- no/debug_test.py | 16 -- no/docker-compose.http.yml | 51 ---- no/docker-compose.yml | 39 --- no/scripts/test_suite.sh | 342 ----------------------- no/test_all_ports.py | 102 ------- no/test_compatibility.py | 182 ------------- no/test_https_api.sh | 49 ---- no/test_interfaces_simulation.py | 115 -------- no/test_special_chars.sh | 71 ----- no/zabbix_integration.sh | 157 ----------- old/Dockerfile | 82 ------ old/README.md | 453 ------------------------------- old/README_OLD.md | 41 --- old/cisco_mac_monitor.py | 423 ----------------------------- old/config.yaml | 93 ------- old/docker-compose.yml | 79 ------ old/entrypoint.sh | 302 --------------------- old/example_output.json | 39 --- old/test_suite.sh | 348 ------------------------ old/zabbix_integration.sh | 132 --------- old/zabbix_template_examples.md | 308 --------------------- run.sh | 0 test_new_format.py | 0 36 files changed, 5307 deletions(-) mode change 100644 => 100755 build.sh mode change 100644 => 100755 cisco_mac_http_service.py delete mode 100644 no/COMPATIBILITY_FIXES.md delete mode 100644 no/DOCKER_USAGE.md delete mode 100644 no/Dockerfile delete mode 100644 no/ESTRUCTURA_ORGANIZADA.md delete mode 100644 no/HTTPS_SECURE_GUIDE.md delete mode 100644 no/HTTP_SERVICE_GUIDE.md delete mode 100644 no/IMPLEMENTACION_COMPLETADA.md delete mode 100644 no/PROMPT delete mode 100644 no/README.md delete mode 100755 no/cisco_mac_monitor.py delete mode 100644 no/debug_parser.py delete mode 100644 no/debug_test.py delete mode 100644 no/docker-compose.http.yml delete mode 100644 no/docker-compose.yml delete mode 100755 no/scripts/test_suite.sh delete mode 100644 no/test_all_ports.py delete mode 100644 no/test_compatibility.py delete mode 100755 no/test_https_api.sh delete mode 100644 no/test_interfaces_simulation.py delete mode 100644 no/test_special_chars.sh delete mode 100755 no/zabbix_integration.sh delete mode 100644 old/Dockerfile delete mode 100644 old/README.md delete mode 100644 old/README_OLD.md delete mode 100755 old/cisco_mac_monitor.py delete mode 100644 old/config.yaml delete mode 100644 old/docker-compose.yml delete mode 100755 old/entrypoint.sh delete mode 100644 old/example_output.json delete mode 100755 old/test_suite.sh delete mode 100755 old/zabbix_integration.sh delete mode 100644 old/zabbix_template_examples.md mode change 100644 => 100755 run.sh mode change 100644 => 100755 test_new_format.py diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 diff --git a/cisco_mac_http_service.py b/cisco_mac_http_service.py old mode 100644 new mode 100755 diff --git a/no/COMPATIBILITY_FIXES.md b/no/COMPATIBILITY_FIXES.md deleted file mode 100644 index 4b694ed..0000000 --- a/no/COMPATIBILITY_FIXES.md +++ /dev/null @@ -1,194 +0,0 @@ -# Cisco MAC Monitor - Fixes de Compatibilidad - -## 📋 Resumen - -Este documento describe las correcciones de compatibilidad implementadas en el proyecto **Cisco MAC Monitor** para asegurar la conectividad SSH con dispositivos Cisco antiguos y modernos. - ---- - -## 🔧 Fix SSH para Dispositivos Antiguos - -### Problema Identificado - -Los dispositivos Cisco con versiones IOS antiguas (especialmente 12.x y algunas versiones 15.x) requieren algoritmos criptográficos legacy que han sido deprecados en versiones modernas de SSH por razones de seguridad. - -### Síntomas del Problema - -- Fallos de conexión SSH con errores como: - ``` - paramiko.ssh_exception.SSHException: Incompatible ssh peer (no acceptable kex algorithm) - paramiko.ssh_exception.SSHException: Incompatible ssh peer (no acceptable host key types) - ``` -- Timeouts durante el handshake SSH -- Imposibilidad de conectar desde OpenSSH moderno sin configuración específica - -### Algoritmos Requeridos - -Los dispositivos Cisco antiguos requieren: - -1. **Key Exchange Algorithms (KEX)**: - - `diffie-hellman-group1-sha1` ⚠️ *Legacy, pero necesario* - -2. **Host Key Algorithms**: - - `ssh-rsa` ⚠️ *Deprecated en SSH moderno, pero esencial* - -### Solución Implementada - -#### Ubicación del Fix -- **Archivo**: `cisco_mac_monitor.py` -- **Clase**: `CiscoSSHConnector` -- **Método**: `connect()` - -#### Cambios Realizados - -```python -def connect(self) -> bool: - """Establece conexión SSH con el dispositivo""" - try: - # Configuración especial para dispositivos Cisco antiguos - # Estos dispositivos requieren algoritmos legacy para conectividad - - # Crear transport manualmente para configurar algoritmos - transport = paramiko.Transport((self.host, self.port)) - transport.set_keepalive(30) - - # Obtener opciones de seguridad y agregar algoritmos antiguos - security_opts = transport.get_security_options() - - # Añadir diffie-hellman-group1-sha1 para dispositivos antiguos - if 'diffie-hellman-group1-sha1' not in security_opts.kex: - security_opts.kex = ['diffie-hellman-group1-sha1'] + list(security_opts.kex) - - # Asegurar que ssh-rsa esté disponible - if 'ssh-rsa' not in security_opts.key_types: - security_opts.key_types = ['ssh-rsa'] + list(security_opts.key_types) - - # Resto de la implementación... -``` - -#### Características del Fix - -✅ **Backward Compatible**: Mantiene soporte para algoritmos modernos -✅ **Forward Compatible**: No rompe conectividad con equipos nuevos -✅ **Priorización Inteligente**: Coloca algoritmos legacy al inicio de la lista -✅ **Logging Mejorado**: Indica cuándo se usan algoritmos de compatibilidad -✅ **Manejo de Errores**: Fallos específicos por problemas de algoritmos - ---- - -## 🛠️ Detalles Técnicos - -### Equivalencia OpenSSH - -El fix implementado es equivalente a usar estas opciones en OpenSSH: - -```bash -ssh -o KexAlgorithms=+diffie-hellman-group1-sha1 \ - -o HostKeyAlgorithms=+ssh-rsa \ - admin@192.168.1.10 -``` - -### Modelos Cisco Afectados - -Los siguientes modelos suelen requerir estos algoritmos: - -- **Catalyst 2960** (IOS 12.2) -- **Catalyst 3560** (IOS 12.2) -- **Catalyst 2950** (IOS 12.1) -- **Catalyst 3550** (IOS 12.1) -- **ISR 1800/2800/3800** Series (IOS 12.4) -- **ASA 5500** Series (versiones antiguas) - -### ⚠️ Consideraciones de Seguridad - -1. **Uso Limitado**: Solo usar estos algoritmos en redes internas controladas -2. **Deprecación**: `diffie-hellman-group1-sha1` tiene vulnerabilidades conocidas -3. **Mitigación**: Mantener dispositivos en VLANs de management seguras -4. **Upgrade Path**: Migrar a hardware moderno cuando sea posible - ---- - -## 🧪 Testing y Validación - -### Test de Compatibilidad - -```bash -# Test con dispositivo moderno (debería usar algoritmos seguros) -python3 cisco_mac_monitor.py --host 192.168.1.100 --username admin --password cisco123 --debug - -# Test con dispositivo antiguo (debería usar algoritmos legacy) -python3 cisco_mac_monitor.py --host 192.168.1.10 --username admin --password cisco123 --debug -``` - -### Verificar Logs - -Los logs deben mostrar: - -``` -2024-11-13 10:30:00 - __main__ - INFO - Conectando a 192.168.1.10 con algoritmos compatibles -2024-11-13 10:30:01 - __main__ - INFO - Conexión exitosa a 192.168.1.10 -``` - -### Docker Testing - -```bash -# Build con nuevos cambios -docker build -t cisco-mac-monitor:compatibility . - -# Test con dispositivo antiguo -docker run --rm --network host cisco-mac-monitor:compatibility \ - --host 192.168.1.10 --username admin --password cisco123 --debug -``` - ---- - -## 📝 Notas de Implementación - -### Por Qué Este Enfoque - -1. **Paramiko Transport**: Permite control granular sobre algoritmos SSH -2. **Lista Prepend**: Agrega algoritmos legacy sin remover los modernos -3. **Detección Automática**: No requiere configuración por dispositivo -4. **Zero Breaking Changes**: Backward compatible con código existente - -### Alternativas Consideradas - -1. ❌ **Configuración por dispositivo**: Muy complejo de mantener -2. ❌ **Fork de Paramiko**: Innecesariamente complejo -3. ❌ **SSH subprocess**: Perdería funcionalidad Python -4. ✅ **Transport manual**: Balance perfecto de control y simplicidad - ---- - -## 🔮 Roadmap Futuro - -### Mejoras Planificadas - -- [ ] Detección automática de versión IOS -- [ ] Configuración de algoritmos por modelo de dispositivo -- [ ] Métricas de algoritmos SSH utilizados -- [ ] Alertas cuando se usan algoritmos legacy - -### Migration Path - -A medida que los dispositivos se actualicen: - -1. **Monitoring**: Trackear qué dispositivos usan algoritmos legacy -2. **Reporting**: Dashboard de compatibilidad SSH -3. **Gradual**: Migrar dispositivos a firmware moderno -4. **Eventually**: Remover soporte legacy cuando sea seguro - ---- - -## 📚 Referencias - -- [RFC 4253 - SSH Transport Layer Protocol](https://tools.ietf.org/html/rfc4253) -- [Paramiko Documentation](https://docs.paramiko.org/) -- [Cisco IOS SSH Configuration Guide](https://cisco.com/c/en/us/support/docs/security-vpn/secure-shell-ssh/4145-ssh.html) -- [OpenSSH Legacy Algorithm Support](https://www.openssh.com/legacy.html) - ---- - -**Fecha**: 13 de noviembre de 2024 -**Autor**: Sistema de Monitorización de Red -**Versión**: 1.0 diff --git a/no/DOCKER_USAGE.md b/no/DOCKER_USAGE.md deleted file mode 100644 index d957796..0000000 --- a/no/DOCKER_USAGE.md +++ /dev/null @@ -1,72 +0,0 @@ -# Docker Usage Guide - Los Vecinos de Cisco - -## 🐳 Imagen Docker Creada - -```bash -REPOSITORY TAG IMAGE ID CREATED SIZE -localhost/los-vecinos-de-cisco develop f3b4044bcfa4 2 minutes ago 805MB -``` - -## 🚀 Cómo Usar la Imagen - -### Uso básico -```bash -docker run --rm --network host localhost/los-vecinos-de-cisco:develop \ - --host 192.168.1.10 \ - --username admin \ - --password cisco123 -``` - -### Con hostname personalizado -```bash -docker run --rm --network host localhost/los-vecinos-de-cisco:develop \ - --host 192.168.1.10 \ - --username admin \ - --password cisco123 \ - --hostname SW-CORE-001 -``` - -### Con modo debug -```bash -docker run --rm --network host localhost/los-vecinos-de-cisco:develop \ - --host 192.168.1.10 \ - --username admin \ - --password cisco123 \ - --debug -``` - -### Modo silencioso (solo JSON) -```bash -docker run --rm --network host localhost/los-vecinos-de-cisco:develop \ - --host 192.168.1.10 \ - --username admin \ - --password cisco123 \ - --quiet -``` - -## 🔧 Características de Compatibilidad SSH - -La imagen incluye soporte automático para: -- ✅ **diffie-hellman-group1-sha1** para dispositivos antiguos -- ✅ **ssh-rsa** host key algorithms -- ✅ Compatible con IOS 12.x y 15.x -- ✅ Mantiene compatibilidad con dispositivos modernos - -## 📝 Notas Importantes - -1. **Plataforma**: Construida para `linux/amd64` -2. **Red**: Usar `--network host` para acceso directo a dispositivos -3. **Compatibilidad**: Funciona con Python 3.6+ en Rocky Linux 8 -4. **Salida**: JSON por stdout, logs por stderr - -## 🧪 Testing - -Para verificar que funciona: -```bash -# Ver ayuda -docker run --rm localhost/los-vecinos-de-cisco:develop --help - -# Test rápido (fallará por falta de dispositivo, pero verifica imagen) -docker run --rm localhost/los-vecinos-de-cisco:develop \ - --host test --username test --password test --timeout 1 -``` \ No newline at end of file diff --git a/no/Dockerfile b/no/Dockerfile deleted file mode 100644 index dcd77d9..0000000 --- a/no/Dockerfile +++ /dev/null @@ -1,58 +0,0 @@ -# Dockerfile para Cisco MAC Monitor con compatibilidad SSH legacy -# =============================================================== -FROM rockylinux:8 - -# Metadatos -LABEL maintainer="Equipo de Monitorización de Red" -LABEL description="Monitor de direcciones MAC para dispositivo Cisco con soporte SSH legacy" -LABEL version="2.0-ssh-compat" - -# Variables de entorno -ENV PYTHONUNBUFFERED=1 -ENV PYTHONDONTWRITEBYTECODE=1 -ENV LANG=en_US.UTF-8 -ENV LC_ALL=en_US.UTF-8 - -# Actualizar sistema e instalar dependencias base -RUN dnf update -y && \ - dnf install -y \ - python3 \ - python3-pip \ - python3-devel \ - openssh-clients \ - gcc \ - openssl-devel \ - libffi-devel \ - krb5-devel && \ - dnf clean all - -# Crear usuario no privilegiado -RUN useradd -m -u 1000 -s /bin/bash monitor - -# Instalar dependencias Python con versiones específicas para compatibilidad -RUN pip3 install --no-cache-dir --upgrade pip && \ - pip3 install --no-cache-dir \ - 'paramiko>=2.12.0,<4.0' \ - 'PyYAML>=5.0,<7.0' \ - 'cryptography>=3.4.8,<42.0' - -# Cambiar al usuario no privilegiado -USER monitor -WORKDIR /app - -# Copiar scripts principales -COPY --chown=monitor:monitor cisco_mac_monitor.py /app/ -COPY --chown=monitor:monitor test_compatibility.py /app/ - -# Permisos de ejecución -USER root -RUN chmod +x /app/cisco_mac_monitor.py /app/test_compatibility.py -USER monitor - -# Healthcheck mejorado que verifica la configuración SSH -HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD python3 /app/test_compatibility.py > /dev/null && \ - python3 /app/cisco_mac_monitor.py --help > /dev/null || exit 1 - -# Entry point -ENTRYPOINT ["python3", "/app/cisco_mac_monitor.py"] \ No newline at end of file diff --git a/no/ESTRUCTURA_ORGANIZADA.md b/no/ESTRUCTURA_ORGANIZADA.md deleted file mode 100644 index cefd26c..0000000 --- a/no/ESTRUCTURA_ORGANIZADA.md +++ /dev/null @@ -1,106 +0,0 @@ -# 📁 ESTRUCTURA DEL PROYECTO ORGANIZADA - -## 🎯 **ARCHIVOS ESENCIALES EN RAÍZ** - -La raíz del proyecto ahora contiene únicamente los archivos **indispensables** para construir y usar la imagen Docker: - -``` -/ -├── 📄 README.md # Documentación principal del proyecto -├── 🐳 Dockerfile.http # Dockerfile para construcción de imagen HTTPS -├── 📋 requirements.txt # Dependencias Python optimizadas -├── 🐍 cisco_mac_monitor.py # Código principal - Conector SSH y parser -├── 🌐 cisco_mac_http_service.py # Servicio HTTPS con API REST -├── 📁 no/ # Archivos no esenciales movidos aquí -└── 📁 old/ # Versiones antiguas del código -``` - -## ✅ **ARCHIVOS ESENCIALES PARA DOCKER:** - -### 1. **Código Python** -- `cisco_mac_monitor.py` - Funcionalidad core SSH + parsing -- `cisco_mac_http_service.py` - Servidor HTTPS + API REST - -### 2. **Configuración Docker** -- `Dockerfile.http` - Para construir la imagen -- `requirements.txt` - Dependencias mínimas - -### 3. **Documentación** -- `README.md` - Instrucciones de uso - ---- - -## 📂 **ARCHIVOS MOVIDOS A CARPETA "no/"** - -Todos los archivos que **NO** son estrictamente necesarios para construir la imagen Docker han sido organizados en `/no/`: - -### 📚 **Documentación y Guías** -- `COMPATIBILITY_FIXES.md` - Documentación de compatibilidad SSH -- `DOCKER_USAGE.md` - Guía de uso de Docker -- `HTTPS_SECURE_GUIDE.md` - Guía de configuración HTTPS -- `HTTP_SERVICE_GUIDE.md` - Documentación del servicio HTTP -- `IMPLEMENTACION_COMPLETADA.md` - Resumen de implementación -- `PROMPT` - Archivo de prompt original - -### 🐳 **Configuraciones Docker Alternativas** -- `Dockerfile` - Dockerfile original (CLI) -- `docker-compose.yml` - Composición original -- `docker-compose.http.yml` - Composición para HTTPS - -### 🧪 **Scripts de Prueba y Testing** -- `test_all_ports.py` - Prueba de funcionalidad completa -- `test_compatibility.py` - Pruebas de compatibilidad SSH -- `test_https_api.sh` - Tests de API HTTPS -- `test_interfaces_simulation.py` - Simulación de interfaces -- `scripts/test_suite.sh` - Suite de pruebas completa - -### 🔧 **Scripts de Integración** -- `zabbix_integration.sh` - Script de integración con Zabbix - ---- - -## 🚀 **CONSTRUCCIÓN DE IMAGEN SIMPLIFICADA** - -Con la estructura limpia, construir la imagen es ahora muy simple: - -```bash -# En la raíz del proyecto -docker build --platform linux/amd64 -t cisco-mac-monitor:https -f Dockerfile.http . - -# Ejecutar -docker run -d --name cisco-mac-https -p 8443:8443 cisco-mac-monitor:https -``` - ---- - -## 💡 **BENEFICIOS DE LA REORGANIZACIÓN** - -### ✅ **Simplicidad** -- Raíz limpia con solo archivos esenciales -- Fácil identificación de lo necesario vs. documentación - -### ✅ **Mantenimiento** -- Separación clara entre código de producción y testing -- Dockerfile más eficiente al no copiar archivos innecesarios - -### ✅ **Documentación Preservada** -- Toda la documentación se mantiene accesible en `/no/` -- Historia del proyecto conservada en `/old/` - -### ✅ **Construcción Optimizada** -- Menos archivos en contexto de Docker = build más rápido -- Cache de Docker más eficiente - ---- - -## 📋 **COMANDOS DE CONSTRUCCIÓN VERIFICADOS** - -✅ **Imagen construida exitosamente** con la nueva estructura: -``` -✅ FINISHED (14/14) - cisco-mac-monitor:clean -✅ Todos los archivos esenciales presentes -✅ Requirements.txt utilizado correctamente -✅ Estructura limpia y organizada -``` - -La reorganización está **completa y verificada** ✨ \ No newline at end of file diff --git a/no/HTTPS_SECURE_GUIDE.md b/no/HTTPS_SECURE_GUIDE.md deleted file mode 100644 index fc0a774..0000000 --- a/no/HTTPS_SECURE_GUIDE.md +++ /dev/null @@ -1,308 +0,0 @@ -# Cisco MAC Monitor - HTTPS Secure Service Guide - -## 🔒 Servicio HTTPS Seguro Deployado - -¡El servicio HTTPS con POST seguro está funcionando perfectamente! Las credenciales ya NO se envían por URLs. - -### 🛡️ Mejoras de Seguridad Implementadas - -✅ **POST en lugar de GET**: Credenciales en JSON body, no en URL -✅ **HTTPS/SSL**: Cifrado TLS con certificado autofirmado -✅ **JSON Body**: Datos estructurados y seguros -✅ **Logs limpios**: No aparecen credenciales en logs - ---- - -## 🌐 API Endpoints - -### 🔐 Monitorización MAC (Seguro) -```http -POST /mac-monitor -Content-Type: application/json - -{ - "host": "192.168.1.10", - "username": "admin", - "password": "cisco123", - "hostname": "SW-CORE-001", // Opcional - "port": 22, // Opcional, default: 22 - "timeout": 30 // Opcional, default: 30 -} -``` - -### 📊 Health Check -```http -GET /health -``` - -### 📈 Métricas -```http -GET /metrics -``` - ---- - -## 🚀 Uso del Servicio - -### Con curl -```bash -# Monitorización básica -curl -k -X POST "https://localhost:8443/mac-monitor" \ - -H "Content-Type: application/json" \ - -d '{"host":"10.150.226.19","username":"itops","password":"Tr4!D0r3s"}' - -# Con parámetros completos -curl -k -X POST "https://localhost:8443/mac-monitor" \ - -H "Content-Type: application/json" \ - -d '{ - "host": "10.150.226.19", - "username": "itops", - "password": "Tr4!D0r3s", - "hostname": "SW-CORE-BUILDING-A", - "timeout": 45 - }' - -# Health check -curl -k "https://localhost:8443/health" -``` - -### Con Python -```python -import requests -import json - -# Configuración -url = "https://localhost:8443/mac-monitor" -payload = { - "host": "10.150.226.19", - "username": "itops", - "password": "Tr4!D0r3s", - "hostname": "SW-CORE-TEST" -} - -# Petición segura -response = requests.post( - url, - json=payload, - verify=False # Para certificado autofirmado -) - -if response.status_code == 200: - data = response.json() - print(f"Puertos encontrados: {data['total_ports']}") - print(f"MACs totales: {data['total_macs']}") -else: - print(f"Error: {response.status_code} - {response.text}") -``` - ---- - -## 🐳 Gestión del Servicio HTTPS - -### Comandos Docker - -```bash -# Iniciar servicio HTTPS (recomendado) -docker run -d -p 8443:8443 --name cisco-mac-https localhost/los-vecinos-de-cisco:https-service - -# Ver logs en tiempo real -docker logs -f cisco-mac-https - -# Reiniciar servicio -docker restart cisco-mac-https - -# Detener servicio -docker stop cisco-mac-https && docker rm cisco-mac-https - -# Verificar estado -docker ps | grep cisco-mac-https -``` - -### Reconstruir tras cambios -```bash -# Reconstruir imagen -docker build --platform linux/amd64 -f Dockerfile.http -t localhost/los-vecinos-de-cisco:https-service . - -# Recrear contenedor -docker stop cisco-mac-https && docker rm cisco-mac-https -docker run -d -p 8443:8443 --name cisco-mac-https localhost/los-vecinos-de-cisco:https-service -``` - ---- - -## 📋 Integración con Zabbix - -### Script de External Check Actualizado - -```bash -#!/bin/bash -# /usr/lib/zabbix/externalscripts/cisco_mac_https_discovery.sh - -HOST="$1" -USERNAME="$2" -PASSWORD="$3" -HOSTNAME="${4:-$HOST}" - -# Crear JSON payload -JSON_PAYLOAD=$(cat <&username=&password=[&hostname=][&port=][&timeout=] -``` - -**Ejemplo:** -```bash -curl "http://localhost:8080/mac-monitor?host=10.150.226.19&username=itops&password=Tr4%21D0r3s" -``` - -#### 2. **Health Check** -```http -GET /health -``` - -**Ejemplo:** -```bash -curl "http://localhost:8080/health" -``` - -**Respuesta:** -```json -{ - "status": "healthy", - "service": "cisco-mac-monitor", - "timestamp": "2025-11-13T08:31:20.504819", - "version": "2.0-http" -} -``` - -#### 3. **Métricas** -```http -GET /metrics -``` - -**Ejemplo:** -```bash -curl "http://localhost:8080/metrics" -``` - ---- - -## 🐳 Gestión del Contenedor - -### Iniciar el Servicio -```bash -# Opción 1: Con mapeo de puertos estándar (recomendado) -docker run -d -p 8080:8080 --name cisco-mac-http localhost/los-vecinos-de-cisco:http-service - -# Opción 2: Con acceso directo a red (para dispositivos locales) -docker run -d --network host --name cisco-mac-http localhost/los-vecinos-de-cisco:http-service - -# Opción 3: Con docker-compose -docker-compose -f docker-compose.http.yml up -d -``` - -### Comandos de Gestión -```bash -# Ver logs en tiempo real -docker logs -f cisco-mac-http - -# Ver estado -docker ps | grep cisco-mac-http - -# Reiniciar servicio -docker restart cisco-mac-http - -# Detener servicio -docker stop cisco-mac-http && docker rm cisco-mac-http -``` - -### Reconstruir Imagen -```bash -# Reconstruir después de cambios -docker build --platform linux/amd64 -f Dockerfile.http -t localhost/los-vecinos-de-cisco:http-service . - -# Recrear contenedor -docker stop cisco-mac-http && docker rm cisco-mac-http -docker run -d -p 8080:8080 --name cisco-mac-http localhost/los-vecinos-de-cisco:http-service -``` - ---- - -## 📝 Logs del Servicio - -### Logs Informativos (stderr) -El servicio mantiene **todos** los logs informativos que tenías antes: - -``` -2025-11-13 08:31:57,395 - cisco_mac_monitor - INFO - Conectando a 10.150.226.19 con algoritmos compatibles -2025-11-13 08:31:58,374 - cisco_mac_monitor - INFO - Conexión exitosa a 10.150.226.19 -2025-11-13 08:31:58,820 - cisco_mac_monitor - INFO - Obtenidas 117 entradas de tabla MAC -2025-11-13 08:31:58,821 - cisco_mac_monitor - INFO - Formateados datos para dispositivo 10.150.226.19: 3 puertos, 117 MACs -2025-11-13 08:31:58,821 - cisco_mac_monitor - INFO - Desconectado de 10.150.226.19 -2025-11-13 08:31:58,822 - __main__ - INFO - Monitorización completada para 10.150.226.19 -``` - -### Respuesta JSON (stdout/HTTP) -El JSON se devuelve **limpio** en la respuesta HTTP, sin mezclarse con logs. - ---- - -## 🔧 Integración con Zabbix - -### Cambio en External Scripts - -**Antes (comando directo):** -```bash -docker run --rm --network host localhost/los-vecinos-de-cisco:develop \ - --host "$HOST" --username "$USERNAME" --password "$PASSWORD" -``` - -**Ahora (petición HTTP):** -```bash -curl -s "http://localhost:8080/mac-monitor?host=$HOST&username=$USERNAME&password=$PASSWORD" -``` - -### Script de Integración Actualizado - -```bash -#!/bin/bash -# /usr/lib/zabbix/externalscripts/cisco_mac_http_discovery.sh - -HOST="$1" -USERNAME="$2" -PASSWORD="$3" -HOSTNAME="${4:-$HOST}" - -# URL encode password si es necesario -PASSWORD_ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PASSWORD'))") - -# Petición al servicio HTTP -curl -s "http://localhost:8080/mac-monitor?host=$HOST&username=$USERNAME&password=$PASSWORD_ENCODED&hostname=$HOSTNAME" -``` - -### Configuración Zabbix - -**Discovery Rule:** -```xml -Name: Cisco MAC HTTP Discovery -Type: External check -Key: cisco_mac_http_discovery.sh[{HOST.IP},{$CISCO_USER},{$CISCO_PASSWORD},{HOST.NAME}] -Update interval: 5m -``` - ---- - -## 🎯 Ventajas del Servicio HTTP - -### ✅ Beneficios - -1. **Rendimiento**: Servidor persistente, sin overhead de inicio de contenedor -2. **Escalabilidad**: Múltiples peticiones concurrentes -3. **Simplicidad**: Peticiones HTTP estándar -4. **Logs Separados**: JSON limpio en respuesta, logs informativos en contenedor -5. **Healthchecks**: Monitorización del servicio incluida -6. **RESTful**: Fácil integración desde cualquier sistema - -### 📊 Comparación - -| Aspecto | Comando Anterior | Servicio HTTP | -|---------|------------------|---------------| -| **Inicio** | ~2-3s por ejecución | <100ms por petición | -| **Concurrencia** | 1 por vez | Múltiples simultáneas | -| **Logs** | Mezclados con JSON | Separados | -| **Integración** | Solo Zabbix | Universal (HTTP) | -| **Debugging** | Difícil | Fácil con curl | - ---- - -## 🧪 Testing Completo - -### Test Manual -```bash -# 1. Health check -curl "http://localhost:8080/health" - -# 2. Métricas -curl "http://localhost:8080/metrics" - -# 3. Test real con dispositivo -curl "http://localhost:8080/mac-monitor?host=10.150.226.19&username=itops&password=Tr4%21D0r3s" - -# 4. Test con parámetros adicionales -curl "http://localhost:8080/mac-monitor?host=10.150.226.19&username=itops&password=Tr4%21D0r3s&hostname=SWITCH-CORE&timeout=45" -``` - -### Test de Rendimiento -```bash -# Test de múltiples peticiones concurrentes -for i in {1..5}; do - curl -s "http://localhost:8080/mac-monitor?host=10.150.226.19&username=itops&password=Tr4%21D0r3s" & -done -wait -``` - -### Monitorización de Logs -```bash -# Ver logs en tiempo real durante testing -docker logs -f cisco-mac-http -``` - ---- - -## 🔮 Próximos Pasos - -1. **Producción**: Desplegar en servidor de monitorización -2. **SSL/TLS**: Añadir HTTPS si es necesario -3. **Autenticación**: API keys si se requiere seguridad adicional -4. **Load Balancer**: Para alta disponibilidad -5. **Métricas**: Integrar con Prometheus/Grafana - ---- - -**El servicio HTTP está 100% operativo y listo para producción! 🚀** \ No newline at end of file diff --git a/no/IMPLEMENTACION_COMPLETADA.md b/no/IMPLEMENTACION_COMPLETADA.md deleted file mode 100644 index df1684b..0000000 --- a/no/IMPLEMENTACION_COMPLETADA.md +++ /dev/null @@ -1,122 +0,0 @@ -# RESUMEN DE IMPLEMENTACIÓN COMPLETADA - -## ✅ FUNCIONALIDADES IMPLEMENTADAS - -### 1. **Compatibilidad SSH con dispositivos Cisco legacy** -- ✅ Algoritmos SSH compatibles con IOS 12.x/15.x -- ✅ `diffie-hellman-group1-sha1` y `ssh-rsa` habilitados -- ✅ Timeout de conexión configurable - -### 2. **Servicio HTTPS seguro** -- ✅ Migración de HTTP a HTTPS con SSL autofirmado -- ✅ Endpoint POST con JSON body (no más credenciales en URL) -- ✅ Puerto 8443 con certificado SSL automático -- ✅ Endpoints: `/mac-monitor`, `/health`, `/metrics` - -### 3. **🎯 NUEVA FUNCIONALIDAD: Mostrar TODAS las interfaces** -- ✅ **Detección de todas las interfaces** mediante `show interfaces status` -- ✅ **Puertos vacíos mostrados con MAC_COUNT=0** -- ✅ **Puertos con MACs mostrados normalmente** -- ✅ **Formato Zabbix LLD compatible** con interfaces sin MACs - -### 4. **Gestión de dependencias mejorada** -- ✅ requirements.txt optimizado con solo librerías esenciales: - - paramiko>=2.12.0,<4.0 - - cryptography>=3.4.8,<42.0 - - PyYAML>=5.0,<7.0 -- ✅ Dockerfile.http actualizado para usar requirements.txt - -### 5. **Contenedorización Docker** -- ✅ Rocky Linux 8 como base -- ✅ Usuario no privilegiado (monitor:1000) -- ✅ Health checks automáticos -- ✅ Plataforma linux/amd64 compatible - ---- - -## 📊 PRUEBA DE FUNCIONALIDAD EXITOSA - -### Resultados de la prueba con datos simulados: -``` -=== RESULTADOS DE LA PRUEBA === -Total de puertos detectados: 7 - - Fa0/1: 2 MACs conectadas ✅ - - Fa0/2: 2 MACs conectadas ✅ - - Fa0/3: 0 MACs (puerto vacío) ⚪ - - Fa0/4: 0 MACs (puerto vacío) ⚪ - - Fa0/5: 0 MACs (puerto vacío) ⚪ - - Gi0/1: 3 MACs conectadas ✅ - - Gi0/2: 0 MACs (puerto vacío) ⚪ - -Puertos con MACs: 3 -Puertos vacíos: 4 ← 🎯 OBJETIVO LOGRADO -``` - -### Salida Zabbix LLD: -```json -{ - "data": [ - { - "{#DEVICE_HOSTNAME}": "cisco-test-device", - "{#PORT_NAME}": "Fa0/3", - "{#MAC_COUNT}": 0, - "{#VLANS}": "", - "{#MAC_ADDRESSES}": "[]" - } - ], - "total_ports": 7, - "ports_with_macs": 3, - "ports_without_macs": 4 -} -``` - ---- - -## 🔧 MÉTODOS IMPLEMENTADOS - -### CiscoMacTableParser - Nuevos métodos: -- `_get_interface_list()` - Ejecuta 'show interfaces status' -- `_parse_interface_list()` - Parsea la salida de interfaces -- `_is_physical_interface()` - Filtra interfaces físicas -- `detected_interfaces` - Lista de interfaces detectadas - -### ZabbixLLDFormatter - Mejorado: -- `format_for_zabbix(mac_entries, all_interfaces)` - Acepta lista de interfaces -- Incluye puertos sin MACs con MAC_COUNT=0 -- Estadísticas en el JSON: total_ports, ports_with_macs, ports_without_macs - ---- - -## 🚀 USO DEL SERVICIO - -### Construcción y ejecución: -```bash -# Construir imagen -docker build --platform linux/amd64 -t cisco-mac-monitor:https -f Dockerfile.http . - -# Ejecutar contenedor -docker run -d --name cisco-mac-https -p 8443:8443 cisco-mac-monitor:https - -# Realizar petición -curl -k -X POST https://localhost:8443/mac-monitor \ - -H "Content-Type: application/json" \ - -d '{ - "host": "10.150.226.19", - "username": "admin", - "password": "password", - "use_legacy_ssh": true - }' -``` - ---- - -## 🎯 RESULTADO FINAL - -**OBJETIVO CUMPLIDO**: El sistema ahora muestra **TODAS** las interfaces del switch, incluyendo aquellas sin dispositivos conectados (MAC_COUNT=0), proporcionando visibilidad completa del estado de los puertos de red para monitorización con Zabbix. - -**Beneficios**: -- ✅ **Visibilidad completa** de todos los puertos -- ✅ **Compatibilidad total** con dispositivos Cisco legacy -- ✅ **Seguridad mejorada** con HTTPS y POST -- ✅ **Gestión eficiente** de dependencias -- ✅ **Contenedorización robusta** lista para producción diff --git a/no/PROMPT b/no/PROMPT deleted file mode 100644 index cbe6baa..0000000 --- a/no/PROMPT +++ /dev/null @@ -1,8 +0,0 @@ - rol: necesito que adquieras el rol de un experto en dispositivos de red de la marca, cisco, con conocimientos de Python - - necesidad: cliente necesita poder monitorizar los dispositivos que hay conectados en cada una de las bocas de sus dispositivos de red de la marca cisco. El cliente nos ha proporcionado un inventario de modelos. Ellos necesitan saber qué direcciones Mac hay conectadas por cada una de las bocas de Red. - - misión: necesitamos montar un pequeño script en Python, el cual se encargue de conectarse a un dispositivo de red dado, y obtener la información que necesita el cliente. este dispositivo de red dado será proporcionado a través de la linea de comandos. las credenciales de acceso al dispositivo también serán proporcionadas a través de la linea de comandos. - - la salida de esta información ha de estar en formato json. la información extraída será consumida por Zabbix. A través de un LLD de una plantilla de Zabbix que ya se creará más adelante, esta alertará en caso de que haya alguna MAC no autorizada conectada a alguna de las bocas de red o si hay cambios en las MACs conectadas. Necesitamos que el script sea capaz de conectarse al dispositivo de red mediante SSH. El script correrá en un contenedor docker en el que ya estará instalado Python y las librerías necesarias para la conexión SSH. - La imagen base deberá ser ROCKY LINUX 8. La información que se necesita extraer de cada dispositivo ha de ser en json como decía antes, pero ha de estar preparada para que Zabbix pueda consumirla a través de un LLD, por lo que la estructura del json ha de ser la adecuada. \ No newline at end of file diff --git a/no/README.md b/no/README.md deleted file mode 100644 index cedea9c..0000000 --- a/no/README.md +++ /dev/null @@ -1,382 +0,0 @@ -# Cisco Single MAC Monitor para Zabbix LLD - -## 📋 Descripción - -**Cisco Single MAC Monitor** es una versión simplificada del monitor de direcciones MAC que se enfoca en **un dispositivo individual por ejecución**. Recibe los parámetros del dispositivo (IP, credenciales) por línea de comandos y genera salida JSON compatible con **Zabbix Low Level Discovery (LLD)**. - -### Características principales: - -- ✅ **Un dispositivo por ejecución** - Parámetros por línea de comandos -- ✅ **Compatible** con modelos Cisco C2960, C3560, C3560E, C2960X, C3560CX y Catalyst -- ✅ **Conexión SSH** segura -- ✅ **Formato JSON** optimizado para Zabbix LLD -- ✅ **Contenedor Docker** ligero basado en Rocky Linux 8 -- ✅ **JSON más manejable** - No hay riesgo de JSON gigante -- ✅ **Integración sencilla** con Zabbix External Scripts - ---- - -## 🚀 Inicio Rápido - -### 1. Construir la imagen Docker - -```bash -# Usar Dockerfile específico para versión single -docker build -f Dockerfile -t cisco-mac-monitor . -``` - -### 2. Ejecutar para un dispositivo - -```bash -# Ejemplo básico -curl -k -X POST https://localhost:8443/mac-monitor -H "Content-Type: application/json" -d '{"host": "10.150.226.19", "username": "itops", "password": "Tr4!D0r3s", "use_legacy_ssh": true}' -``` - -### ⚠️ **Manejo de Contraseñas con Caracteres Especiales** - -```bash -# ✅ CORRECTO: Sin escape para caracteres como ! -curl -k -X POST https://localhost:8443/mac-monitor -H "Content-Type: application/json" -d '{"host": "10.150.226.19", "username": "itops", "password": "Tr4!D0r3s", "use_legacy_ssh": true}' - -# ✅ CORRECTO: Para contraseñas con comillas, usar comillas dobles externas -curl -k -X POST https://localhost:8443/mac-monitor -H "Content-Type: application/json" -d "{\"host\": \"10.150.226.19\", \"username\": \"itops\", \"password\": \"Pass\\\"word\", \"use_legacy_ssh\": true}" - -# ✅ CORRECTO: Para contraseñas muy complejas, usar archivo -echo '{"host": "10.150.226.19", "username": "itops", "password": "C0mpl3x!P@ss#w0rd$", "use_legacy_ssh": true}' > payload.json -curl -k -X POST https://localhost:8443/mac-monitor -H "Content-Type: application/json" -d @payload.json - -# ❌ INCORRECTO: No usar \ para escapar ! en JSON -curl -k -X POST https://localhost:8443/mac-monitor -H "Content-Type: application/json" -d '{"password": "Tr4\!D0r3s"}' # ERROR! -``` - -### 3. Integración con Zabbix - -```bash -# Copiar script de integración -sudo cp zabbix_single_integration.sh /usr/lib/zabbix/externalscripts/ -sudo chmod +x /usr/lib/zabbix/externalscripts/zabbix_single_integration.sh -sudo chown zabbix:zabbix /usr/lib/zabbix/externalscripts/zabbix_single_integration.sh - -# Probar manualmente -/usr/lib/zabbix/externalscripts/zabbix_single_integration.sh 192.168.1.10 admin cisco123 -``` - ---- - -## 📁 Archivos de la Versión Simplificada - -``` -cisco-mac-monitor/ -├── cisco_single_mac_monitor.py # Script principal simplificado -├── Dockerfile.single # Imagen Docker optimizada -├── docker-compose.single.yml # Compose simplificado -├── zabbix_single_integration.sh # Script para Zabbix External Scripts -├── example_single_output.json # Ejemplo de salida JSON -└── README_single.md # Esta documentación -``` - ---- - -## ⚙️ Uso Detallado - -### Parámetros de línea de comandos - -```bash -python3 cisco_single_mac_monitor.py [OPTIONS] - -Argumentos requeridos: - --host/-H IP o hostname del dispositivo Cisco - --username/-u Usuario SSH - --password/-p Contraseña SSH - -Argumentos opcionales: - --port/-P Puerto SSH (default: 22) - --timeout/-t Timeout en segundos (default: 30) - --hostname/-n Nombre del dispositivo (default: usa --host) - --debug/-d Modo debug - --quiet/-q Modo silencioso (solo JSON) -``` - -### Ejemplos de uso - -```bash -# Básico -python3 cisco_single_mac_monitor.py -H 192.168.1.10 -u admin -p cisco123 - -# Puerto SSH personalizado -python3 cisco_single_mac_monitor.py -H 192.168.1.10 -u admin -p cisco123 -P 2222 - -# Con timeout personalizado -python3 cisco_single_mac_monitor.py -H 192.168.1.10 -u admin -p cisco123 -t 60 - -# Modo silencioso (solo JSON por stdout) -python3 cisco_single_mac_monitor.py -H 192.168.1.10 -u admin -p cisco123 -q - -# Con nombre personalizado del dispositivo -python3 cisco_single_mac_monitor.py -H 192.168.1.10 -u admin -p cisco123 -n SW-CORE-001 -``` - ---- - -## 🐳 Uso con Docker - -### Construir imagen - -```bash -# Opción 1: Docker directo -docker build -f Dockerfile.single -t cisco-single-mac-monitor . - -# Opción 2: Docker Compose -docker-compose -f docker-compose.single.yml build -``` - -### Ejecutar contenedor - -```bash -# Ejecución básica -docker run --rm --network host cisco-single-mac-monitor \ - --host 192.168.1.10 --username admin --password cisco123 - -# Con volumen para logs (opcional) -docker run --rm --network host -v /tmp:/tmp cisco-single-mac-monitor \ - --host 192.168.1.10 --username admin --password cisco123 --debug - -# Modo interactivo para debugging -docker run --rm -it --network host cisco-single-mac-monitor --help -``` - ---- - -## 📊 Integración con Zabbix - -### Formato JSON de salida - -```json -{ - "data": [ - { - "{#DEVICE_HOSTNAME}": "SW-CORE-001", - "{#PORT_NAME}": "Fa0/1", - "{#MAC_COUNT}": 2, - "{#VLANS}": "10,20", - "{#MAC_ADDRESSES}": "[{\"mac\": \"aa:bb:cc:dd:ee:01\", \"vlan\": \"10\", \"type\": \"DYNAMIC\"}]", - "{#LAST_UPDATE}": "2024-10-24T14:30:00.123456" - } - ], - "timestamp": "2024-10-24T14:30:00.123456", - "device_hostname": "SW-CORE-001", - "total_ports": 3, - "total_macs": 6 -} -``` - -### Configuración en Zabbix - -**1. Discovery Rule por dispositivo:** - -```xml -Name: Cisco MAC Discovery for {HOST.NAME} -Type: External check -Key: cisco_single_mac_discovery.sh[{HOST.IP},{$CISCO_USER},{$CISCO_PASSWORD},{HOST.NAME}] -Update interval: 5m -``` - -**2. Macros requeridas (nivel de host o template):** - -```xml -{$CISCO_USER} = admin -{$CISCO_PASSWORD} = cisco123 -``` - -**3. Item prototypes:** - -- `cisco.mac.count[{#DEVICE_HOSTNAME},{#PORT_NAME}]` -- `cisco.mac.addresses[{#DEVICE_HOSTNAME},{#PORT_NAME}]` -- `cisco.mac.vlans[{#DEVICE_HOSTNAME},{#PORT_NAME}]` - -**4. Trigger prototypes:** - -- MAC count changed -- Too many MACs on port -- New MAC addresses detected - -### Script de External Check - -```bash -#!/bin/bash -# /usr/lib/zabbix/externalscripts/cisco_single_mac_discovery.sh - -HOST="$1" -USERNAME="$2" -PASSWORD="$3" -HOSTNAME="$4" - -docker run --rm --network host cisco-single-mac-monitor \ - --host "$HOST" \ - --username "$USERNAME" \ - --password "$PASSWORD" \ - --hostname "$HOSTNAME" \ - --quiet -``` - ---- - -## 🔧 Ventajas de la Versión Simplificada - -### ✅ Ventajas - -1. **JSON Manejable**: Un dispositivo = Un JSON pequeño -2. **Escalabilidad**: Cada dispositivo se procesa independientemente -3. **Flexibilidad**: Diferentes credenciales por dispositivo -4. **Paralelización**: Zabbix puede ejecutar múltiples checks en paralelo -5. **Debugging Fácil**: Probar un dispositivo específico -6. **Recursos Mínimos**: Contenedor ligero y rápido - -### 📊 Comparación con versión completa - -| Aspecto | Versión Single | Versión Completa | -|---------|----------------|------------------| -| JSON por ejecución | Pequeño (1 dispositivo) | Grande (N dispositivos) | -| Configuración | Línea de comandos | Archivo YAML | -| Flexibilidad | Alta (por dispositivo) | Media (global) | -| Debugging | Fácil | Complejo | -| Recursos | Mínimos | Moderados | -| Uso en Zabbix | External check directo | Requires data processing | - ---- - -## 🛠️ Desarrollo y Testing - -### Ejecutar sin Docker - -```bash -# Instalar dependencias -pip3 install paramiko PyYAML - -# Ejecutar directamente -python3 cisco_single_mac_monitor.py \ - --host 192.168.1.10 \ - --username admin \ - --password cisco123 \ - --debug -``` - -### Testing del script de integración - -```bash -# Test de dependencias -./zabbix_single_integration.sh test - -# Test manual -./zabbix_single_integration.sh 192.168.1.10 admin cisco123 SW-TEST - -# Test con hostname personalizado -./zabbix_single_integration.sh 192.168.1.10 admin cisco123 SW-CORE-001 -``` - -### Validar JSON - -```bash -# Ejecutar y validar JSON -python3 cisco_single_mac_monitor.py -H 192.168.1.10 -u admin -p cisco123 -q | jq . - -# Verificar estructura Zabbix LLD -python3 cisco_single_mac_monitor.py -H 192.168.1.10 -u admin -p cisco123 -q | jq '.data[0]' -``` - ---- - -## 🔍 Troubleshooting - -### Problemas comunes - -**1. Error de conexión SSH:** -```bash -# Probar conexión manual -ssh admin@192.168.1.10 - -# Debug del script -python3 cisco_single_mac_monitor.py -H 192.168.1.10 -u admin -p cisco123 --debug -``` - -**2. JSON vacío:** -```bash -# Verificar comando en dispositivo -ssh admin@192.168.1.10 -show mac address-table - -# Verificar logs -python3 cisco_single_mac_monitor.py -H 192.168.1.10 -u admin -p cisco123 --debug -``` - -**3. Imagen Docker no encontrada:** -```bash -# Verificar imagen -docker images | grep cisco-single-mac - -# Reconstruir si es necesario -docker build -f Dockerfile.single -t cisco-single-mac-monitor . -``` - -### Logs útiles - -```bash -# Logs del script (van por stderr) -python3 cisco_single_mac_monitor.py -H 192.168.1.10 -u admin -p cisco123 2>debug.log - -# Logs del contenedor -docker run --rm --network host cisco-single-mac-monitor \ - --host 192.168.1.10 --username admin --password cisco123 --debug 2>container.log -``` - ---- - -## 📈 Monitorización en Producción - -### Configuración recomendada en Zabbix - -1. **Por dispositivo de red**: - - Crear macro `{$CISCO_USER}` y `{$CISCO_PASSWORD}` - - Asignar template "Cisco MAC Monitor" - - Discovery rule se ejecuta cada 5 minutos - -2. **Alertas importantes**: - - Cambio en número de MACs por puerto - - Nueva MAC no autorizada - - Puerto con demasiadas MACs (>5) - -3. **Dashboards**: - - Gráfico de MACs por dispositivo - - Top 10 puertos con más MACs - - Alertas recientes de MACs - ---- - -## 🔒 Seguridad - -### Mejores prácticas - -1. **Credenciales**: - - Usar macros de Zabbix para passwords - - Crear usuario específico en switches - - Rotar credenciales regularmente - -2. **Red**: - - Ejecutar en VLAN de management - - Configurar ACLs en switches - - Monitorizar accesos SSH - -3. **Contenedor**: - - Usuario no privilegiado - - Sin volúmenes innecesarios - - Imagen base actualizada - ---- - -## 📄 Licencia - -Este proyecto está bajo licencia MIT. - ---- - -**Versión simplificada optimizada para uso individual por dispositivo** 🎯 \ No newline at end of file diff --git a/no/cisco_mac_monitor.py b/no/cisco_mac_monitor.py deleted file mode 100755 index a5f0bfe..0000000 --- a/no/cisco_mac_monitor.py +++ /dev/null @@ -1,370 +0,0 @@ -#!/usr/bin/env python3 -""" -Cisco MAC Address Monitor - Single Device Version -================================================= - -Script simplificado para monitorizar direcciones MAC en un dispositivo Cisco específico. -Recibe parámetros por línea de comandos y genera salida JSON para Zabbix LLD. - -Compatible con modelos C2960, C3560, C3560E, C2960X, C3560CX y Catalyst series. - -Uso: - python3 cisco_single_mac_monitor.py --host --username --password - -Autor: Sistema de Monitorización de Red -Fecha: Octubre 2024 -""" - -import json -import logging -import argparse -import paramiko -import re -import sys -from datetime import datetime -from typing import List, Dict, Any -from dataclasses import dataclass - - -@dataclass -class MacEntry: - """Entrada de tabla MAC""" - vlan: str - mac_address: str - type: str - port: str - - -class CiscoSSHConnector: - """Conector SSH para dispositivos Cisco""" - - def __init__(self, host: str, username: str, password: str, port: int = 22, timeout: int = 30): - self.host = host - self.username = username - self.password = password - self.port = port - self.timeout = timeout - self.ssh_client = None - self.logger = logging.getLogger(__name__) - - def connect(self) -> bool: - """Establece conexión SSH con el dispositivo""" - try: - self.ssh_client = paramiko.SSHClient() - self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - self.logger.info(f"Conectando a {self.host}") - - self.ssh_client.connect( - hostname=self.host, - port=self.port, - username=self.username, - password=self.password, - timeout=self.timeout, - look_for_keys=False, - allow_agent=False - ) - - self.logger.info(f"Conexión exitosa a {self.host}") - return True - - except Exception as e: - self.logger.error(f"Error conectando a {self.host}: {str(e)}") - return False - - def execute_command(self, command: str) -> str: - """Ejecuta comando en el dispositivo""" - if not self.ssh_client: - raise Exception("No hay conexión SSH activa") - - try: - self.logger.debug(f"Ejecutando comando: {command}") - stdin, stdout, stderr = self.ssh_client.exec_command(command) - - output = stdout.read().decode('utf-8') - error = stderr.read().decode('utf-8') - - if error: - self.logger.warning(f"Error en comando '{command}': {error}") - - return output - - except Exception as e: - self.logger.error(f"Error ejecutando comando '{command}': {str(e)}") - raise - - def disconnect(self): - """Cierra la conexión SSH""" - if self.ssh_client: - self.ssh_client.close() - self.logger.info(f"Desconectado de {self.host}") - - -class CiscoMacTableParser: - """Parser para tabla MAC de dispositivos Cisco""" - - def __init__(self): - self.logger = logging.getLogger(__name__) - - def get_mac_table(self, ssh_connector: CiscoSSHConnector) -> List[MacEntry]: - """Obtiene la tabla MAC del dispositivo""" - try: - # Comando para obtener tabla MAC (compatible con IOS 12.x y 15.x) - command = "show mac address-table" - output = ssh_connector.execute_command(command) - - mac_entries = self._parse_mac_table_output(output) - self.logger.info(f"Obtenidas {len(mac_entries)} entradas de tabla MAC") - - return mac_entries - - except Exception as e: - self.logger.error(f"Error obteniendo tabla MAC: {str(e)}") - return [] - - def _parse_mac_table_output(self, output: str) -> List[MacEntry]: - """Parsea la salida del comando show mac address-table""" - mac_entries = [] - - # Patrones para diferentes formatos de salida de IOS - patterns = [ - # IOS 12.x y 15.x estándar - r'^\s*(\d+)\s+([0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4})\s+(\w+)\s+(.+)$', - # Formato alternativo con guiones - r'^\s*(\d+)\s+([0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2})\s+(\w+)\s+(.+)$', - # Formato con dos puntos - r'^\s*(\d+)\s+([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2})\s+(\w+)\s+(.+)$' - ] - - lines = output.split('\n') - - for line in lines: - line = line.strip() - if not line or line.startswith('-') or 'VLAN' in line.upper() or 'MAC Address' in line: - continue - - for pattern in patterns: - match = re.match(pattern, line) - if match: - vlan = match.group(1) - mac_address = self._normalize_mac_address(match.group(2)) - mac_type = match.group(3) - port = match.group(4).strip() - - # Filtrar MACs del CPU y otras no relevantes - if not self._is_valid_port(port): - continue - - mac_entries.append(MacEntry( - vlan=vlan, - mac_address=mac_address, - type=mac_type, - port=port - )) - break - - return mac_entries - - def _normalize_mac_address(self, mac: str) -> str: - """Normaliza formato de dirección MAC""" - # Remover todos los separadores y convertir a minúsculas - clean_mac = re.sub(r'[.:-]', '', mac.lower()) - - # Formatear como XX:XX:XX:XX:XX:XX - if len(clean_mac) == 12: - return ':'.join([clean_mac[i:i+2] for i in range(0, 12, 2)]) - - return mac - - def _is_valid_port(self, port: str) -> bool: - """Verifica si el puerto es válido para monitorización""" - # Excluir puertos del CPU, uplinks management, etc. - exclude_patterns = [ - r'cpu', - r'switch', - r'router', - r'null', - r'drop', - r'punt' - ] - - port_lower = port.lower() - for pattern in exclude_patterns: - if re.search(pattern, port_lower): - return False - - return True - - -class ZabbixLLDFormatter: - """Formateador de datos para Zabbix Low Level Discovery""" - - def __init__(self, device_hostname: str): - self.device_hostname = device_hostname - self.logger = logging.getLogger(__name__) - - def format_for_zabbix(self, mac_entries: List[MacEntry]) -> Dict[str, Any]: - """Formatea datos para Zabbix LLD""" - discovery_data = [] - timestamp = datetime.now().isoformat() - - # Agrupar MACs por puerto - ports_data = {} - - for entry in mac_entries: - port = entry.port - if port not in ports_data: - ports_data[port] = { - "port_name": port, - "device_hostname": self.device_hostname, - "mac_addresses": [], - "mac_count": 0, - "vlans": set() - } - - ports_data[port]["mac_addresses"].append({ - "mac": entry.mac_address, - "vlan": entry.vlan, - "type": entry.type - }) - ports_data[port]["vlans"].add(entry.vlan) - - # Crear entradas para LLD - for port, data in ports_data.items(): - data["mac_count"] = len(data["mac_addresses"]) - data["vlans"] = list(data["vlans"]) - - discovery_item = { - "{#DEVICE_HOSTNAME}": self.device_hostname, - "{#PORT_NAME}": port, - "{#MAC_COUNT}": data["mac_count"], - "{#VLANS}": ",".join(data["vlans"]), - "{#MAC_ADDRESSES}": json.dumps(data["mac_addresses"]), - "{#LAST_UPDATE}": timestamp - } - - discovery_data.append(discovery_item) - - result = { - "data": discovery_data, - "timestamp": timestamp, - "device_hostname": self.device_hostname, - "total_ports": len(discovery_data), - "total_macs": len(mac_entries) - } - - self.logger.info(f"Formateados datos para dispositivo {self.device_hostname}: {len(discovery_data)} puertos, {len(mac_entries)} MACs") - - return result - - -def setup_logging(debug: bool = False) -> logging.Logger: - """Configura el sistema de logging""" - level = logging.DEBUG if debug else logging.INFO - - logging.basicConfig( - level=level, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - handlers=[ - logging.StreamHandler(sys.stderr) - ] - ) - - return logging.getLogger(__name__) - - -def main(): - """Función principal""" - parser = argparse.ArgumentParser( - description='Monitor de direcciones MAC para dispositivo Cisco individual', - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" -Ejemplos de uso: - %(prog)s --host 192.168.1.10 --username admin --password cisco123 - %(prog)s -H 192.168.1.10 -u admin -p cisco123 --port 2222 - %(prog)s -H 192.168.1.10 -u admin -p cisco123 --debug - %(prog)s -H 192.168.1.10 -u admin -p cisco123 --hostname SW-CORE-001 - -Salida: - El script genera JSON en formato Zabbix LLD por stdout. - Los logs van por stderr. - """ - ) - - # Argumentos requeridos - parser.add_argument('--host', '-H', required=True, - help='Dirección IP o hostname del dispositivo Cisco') - parser.add_argument('--username', '-u', required=True, - help='Usuario para conexión SSH') - parser.add_argument('--password', '-p', required=True, - help='Contraseña para conexión SSH') - - # Argumentos opcionales - parser.add_argument('--port', '-P', type=int, default=22, - help='Puerto SSH (default: 22)') - parser.add_argument('--timeout', '-t', type=int, default=30, - help='Timeout de conexión en segundos (default: 30)') - parser.add_argument('--hostname', '-n', - help='Nombre del dispositivo para identificación (default: usa --host)') - parser.add_argument('--debug', '-d', action='store_true', - help='Habilitar modo debug') - parser.add_argument('--quiet', '-q', action='store_true', - help='Modo silencioso (solo JSON por stdout)') - - args = parser.parse_args() - - # Configurar logging - if not args.quiet: - logger = setup_logging(args.debug) - else: - # En modo silencioso, solo errores críticos - logging.basicConfig(level=logging.CRITICAL) - logger = logging.getLogger(__name__) - - # Determinar hostname del dispositivo - device_hostname = args.hostname or args.host - - # Inicializar componentes - ssh_connector = CiscoSSHConnector( - host=args.host, - username=args.username, - password=args.password, - port=args.port, - timeout=args.timeout - ) - - mac_parser = CiscoMacTableParser() - zabbix_formatter = ZabbixLLDFormatter(device_hostname) - - try: - # Conectar al dispositivo - if not ssh_connector.connect(): - logger.error("No se pudo establecer conexión SSH") - sys.exit(1) - - # Obtener tabla MAC - mac_entries = mac_parser.get_mac_table(ssh_connector) - - if not mac_entries: - logger.warning("No se obtuvieron entradas de tabla MAC") - - # Formatear para Zabbix - zabbix_data = zabbix_formatter.format_for_zabbix(mac_entries) - - # Generar salida JSON - json_output = json.dumps(zabbix_data, indent=2, ensure_ascii=False) - print(json_output) - - if not args.quiet: - logger.info("Proceso completado exitosamente") - - except Exception as e: - logger.error(f"Error durante la ejecución: {str(e)}") - sys.exit(1) - - finally: - ssh_connector.disconnect() - - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/no/debug_parser.py b/no/debug_parser.py deleted file mode 100644 index afbed5b..0000000 --- a/no/debug_parser.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 -import logging -logging.basicConfig(level=logging.DEBUG) -from cisco_mac_monitor import CiscoSSHConnector, CiscoMacTableParser - -ssh = CiscoSSHConnector('10.150.226.19', 'itops', 'Tr4!D0r3s') -parser = CiscoMacTableParser() - -print("Conectando...") -ssh.connect() -print("Ejecutando comando...") -output = ssh.execute_command('show mac address-table') -print('OUTPUT LENGTH:', len(output)) - -print("\nParseando...") -mac_entries = parser._parse_mac_table_output(output) -print(f"MAC ENTRIES FOUND: {len(mac_entries)}") - -for i, entry in enumerate(mac_entries[:3]): - print(f"Entry {i+1}: VLAN={entry.vlan}, MAC={entry.mac_address}, Type={entry.type}, Port={entry.port}") - -ssh.disconnect() -print("Desconectado.") \ No newline at end of file diff --git a/no/debug_test.py b/no/debug_test.py deleted file mode 100644 index 2848f06..0000000 --- a/no/debug_test.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python3 -import logging -logging.basicConfig(level=logging.DEBUG) -from cisco_mac_monitor import CiscoSSHConnector, CiscoMacTableParser - -ssh = CiscoSSHConnector('10.150.226.19', 'itops', 'Tr4!D0r3s') -print("Conectando...") -ssh.connect() -print("Ejecutando comando...") -output = ssh.execute_command('show mac address-table') -print('OUTPUT LENGTH:', len(output)) -print('FIRST 300 CHARS:', repr(output[:300])) -if len(output) > 300: - print('LAST 100 CHARS:', repr(output[-100:])) -ssh.disconnect() -print("Desconectado.") \ No newline at end of file diff --git a/no/docker-compose.http.yml b/no/docker-compose.http.yml deleted file mode 100644 index a5ec54e..0000000 --- a/no/docker-compose.http.yml +++ /dev/null @@ -1,51 +0,0 @@ -# Docker Compose para Cisco MAC Monitor HTTP Service -# ================================================== - -services: - cisco-mac-http: - build: - context: . - dockerfile: Dockerfile.http - platforms: - - linux/amd64 - image: cisco-mac-monitor-http:latest - container_name: cisco-mac-monitor-http - - # Puerto HTTP - ports: - - "8080:8080" - - # Red del host para acceso a dispositivos locales - network_mode: host - - # Variables de entorno - environment: - - HTTP_HOST=0.0.0.0 - - HTTP_PORT=8080 - - # Límites de recursos - deploy: - resources: - limits: - cpus: '0.5' - memory: 256M - reservations: - cpus: '0.1' - memory: 64M - - # Reinicio automático - restart: unless-stopped - - # Logging del contenedor - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" - -# Ejemplo de uso: -# docker-compose -f docker-compose.http.yml up -d -# -# Test: -# curl "http://localhost:8080/health" -# curl "http://localhost:8080/mac-monitor?host=192.168.1.10&username=admin&password=cisco123" \ No newline at end of file diff --git a/no/docker-compose.yml b/no/docker-compose.yml deleted file mode 100644 index db5c3d6..0000000 --- a/no/docker-compose.yml +++ /dev/null @@ -1,39 +0,0 @@ -# Docker Compose para Cisco Single MAC Monitor -# ============================================ - -services: - cisco-mac-monitor: - build: - context: . - dockerfile: Dockerfile - platforms: - - linux/amd64 - image: cisco-mac-monitor:latest - container_name: cisco-mac-monitor - - # Red del host para acceso a dispositivos locales - network_mode: host - - # Límites de recursos - deploy: - resources: - limits: - cpus: '0.2' - memory: 128M - reservations: - cpus: '0.05' - memory: 32M - - # Logging del contenedor - logging: - driver: "json-file" - options: - max-size: "5m" - max-file: "2" - -# Ejemplo de uso en comentarios: -# Para ejecutar el contenedor con parámetros: -# docker run --rm --network host cisco-mac-monitor \ -# --host 192.168.1.10 \ -# --username admin \ -# --password cisco123 \ No newline at end of file diff --git a/no/scripts/test_suite.sh b/no/scripts/test_suite.sh deleted file mode 100755 index f4f3b22..0000000 --- a/no/scripts/test_suite.sh +++ /dev/null @@ -1,342 +0,0 @@ -#!/bin/bash -# Script de prueba para Cisco MAC Monitor -# ====================================== - -set -e - -# Colores para output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Contadores -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Función para logging -log() { - echo -e "${BLUE}[TEST]${NC} $1" -} - -success() { - echo -e "${GREEN}[PASS]${NC} $1" - ((TESTS_PASSED++)) -} - -error() { - echo -e "${RED}[FAIL]${NC} $1" - ((TESTS_FAILED++)) -} - -warn() { - echo -e "${YELLOW}[WARN]${NC} $1" -} - -# Test 1: Verificar archivos necesarios -test_single_files() { - log "Verificando archivos necesarios..." - - local files=( - "cisco_mac_monitor.py" - "Dockerfile" - "docker-compose.yml" - "zabbix_integration.sh" - "example_output.json" - "README.md" - ) - - for file in "${files[@]}"; do - if [[ -f "$file" ]]; then - success "Archivo encontrado: $file" - else - error "Archivo faltante: $file" - fi - done -} - -# Test 2: Validar sintaxis del script principal -test_single_python_syntax() { - log "Validando sintaxis del script simplificado..." - - if command -v python3 >/dev/null 2>&1; then - if python3 -m py_compile cisco_mac_monitor.py; then - success "Sintaxis de Python válida (script simplificado)" - else - error "Error de sintaxis en script simplificado" - fi - else - warn "Python3 no disponible para test de sintaxis" - fi -} - -# Test 3: Probar ayuda del script -test_script_help() { - log "Probando función de ayuda del script..." - - if command -v python3 >/dev/null 2>&1; then - if python3 cisco_mac_monitor.py --help >/dev/null 2>&1; then - success "Función de ayuda funciona correctamente" - else - error "Error en función de ayuda" - fi - else - warn "Python3 no disponible para test de ayuda" - fi -} - -# Test 4: Validar Dockerfile simplificado -test_single_dockerfile() { - log "Validando Dockerfile simplificado..." - - if command -v docker >/dev/null 2>&1; then - if docker build -f Dockerfile -t cisco-mac-monitor-test . >/dev/null 2>&1; then - success "Dockerfile válido - imagen construida" - # Limpiar imagen de test - docker rmi cisco-mac-monitor-test >/dev/null 2>&1 || true - else - error "Error en construcción de Dockerfile" - fi - else - warn "Docker no disponible para test de Dockerfile" - fi -} - -# Test 5: Validar docker-compose simplificado -test_single_docker_compose() { - log "Validando docker-compose.single.yml..." - - if command -v docker-compose >/dev/null 2>&1; then - if docker-compose config >/dev/null 2>&1; then - success "docker-compose.yml válido" - else - error "Error en docker-compose.yml" - fi - else - warn "docker-compose no disponible para test" - fi -} - -# Test 6: Validar JSON de ejemplo simplificado -test_single_example_json() { - log "Validando JSON de ejemplo simplificado..." - - if [[ -f "example_output.json" ]]; then - if command -v python3 >/dev/null 2>&1; then - if python3 -m json.tool example_output.json >/dev/null 2>&1; then - success "JSON de ejemplo válido" - - # Verificar estructura específica de Zabbix LLD - if python3 -c " -import json -with open('example_output.json', 'r') as f: - data = json.load(f) - -# Verificar estructura básica -if 'data' not in data: - print('Missing data array') - exit(1) - -if not isinstance(data['data'], list): - print('data should be an array') - exit(1) - -# Verificar macros de Zabbix LLD en primer elemento -if len(data['data']) > 0: - item = data['data'][0] - required_macros = ['{#DEVICE_HOSTNAME}', '{#PORT_NAME}', '{#MAC_COUNT}', '{#VLANS}', '{#MAC_ADDRESSES}', '{#LAST_UPDATE}'] - for macro in required_macros: - if macro not in item: - print(f'Missing macro: {macro}') - exit(1) - -print('Zabbix LLD structure valid') -"; then - success "Estructura Zabbix LLD válida en JSON de ejemplo" - else - error "Estructura Zabbix LLD inválida en JSON de ejemplo" - fi - else - error "JSON de ejemplo inválido" - fi - else - warn "Python3 no disponible para validación de JSON" - fi - else - error "Archivo example_output.json no encontrado" - fi -} - -# Test 7: Validar script de integración con Zabbix -test_zabbix_integration_script() { - log "Validando script de integración con Zabbix..." - - if [[ -f "zabbix_integration.sh" ]]; then - if [[ -x "zabbix_integration.sh" ]]; then - success "Script de integración tiene permisos de ejecución" - - # Probar función de ayuda - if ./zabbix_integration.sh help >/dev/null 2>&1; then - success "Función de ayuda del script de integración funciona" - else - warn "Problema con función de ayuda del script de integración" - fi - else - warn "Script de integración no tiene permisos de ejecución" - chmod +x zabbix_integration.sh - success "Permisos de ejecución agregados al script de integración" - fi - else - error "Script de integración no encontrado" - fi -} - -# Test 8: Validar que la imagen simplificada es más pequeña -test_image_size_comparison() { - log "Comparando tamaño de imágenes..." - - if command -v docker >/dev/null 2>&1; then - # Construir ambas imágenes - log "Construyendo imagen simplificada..." - if docker build -f Dockerfile.single -t cisco-single-test:single . >/dev/null 2>&1; then - local single_size=$(docker images cisco-single-test:single --format "{{.Size}}") - success "Imagen simplificada construida: $single_size" - - # Limpiar imagen de test - docker rmi cisco-single-test:single >/dev/null 2>&1 || true - else - warn "No se pudo construir imagen simplificada para comparación" - fi - else - warn "Docker no disponible para comparación de tamaños" - fi -} - -# Test 9: Probar script sin parámetros (debe mostrar error útil) -test_parameter_validation() { - log "Probando validación de parámetros..." - - if command -v python3 >/dev/null 2>&1; then - # Script debe fallar sin parámetros pero mostrar mensaje útil - if ! python3 cisco_mac_monitor.py >/dev/null 2>&1; then - success "Script valida parámetros requeridos correctamente" - else - warn "Script no valida parámetros requeridos" - fi - - # Probar con parámetros incompletos - if ! python3 cisco_mac_monitor.py --host 192.168.1.10 >/dev/null 2>&1; then - success "Script valida parámetros incompletos correctamente" - else - warn "Script no valida parámetros incompletos" - fi - else - warn "Python3 no disponible para test de validación" - fi -} - -# Test 10: Verificar documentación simplificada -test_single_documentation() { - log "Verificando documentación simplificada..." - - if [[ -f "README.md" ]]; then - local readme_size=$(wc -c < README.md) - if [[ $readme_size -gt 500 ]]; then - success "README.md presente y completo ($readme_size bytes)" - else - warn "README.md muy pequeño ($readme_size bytes)" - fi - else - error "README.md faltante" - fi -} - -# Test de integración con contenedor simplificado -test_single_container_integration() { - log "Ejecutando test de integración con contenedor simplificado..." - - if command -v docker >/dev/null 2>&1; then - # Construir imagen - if docker build -f Dockerfile -t cisco-mac-test . >/dev/null 2>&1; then - success "Imagen Docker construida correctamente" - - # Test de help en contenedor - if docker run --rm cisco-mac-test --help >/dev/null 2>&1; then - success "Test de help en contenedor exitoso" - else - error "Test de help en contenedor falló" - fi - - # Limpiar - docker rmi cisco-mac-test >/dev/null 2>&1 || true - else - error "Error construyendo imagen para test de integración" - fi - else - warn "Docker no disponible para test de integración" - fi -} - -# Función principal -main() { - echo "==============================================" - echo " Cisco MAC Monitor - Test Suite" - echo "==============================================" - echo - - # Verificar que estamos en el directorio correcto - if [[ ! -f "cisco_mac_monitor.py" ]]; then - error "No se encontró cisco_mac_monitor.py. ¿Estás en el directorio correcto?" - exit 1 - fi - - # Ejecutar tests específicos para versión simplificada - test_single_files - test_single_python_syntax - test_script_help - test_parameter_validation - test_single_example_json - test_zabbix_integration_script - test_single_documentation - - # Tests que requieren herramientas externas - if command -v docker >/dev/null 2>&1; then - test_single_dockerfile - test_single_container_integration - test_image_size_comparison - else - warn "Docker no disponible - saltando tests de Docker" - fi - - if command -v docker-compose >/dev/null 2>&1; then - test_single_docker_compose - else - warn "docker-compose no disponible - saltando test" - fi - - # Resumen - echo - echo "==============================================" - echo " Resumen de Tests" - echo "==============================================" - echo -e "${GREEN}Tests exitosos: $TESTS_PASSED${NC}" - echo -e "${RED}Tests fallidos: $TESTS_FAILED${NC}" - echo - - if [[ $TESTS_FAILED -eq 0 ]]; then - echo -e "${GREEN}🎉 Todos los tests pasaron exitosamente!${NC}" - echo - echo "La versión está lista para usar:" - echo "1. Construir imagen: docker build -t cisco-mac-monitor ." - echo "2. Ejecutar: docker run --rm --network host cisco-mac-monitor --host --username --password " - echo "3. Integrar con Zabbix usando zabbix_integration.sh" - exit 0 - else - echo -e "${RED}❌ Algunos tests fallaron. Revisa los errores arriba.${NC}" - exit 1 - fi -} - -# Ejecutar tests -main "$@" \ No newline at end of file diff --git a/no/test_all_ports.py b/no/test_all_ports.py deleted file mode 100644 index 8e0ecfe..0000000 --- a/no/test_all_ports.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python3 -""" -Prueba para verificar que se muestren todos los puertos, incluidos los sin MACs conectadas. -""" - -import requests -import json -import ssl -import urllib3 - -# Deshabilitar advertencias SSL -urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - -def test_all_ports(): - """Prueba que el servicio muestre todos los puertos, no solo los con MACs""" - - # Configuración de la prueba - url = "https://localhost:8443/api/mac-table" - - payload = { - "cisco_ip": "10.150.226.19", - "ssh_username": "ruser", - "ssh_password": "pass123", - "use_legacy_ssh": True - } - - headers = { - "Content-Type": "application/json" - } - - try: - print("Realizando petición HTTPS POST al servicio...") - print(f"URL: {url}") - print(f"Payload: {json.dumps(payload, indent=2)}") - - response = requests.post( - url, - json=payload, - headers=headers, - verify=False, - timeout=30 - ) - - print(f"\nRespuesta HTTP: {response.status_code}") - print(f"Headers de respuesta: {dict(response.headers)}") - - if response.status_code == 200: - data = response.json() - print(f"\nRespuesta exitosa:") - print(json.dumps(data, indent=2)) - - # Analizar puertos - if 'data' in data: - ports_with_macs = 0 - ports_without_macs = 0 - total_ports = len(data['data']) - - for item in data['data']: - interface = item.get('{#INTERFACE}', '') - mac_count = item.get('MAC_COUNT', 0) - - if mac_count > 0: - ports_with_macs += 1 - else: - ports_without_macs += 1 - - print(f"\n=== ANÁLISIS DE PUERTOS ===") - print(f"Total de puertos detectados: {total_ports}") - print(f"Puertos con MACs conectadas: {ports_with_macs}") - print(f"Puertos sin MACs (vacíos): {ports_without_macs}") - - # Mostrar algunos ejemplos - print(f"\n=== EJEMPLOS DE PUERTOS ===") - for i, item in enumerate(data['data'][:10]): # Solo los primeros 10 - interface = item.get('{#INTERFACE}', '') - mac_count = item.get('MAC_COUNT', 0) - mac_addresses = item.get('MAC_ADDRESSES', '') - print(f"{i+1}. {interface}: {mac_count} MACs") - if mac_count > 0: - print(f" MACs: {mac_addresses[:100]}...") # Truncar para legibilidad - - # Verificar que hay puertos sin MACs - if ports_without_macs > 0: - print(f"\n✅ ÉXITO: Se detectaron {ports_without_macs} puertos vacíos (MAC_COUNT=0)") - else: - print(f"\n⚠️ ADVERTENCIA: No se detectaron puertos vacíos") - - else: - print("❌ Error: No se encontró 'data' en la respuesta") - - else: - print(f"❌ Error HTTP {response.status_code}") - print(f"Mensaje: {response.text}") - - except requests.exceptions.ConnectionError as e: - print(f"❌ Error de conexión: {e}") - print("Asegúrate de que el contenedor esté ejecutándose en puerto 8443") - except Exception as e: - print(f"❌ Error inesperado: {e}") - -if __name__ == "__main__": - test_all_ports() \ No newline at end of file diff --git a/no/test_compatibility.py b/no/test_compatibility.py deleted file mode 100644 index fa0b3de..0000000 --- a/no/test_compatibility.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/env python3 -""" -Test script para verificar compatibilidad SSH con dispositivos Cisco antiguos -=========================================================================== - -Este script valida que: -1. Las importaciones funcionen correctamente -2. La clase CiscoSSHConnector se pueda instanciar -3. Los algoritmos de compatibilidad se configuren correctamente -4. No hay errores de sintaxis en el código - -Uso: - python3 test_compatibility.py -""" - -import sys -import os - -def test_imports(): - """Test de importaciones básicas""" - print("🔍 Testing importaciones...") - - try: - import json - import logging - import argparse - import re - import sys - from datetime import datetime - from typing import List, Dict, Any - print("✅ Importaciones estándar OK") - except ImportError as e: - print(f"❌ Error en importaciones estándar: {e}") - return False - - try: - import paramiko - print("✅ Paramiko disponible") - except ImportError as e: - print(f"⚠️ Paramiko no instalado: {e}") - print(" Para testing completo, instalar con: pip install paramiko") - return "paramiko_missing" - - return True - -def test_cisco_monitor_syntax(): - """Test de sintaxis del módulo principal""" - print("\n🔍 Testing sintaxis de cisco_mac_monitor.py...") - - try: - # Compilar archivo para verificar sintaxis - with open('cisco_mac_monitor.py', 'r') as f: - source_code = f.read() - - compile(source_code, 'cisco_mac_monitor.py', 'exec') - print("✅ Sintaxis de cisco_mac_monitor.py OK") - return True - - except SyntaxError as e: - print(f"❌ Error de sintaxis: {e}") - print(f" Línea {e.lineno}: {e.text}") - return False - except FileNotFoundError: - print("❌ Archivo cisco_mac_monitor.py no encontrado") - return False - except Exception as e: - print(f"❌ Error inesperado: {e}") - return False - -def test_class_instantiation(): - """Test de instanciación de clases (si paramiko disponible)""" - print("\n🔍 Testing instanciación de clases...") - - try: - import paramiko - except ImportError: - print("⚠️ Saltando test (paramiko no disponible)") - return True - - try: - # Import del módulo principal - sys.path.insert(0, '.') - from cisco_mac_monitor import CiscoSSHConnector, CiscoMacTableParser, ZabbixLLDFormatter - - # Test CiscoSSHConnector - connector = CiscoSSHConnector("test.example.com", "test", "test") - print("✅ CiscoSSHConnector se puede instanciar") - - # Test CiscoMacTableParser - parser = CiscoMacTableParser() - print("✅ CiscoMacTableParser se puede instanciar") - - # Test ZabbixLLDFormatter - formatter = ZabbixLLDFormatter("test-device") - print("✅ ZabbixLLDFormatter se puede instanciar") - - return True - - except Exception as e: - print(f"❌ Error instanciando clases: {e}") - return False - -def test_ssh_compatibility_config(): - """Test de configuración de compatibilidad SSH""" - print("\n🔍 Testing configuración de compatibilidad SSH...") - - try: - import paramiko - except ImportError: - print("⚠️ Saltando test (paramiko no disponible)") - return True - - try: - # Test de configuración Transport - transport = paramiko.Transport(('dummy', 22)) - security_opts = transport.get_security_options() - - # Verificar algoritmos originales - original_kex = list(security_opts.kex) - original_host_keys = list(security_opts.key_types) - - # Aplicar configuración de compatibilidad - if 'diffie-hellman-group1-sha1' not in security_opts.kex: - security_opts.kex = ['diffie-hellman-group1-sha1'] + list(security_opts.kex) - print("✅ diffie-hellman-group1-sha1 añadido a KEX") - - if 'ssh-rsa' not in security_opts.key_types: - security_opts.key_types = ['ssh-rsa'] + list(security_opts.key_types) - print("✅ ssh-rsa añadido a host key types") - - # Verificar que se añadieron - assert 'diffie-hellman-group1-sha1' in security_opts.kex - assert 'ssh-rsa' in security_opts.key_types - - print("✅ Configuración de compatibilidad SSH aplicada correctamente") - transport.close() - return True - - except Exception as e: - print(f"❌ Error configurando SSH compatibility: {e}") - return False - -def main(): - """Función principal de testing""" - print("=" * 60) - print("🧪 CISCO MAC MONITOR - COMPATIBILITY TEST") - print("=" * 60) - - # Test 1: Imports - import_result = test_imports() - if import_result is False: - print("\n❌ FAILED: Importaciones básicas fallaron") - return 1 - - # Test 2: Syntax - if not test_cisco_monitor_syntax(): - print("\n❌ FAILED: Errores de sintaxis encontrados") - return 1 - - # Test 3: Class instantiation (si paramiko disponible) - if import_result is not "paramiko_missing": - if not test_class_instantiation(): - print("\n❌ FAILED: Error instanciando clases") - return 1 - - # Test 4: SSH compatibility config - if not test_ssh_compatibility_config(): - print("\n❌ FAILED: Error configurando compatibilidad SSH") - return 1 - - print("\n" + "=" * 60) - if import_result == "paramiko_missing": - print("✅ PASSED (con limitaciones): Tests básicos completados") - print("📝 Para testing completo, instalar: pip install paramiko") - else: - print("✅ PASSED: Todos los tests completados exitosamente") - print("=" * 60) - - return 0 - -if __name__ == '__main__': - exit(main()) \ No newline at end of file diff --git a/no/test_https_api.sh b/no/test_https_api.sh deleted file mode 100755 index ec5af2e..0000000 --- a/no/test_https_api.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -# Script de ejemplo para usar el nuevo servicio HTTPS con POST -# ============================================================ - -# Configuración -SERVER_URL="https://localhost:8443" -HOST="10.150.226.29" -USERNAME="itops" -PASSWORD='Tr4!D0r3s' - -echo "🔒 Cisco MAC Monitor - Ejemplo HTTPS POST" -echo "==========================================" - -# Test 1: Health check -echo "1. Testing health check..." -curl -k -s "${SERVER_URL}/health" | python3 -m json.tool - -echo -e "\n" - -# Test 2: Métricas -echo "2. Testing metrics..." -curl -k -s "${SERVER_URL}/metrics" | python3 -m json.tool - -echo -e "\n" - -# Test 3: Monitorización MAC con POST seguro -echo "3. Testing MAC monitoring (POST seguro)..." - -# Crear JSON payload -JSON_PAYLOAD=$(cat < 0: - print(f" - Primer elemento: {zabbix_data['data'][0]}") - print(f" - Claves del primer elemento: {list(zabbix_data['data'][0].keys())}") - - # Analizar resultados - ports_with_macs = 0 - ports_without_macs = 0 - - for item in zabbix_data['data']: - interface = item['{#PORT_NAME}'] - mac_count = item['{#MAC_COUNT}'] - - if mac_count > 0: - ports_with_macs += 1 - print(f" ✅ {interface}: {mac_count} MACs conectadas") - else: - ports_without_macs += 1 - print(f" ⚪ {interface}: 0 MACs (puerto vacío)") - - print(f"\n=== RESUMEN DE RESULTADOS ===") - print(f"Total de puertos: {len(zabbix_data['data'])}") - print(f"Puertos con MACs: {ports_with_macs}") - print(f"Puertos vacíos: {ports_without_macs}") - - # Mostrar algunos ejemplos del JSON Zabbix - print(f"\n=== EJEMPLO DE SALIDA ZABBIX LLD ===") - import json - print(json.dumps(zabbix_data, indent=2)[:1000] + "..." if len(str(zabbix_data)) > 1000 else json.dumps(zabbix_data, indent=2)) - - # Verificar que la funcionalidad funciona - if ports_without_macs > 0: - print(f"\n🎉 ¡ÉXITO! La funcionalidad funciona correctamente:") - print(f" - Se detectaron {ports_without_macs} puertos vacíos con MAC_COUNT=0") - print(f" - Se detectaron {ports_with_macs} puertos con MACs conectadas") - print(f" - Total de puertos mostrados: {len(zabbix_data['data'])}") - return True - else: - print(f"\n❌ Error: No se detectaron puertos vacíos") - return False - -if __name__ == "__main__": - success = test_all_interfaces_functionality() - sys.exit(0 if success else 1) \ No newline at end of file diff --git a/no/test_special_chars.sh b/no/test_special_chars.sh deleted file mode 100644 index 99664cc..0000000 --- a/no/test_special_chars.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash -# Script para probar manejo de contraseñas con caracteres especiales -# Uso: ./test_special_chars.sh - -echo "🔧 PRUEBA DE MANEJO DE CARACTERES ESPECIALES EN CONTRASEÑAS" -echo "============================================================" - -# URL del servicio -URL="https://localhost:8443/mac-monitor" -HEADERS="Content-Type: application/json" - -echo "" -echo "1. 📝 Probando contraseña con ! (sin escape)" -echo " Password: Tr4!D0r3s" -RESPONSE1=$(curl -s -k -X POST "$URL" -H "$HEADERS" -d '{"host": "10.150.226.19", "username": "itops", "password": "Tr4!D0r3s", "use_legacy_ssh": true}') -if [[ $RESPONSE1 == *"JSON inválido"* ]]; then - echo " ❌ ERROR: $RESPONSE1" -else - echo " ✅ JSON válido - Conectando..." -fi - -echo "" -echo "2. 📝 Probando contraseña con @ y # y $" -echo " Password: P@ssw0rd#123$" -RESPONSE2=$(curl -s -k -X POST "$URL" -H "$HEADERS" -d '{"host": "10.150.226.19", "username": "itops", "password": "P@ssw0rd#123$", "use_legacy_ssh": true}') -if [[ $RESPONSE2 == *"JSON inválido"* ]]; then - echo " ❌ ERROR: $RESPONSE2" -else - echo " ✅ JSON válido - Conectando..." -fi - -echo "" -echo "3. 📝 Probando contraseña con espacios" -echo " Password: 'Mi Contraseña 123!'" -RESPONSE3=$(curl -s -k -X POST "$URL" -H "$HEADERS" -d '{"host": "10.150.226.19", "username": "itops", "password": "Mi Contraseña 123!", "use_legacy_ssh": true}') -if [[ $RESPONSE3 == *"JSON inválido"* ]]; then - echo " ❌ ERROR: $RESPONSE3" -else - echo " ✅ JSON válido - Conectando..." -fi - -echo "" -echo "4. 📝 Probando contraseña con comillas (usando archivo temporal)" -echo " Password: Pass\"word" -echo '{"host": "10.150.226.19", "username": "itops", "password": "Pass\"word", "use_legacy_ssh": true}' > /tmp/test_payload.json -RESPONSE4=$(curl -s -k -X POST "$URL" -H "$HEADERS" -d @/tmp/test_payload.json) -rm -f /tmp/test_payload.json -if [[ $RESPONSE4 == *"JSON inválido"* ]]; then - echo " ❌ ERROR: $RESPONSE4" -else - echo " ✅ JSON válido - Conectando..." -fi - -echo "" -echo "5. ❌ Probando escape incorrecto de !" -echo " Password: Tr4\\!D0r3s (incorrecto)" -RESPONSE5=$(curl -s -k -X POST "$URL" -H "$HEADERS" -d '{"host": "10.150.226.19", "username": "itops", "password": "Tr4\!D0r3s", "use_legacy_ssh": true}') -if [[ $RESPONSE5 == *"JSON inválido"* ]]; then - echo " ✅ Error esperado: $RESPONSE5" -else - echo " ❌ No debería funcionar: $RESPONSE5" -fi - -echo "" -echo "📋 RESUMEN:" -echo " - Los caracteres ! @ # $ % & * + = funcionan sin escape en JSON" -echo " - Los espacios funcionan normalmente dentro de comillas" -echo " - Solo necesitan escape: \" \\ y caracteres de control \\n \\t \\r" -echo " - NO usar \\! para escapar exclamación en JSON" -echo "" -echo "✅ Pruebas completadas" \ No newline at end of file diff --git a/no/zabbix_integration.sh b/no/zabbix_integration.sh deleted file mode 100755 index bc62e98..0000000 --- a/no/zabbix_integration.sh +++ /dev/null @@ -1,157 +0,0 @@ -#!/bin/bash -# Script de integración simplificado para Zabbix External Scripts -# ============================================================== -# -# Este script debe colocarse en: -# /usr/lib/zabbix/externalscripts/cisco_single_mac_discovery.sh -# -# Y configurar permisos: -# chmod +x /usr/lib/zabbix/externalscripts/cisco_single_mac_discovery.sh -# chown zabbix:zabbix /usr/lib/zabbix/externalscripts/cisco_single_mac_discovery.sh - -# Configuración -CONTAINER_IMAGE="cisco-mac-monitor:latest" -TIMEOUT=60 - -# Función de logging para stderr -log() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" >&2 -} - -error() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1" >&2 - exit 1 -} - -# Función para ejecutar discovery en un dispositivo específico -run_single_device_discovery() { - local host="$1" - local username="$2" - local password="$3" - local hostname="$4" - - # Validar parámetros - if [[ -z "$host" ]] || [[ -z "$username" ]] || [[ -z "$password" ]]; then - error "Parámetros requeridos: host, username, password" - fi - - log "Ejecutando discovery para dispositivo: $host" - - # Construir comando Docker - local docker_cmd=( - docker run --rm --network host - "$CONTAINER_IMAGE" - --host "$host" - --username "$username" - --password "$password" - --quiet - ) - - # Agregar hostname si se proporciona - if [[ -n "$hostname" ]]; then - docker_cmd+=(--hostname "$hostname") - fi - - # Ejecutar con timeout - local json_data - if ! json_data=$(timeout "$TIMEOUT" "${docker_cmd[@]}" 2>/dev/null); then - error "Falló la ejecución de discovery para $host (timeout: ${TIMEOUT}s)" - fi - - # Validar JSON - if [[ -z "$json_data" ]]; then - error "No se obtuvieron datos del dispositivo $host" - fi - - # Validar formato JSON básico - if ! echo "$json_data" | python3 -m json.tool >/dev/null 2>&1; then - error "Datos no están en formato JSON válido para $host" - fi - - # Retornar datos - echo "$json_data" -} - -# Función para mostrar ayuda -show_help() { - cat << EOF -Cisco Single MAC Discovery - Zabbix Integration Script - -USAGE: - $0 [HOSTNAME] - -PARAMETERS: - HOST IP address or hostname of Cisco device - USERNAME SSH username - PASSWORD SSH password - HOSTNAME Optional device hostname for identification - -EXAMPLES: - # Básico - $0 192.168.1.10 admin cisco123 - - # Con hostname personalizado - $0 192.168.1.10 admin cisco123 SW-CORE-001 - - # Para usar en Zabbix Discovery Rule - cisco_mac_discovery.sh {HOST.IP} {$USER} {$PASSWORD} {HOST.NAME} - -ZABBIX CONFIGURATION: - Discovery Rule: - - Name: Cisco Device MAC Discovery - - Type: External check - - Key: cisco_mac_discovery.sh[{HOST.IP},{$CISCO_USER},{$CISCO_PASSWORD},{HOST.NAME}] - - Update interval: 5m - - Required Macros: - - {$CISCO_USER} : SSH username - - {$CISCO_PASSWORD} : SSH password - -EOF -} - -# Verificar que Docker esté disponible -check_dependencies() { - if ! command -v docker >/dev/null 2>&1; then - error "Docker no está instalado o no está en PATH" - fi - - if ! command -v python3 >/dev/null 2>&1; then - error "Python3 no está instalado o no está en PATH" - fi - - # Verificar que la imagen existe - if ! docker image inspect "$CONTAINER_IMAGE" >/dev/null 2>&1; then - error "Imagen Docker no encontrada: $CONTAINER_IMAGE" - fi -} - -# Función principal -main() { - case "${1:-}" in - "help"|"--help"|"-h"|"") - show_help - exit 0 - ;; - "test") - check_dependencies - log "Dependencias verificadas correctamente" - exit 0 - ;; - esac - - # Verificar dependencias - check_dependencies - - # Obtener parámetros - local host="$1" - local username="$2" - local password="$3" - local hostname="$4" - - # Ejecutar discovery - run_single_device_discovery "$host" "$username" "$password" "$hostname" -} - -# Ejecutar función principal con todos los argumentos -main "$@" \ No newline at end of file diff --git a/old/Dockerfile b/old/Dockerfile deleted file mode 100644 index ccee23d..0000000 --- a/old/Dockerfile +++ /dev/null @@ -1,82 +0,0 @@ -# Dockerfile para Cisco MAC Monitor -# Basado en Rocky Linux 8 con Python y dependencias SSH -FROM rockylinux:8 - -# Metadatos -LABEL maintainer="Equipo de Monitorización de Red" -LABEL description="Monitor de direcciones MAC para dispositivos Cisco - Compatible con Zabbix LLD" -LABEL version="1.0" - -# Variables de entorno -ENV PYTHONUNBUFFERED=1 -ENV PYTHONDONTWRITEBYTECODE=1 -ENV LANG=en_US.UTF-8 -ENV LC_ALL=en_US.UTF-8 - -# Actualizar sistema e instalar dependencias base -RUN dnf update -y && \ - dnf install -y \ - python3 \ - python3-pip \ - python3-devel \ - openssh-clients \ - git \ - gcc \ - openssl-devel \ - libffi-devel \ - curl \ - vim \ - procps-ng \ - iproute \ - iputils \ - net-tools && \ - dnf clean all - -# Crear usuario no privilegiado para ejecutar la aplicación -RUN useradd -m -u 1000 -s /bin/bash monitor && \ - mkdir -p /app /config /logs /data && \ - chown -R monitor:monitor /app /config /logs /data - -# Instalar dependencias Python -COPY requirements.txt /tmp/requirements.txt -RUN pip3 install --no-cache-dir --upgrade pip && \ - pip3 install --no-cache-dir -r /tmp/requirements.txt && \ - rm /tmp/requirements.txt - -# Cambiar al usuario no privilegiado -USER monitor -WORKDIR /app - -# Copiar archivos de la aplicación -COPY --chown=monitor:monitor cisco_mac_monitor.py /app/ -COPY --chown=monitor:monitor config.yaml /config/config.yaml - -# Crear directorios necesarios -RUN mkdir -p /app/scripts /app/utils - -# Script de entrada personalizable -COPY --chown=monitor:monitor entrypoint.sh /app/entrypoint.sh - -# Permisos de ejecución -USER root -RUN chmod +x /app/entrypoint.sh /app/cisco_mac_monitor.py -USER monitor - -# Volúmenes para persistencia -VOLUME ["/config", "/logs", "/data"] - -# Puertos expuestos (si fuera necesario para futuras extensiones) -EXPOSE 8080 - -# Variables de entorno por defecto -ENV CONFIG_FILE=/config/config.yaml -ENV LOG_LEVEL=INFO -ENV MONITORING_INTERVAL=5 - -# Healthcheck -HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD python3 /app/cisco_mac_monitor.py --config ${CONFIG_FILE} --single-run || exit 1 - -# Comando por defecto -ENTRYPOINT ["/app/entrypoint.sh"] -CMD ["continuous"] \ No newline at end of file diff --git a/old/README.md b/old/README.md deleted file mode 100644 index ca6e8bc..0000000 --- a/old/README.md +++ /dev/null @@ -1,453 +0,0 @@ -# Cisco MAC Monitor para Zabbix LLD - -## 📋 Descripción - -**Cisco MAC Monitor** es una solución completa para monitorizar direcciones MAC conectadas a puertos de switches Cisco. Está diseñado específicamente para integrarse con **Zabbix Low Level Discovery (LLD)** y detectar cambios en dispositivos conectados a la red. - -### Características principales: - -- ✅ **Compatibilidad total** con modelos Cisco C2960, C3560, C3560E, C2960X, C3560CX y Catalyst series -- ✅ **Conexión SSH** segura a dispositivos de red -- ✅ **Formato JSON** optimizado para Zabbix LLD -- ✅ **Monitorización continua** configurable por intervalos -- ✅ **Contenedor Docker** basado en Rocky Linux 8 -- ✅ **Detección automática** de cambios en tabla MAC -- ✅ **Filtros avanzados** para puertos y VLANs específicas -- ✅ **Logging completo** y manejo robusto de errores - ---- - -## 🚀 Inicio Rápido - -### 1. Preparar configuración - -```bash -# Clonar o descargar los archivos -mkdir cisco-mac-monitor -cd cisco-mac-monitor - -# Copiar archivos del proyecto -# - cisco_mac_monitor.py -# - config.yaml -# - Dockerfile -# - docker-compose.yml -# - requirements.txt -# - entrypoint.sh -``` - -### 2. Configurar dispositivos - -Edita el archivo `config.yaml` con tus dispositivos: - -```yaml -devices: - - hostname: "SW-PRINCIPAL-001" - ip: "192.168.1.10" - username: "admin" - password: "tu_password" - - - hostname: "SW-SECUNDARIO-001" - ip: "192.168.1.11" - username: "admin" - password: "tu_password" -``` - -### 3. Construir y ejecutar - -```bash -# Opción 1: Docker Compose (Recomendado) -docker-compose up --build -d - -# Opción 2: Docker tradicional -docker build -t cisco-mac-monitor . -docker run -v $(pwd)/config:/config -v $(pwd)/data:/data cisco-mac-monitor -``` - -### 4. Verificar funcionamiento - -```bash -# Ver logs en tiempo real -docker-compose logs -f - -# Ejecutar una prueba única -docker-compose exec cisco-mac-monitor single-run - -# Probar configuración -docker-compose exec cisco-mac-monitor test-config -``` - ---- - -## 📁 Estructura del Proyecto - -``` -cisco-mac-monitor/ -├── cisco_mac_monitor.py # Script principal de Python -├── config.yaml # Configuración de dispositivos -├── requirements.txt # Dependencias de Python -├── Dockerfile # Imagen de contenedor -├── docker-compose.yml # Orquestación de contenedores -├── entrypoint.sh # Script de entrada del contenedor -├── example_output.json # Ejemplo de salida JSON -├── modelos.csv # Inventario de dispositivos (referencia) -└── README.md # Esta documentación -``` - ---- - -## ⚙️ Configuración Detallada - -### Archivo `config.yaml` - -```yaml -# Configuración de monitorización -monitoring: - interval_minutes: 5 # Intervalo entre monitoreos - output_file: "/data/cisco_mac_data.json" # Archivo de salida - log_level: "INFO" # Nivel de logging - log_file: "/logs/cisco_mac_monitor.log" # Archivo de log - -# Dispositivos de red -devices: - - hostname: "SW-CORE-001" - ip: "192.168.1.5" - username: "admin" - password: "cisco123" - port: 22 - timeout: 30 - - - hostname: "SW-ACCESS-001" - ip: "192.168.1.10" - username: "admin" - password: "cisco123" - -# Filtros opcionales -filters: - exclude_ports: - - "Gi0/1" # Uplinks - - "Gi0/2" - exclude_vlans: - - "999" # VLAN de management - exclude_macs: - - "00:1a:2b:3c:4d:5e" # Servidores conocidos - -# Configuración para Zabbix -zabbix: - custom_macros: - discovery_rule: "cisco.mac.discovery" - item_prefix: "cisco.mac" -``` - -### Variables de entorno del contenedor - -| Variable | Default | Descripción | -|----------|---------|-------------| -| `CONFIG_FILE` | `/config/config.yaml` | Ruta al archivo de configuración | -| `LOG_LEVEL` | `INFO` | Nivel de logging (DEBUG, INFO, WARNING, ERROR) | -| `MONITORING_INTERVAL` | `5` | Intervalo en minutos (sobrescribe config.yaml) | - ---- - -## 🐳 Uso con Docker - -### Comandos básicos - -```bash -# Monitorización continua (por defecto) -docker run -v $(pwd)/config:/config cisco-mac-monitor - -# Ejecución única -docker run -v $(pwd)/config:/config cisco-mac-monitor single-run - -# Probar configuración -docker run -v $(pwd)/config:/config cisco-mac-monitor test-config - -# Probar conexiones SSH -docker run -v $(pwd)/config:/config cisco-mac-monitor test-ssh - -# Shell interactivo para debugging -docker run -it -v $(pwd)/config:/config cisco-mac-monitor shell -``` - -### Volúmenes recomendados - -```yaml -volumes: - - ./config:/config:ro # Configuración (solo lectura) - - ./logs:/logs # Logs persistentes - - ./data:/data # Datos de salida -``` - ---- - -## 📊 Integración con Zabbix - -### Formato JSON de salida - -El script genera JSON en el formato requerido por Zabbix LLD: - -```json -{ - "data": [ - { - "{#DEVICE_HOSTNAME}": "SW-PB-001", - "{#PORT_NAME}": "Fa0/1", - "{#MAC_COUNT}": 2, - "{#VLANS}": "10,20", - "{#MAC_ADDRESSES}": "[{\"mac\": \"aa:bb:cc:dd:ee:01\", \"vlan\": \"10\", \"type\": \"DYNAMIC\"}]", - "{#LAST_UPDATE}": "2024-10-24T14:30:00.123456" - } - ], - "timestamp": "2024-10-24T14:30:00.123456", - "total_devices": 3, - "total_ports": 15 -} -``` - -### Configuración en Zabbix - -1. **Crear Discovery Rule:** - - **Name:** `Cisco MAC Discovery` - - **Type:** `External check` - - **Key:** `cisco.mac.discovery` - - **Update interval:** `5m` - -2. **Item prototypes:** - - `cisco.mac.count[{#DEVICE_HOSTNAME},{#PORT_NAME}]` - - `cisco.mac.list[{#DEVICE_HOSTNAME},{#PORT_NAME}]` - - `cisco.mac.vlans[{#DEVICE_HOSTNAME},{#PORT_NAME}]` - -3. **Trigger prototypes:** - - MAC count changes - - New unauthorized MAC detected - - MAC disappeared from port - -### Script de integración para Zabbix - -```bash -#!/bin/bash -# /usr/lib/zabbix/externalscripts/cisco_mac_discovery.sh - -docker exec cisco-mac-monitor single-run > /dev/null 2>&1 -cat /data/cisco_mac_data.json -``` - ---- - -## 🔧 Compatibilidad de Dispositivos - -### Modelos soportados (basado en `modelos.csv`): - -| Modelo | IOS Version | Estado | -|--------|-------------|--------| -| C2960 | 12.2(35)SE5+ | ✅ Soportado | -| C2960X | 15.2(2)E3+ | ✅ Soportado | -| C2960S | 12.2(55)SE5+ | ✅ Soportado | -| C3560 | 12.2(25)SEB4+ | ✅ Soportado | -| C3560E | 12.2(55)SE3+ | ✅ Soportado | -| C3560CX | 15.2(4)E6+ | ✅ Soportado | -| Catalyst 3K | 16.6.5+ | ✅ Soportado | - -### Comandos utilizados: - -- `show mac address-table` - Obtener tabla MAC completa -- Compatible con IOS 12.x, 15.x y 16.x - ---- - -## 🛠️ Desarrollo y Personalización - -### Ejecutar en modo desarrollo - -```bash -# Instalar dependencias -pip3 install -r requirements.txt - -# Ejecutar directamente -python3 cisco_mac_monitor.py --config config.yaml --single-run - -# Debug mode -python3 cisco_mac_monitor.py --config config.yaml --single-run --debug -``` - -### Estructura del código - -```python -# Clases principales: -CiscoSSHConnector # Manejo de conexiones SSH -CiscoMacTableParser # Parsing de tabla MAC -ZabbixLLDFormatter # Formateo para Zabbix -CiscoMacMonitor # Orquestador principal -``` - -### Agregar nuevo modelo de dispositivo - -1. Editar `CiscoMacTableParser._parse_mac_table_output()` -2. Agregar nuevo patrón regex para el formato de salida -3. Probar con `test-ssh` command - ---- - -## 🔍 Troubleshooting - -### Problemas comunes - -**1. Error de conexión SSH:** -```bash -# Probar conectividad -docker-compose exec cisco-mac-monitor test-ssh - -# Verificar credenciales en config.yaml -# Verificar conectividad de red del contenedor -``` - -**2. Formato JSON incorrecto:** -```bash -# Verificar salida -docker-compose exec cisco-mac-monitor single-run -cat data/cisco_mac_data.json | jq . -``` - -**3. No se detectan MACs:** -```bash -# Verificar comando en dispositivo -ssh admin@192.168.1.10 -show mac address-table - -# Verificar filtros en config.yaml -``` - -**4. Container no inicia:** -```bash -# Verificar logs -docker-compose logs cisco-mac-monitor - -# Verificar configuración -docker-compose exec cisco-mac-monitor test-config -``` - -### Logs útiles - -```bash -# Logs del contenedor -docker-compose logs -f cisco-mac-monitor - -# Logs internos de la aplicación -docker-compose exec cisco-mac-monitor cat /logs/cisco_mac_monitor.log - -# Debug completo -docker-compose exec cisco-mac-monitor tail -f /logs/cisco_mac_monitor.log -``` - ---- - -## 📈 Monitorización y Alertas - -### Métricas disponibles - -- **MAC Count por puerto:** Número de MACs conectadas -- **VLAN Distribution:** Distribución de VLANs por puerto -- **MAC Changes:** Cambios en tabla MAC -- **Device Connectivity:** Estado de conexión a dispositivos - -### Alertas recomendadas - -1. **Nuevo dispositivo conectado** (MAC no autorizada) -2. **Dispositivo desconectado** (MAC desaparecida) -3. **Puerto saturado** (demasiadas MACs) -4. **Error de conectividad** con switch -5. **Cambio en VLAN** de dispositivo conocido - ---- - -## 🔒 Seguridad - -### Mejores prácticas - -1. **Credenciales:** - - Usar variables de entorno para passwords - - Configurar usuarios SSH específicos para monitorización - - Rotar credenciales regularmente - -2. **Red:** - - Usar VLAN de management dedicada - - Configurar ACLs para acceso SSH - - Monitorizar accesos SSH en logs de switches - -3. **Contenedor:** - - Ejecutar como usuario no privilegiado - - Montar volúmenes como solo lectura cuando sea posible - - Actualizar imagen base regularmente - -### Configuración SSH en switches Cisco - -```cisco -! Crear usuario para monitorización -username monitor privilege 1 password cisco123 - -! Configurar acceso SSH -line vty 0 15 - login local - transport input ssh - -! ACL para acceso (opcional) -access-list 99 permit 192.168.1.100 -line vty 0 15 - access-class 99 in -``` - ---- - -## 🤝 Contribución - -### Cómo contribuir - -1. Fork del repositorio -2. Crear branch para feature: `git checkout -b feature/nueva-funcionalidad` -3. Commit cambios: `git commit -am 'Agregar nueva funcionalidad'` -4. Push al branch: `git push origin feature/nueva-funcionalidad` -5. Crear Pull Request - -### Reportar bugs - -Incluir en el reporte: -- Versión del script -- Modelo y versión IOS del dispositivo -- Logs relevantes -- Configuración utilizada (sin credenciales) - ---- - -## 📄 Licencia - -Este proyecto está bajo licencia MIT. Ver archivo `LICENSE` para más detalles. - ---- - -## 👥 Soporte - -- **Documentación:** Este README -- **Logs:** `/logs/cisco_mac_monitor.log` -- **Ejemplos:** `example_output.json` -- **Issues:** GitHub Issues - ---- - -## 🏷️ Versiones - -### v1.0.0 (Actual) -- ✅ Soporte inicial para switches Cisco -- ✅ Integración con Zabbix LLD -- ✅ Contenedor Docker con Rocky Linux 8 -- ✅ Monitorización continua configurable -- ✅ Sistema de filtros avanzado - -### Roadmap v1.1.0 -- 🔄 Soporte para SNMPv3 -- 🔄 API REST para consultas -- 🔄 Dashboard web integrado -- 🔄 Soporte para múltiples vendors (HP, Juniper) - ---- - -**Desarrollado para entornos de producción con switches Cisco** 🚀 \ No newline at end of file diff --git a/old/README_OLD.md b/old/README_OLD.md deleted file mode 100644 index d4405fd..0000000 --- a/old/README_OLD.md +++ /dev/null @@ -1,41 +0,0 @@ -# Archivos de la Versión Completa (OLD) - -Esta carpeta contiene todos los archivos de la **versión completa original** del Cisco MAC Monitor, que manejaba múltiples dispositivos simultáneamente con configuración YAML. - -## ⚠️ Versión Obsoleta - -Estos archivos corresponden al prompt original que requería: -- Configuración YAML para múltiples dispositivos -- Sistema de configuración temporal -- Monitorización continua en un solo contenedor -- JSON grande con todos los dispositivos - -## 📁 Archivos contenidos: - -- **`cisco_mac_monitor.py`** - Script principal de la versión completa -- **`config.yaml`** - Archivo de configuración YAML con múltiples dispositivos -- **`Dockerfile`** - Imagen Docker completa con entrypoint -- **`docker-compose.yml`** - Compose para la versión completa -- **`entrypoint.sh`** - Script de entrada con múltiples modos -- **`example_output.json`** - Ejemplo de JSON con múltiples dispositivos -- **`README.md`** - Documentación completa de la versión original -- **`test_suite.sh`** - Suite de pruebas para la versión completa -- **`zabbix_integration.sh`** - Integración Zabbix para versión completa -- **`zabbix_template_examples.md`** - Plantillas detalladas de Zabbix - -## 🔄 Versión Actual - -La versión actual (en el directorio padre) es la **versión simplificada** que cumple con el prompt actualizado: -- Un dispositivo por ejecución -- Parámetros por línea de comandos -- JSON pequeño y manejable -- Contenedor ligero - -## 🗂️ Si necesitas la versión completa - -Puedes recuperar cualquier archivo de esta carpeta si en el futuro necesitas funcionalidades de la versión completa. - ---- - -**Fecha de archivado:** 24 de octubre de 2024 -**Motivo:** Cambio de requerimientos del prompt - Simplificación a un dispositivo por ejecución \ No newline at end of file diff --git a/old/cisco_mac_monitor.py b/old/cisco_mac_monitor.py deleted file mode 100755 index 1c74e61..0000000 --- a/old/cisco_mac_monitor.py +++ /dev/null @@ -1,423 +0,0 @@ -#!/usr/bin/env python3 -""" -Cisco MAC Address Monitor for Zabbix LLD -======================================== - -Script para monitorizar direcciones MAC conectadas a puertos de switches Cisco. -Compatible con modelos C2960, C3560, C3560E, C2960X, C3560CX y Catalyst series. -Genera salida JSON compatible con Zabbix Low Level Discovery. - -Autor: Sistema de Monitorización de Red -Fecha: Octubre 2024 -""" - -import json -import logging -import time -import yaml -import paramiko -import re -import sys -from datetime import datetime -from typing import Dict, List, Any, Optional -from dataclasses import dataclass - - -@dataclass -class DeviceConfig: - """Configuración de dispositivo de red""" - hostname: str - ip: str - username: str - password: str - port: int = 22 - device_type: str = "cisco_ios" - timeout: int = 30 - - -@dataclass -class MacEntry: - """Entrada de tabla MAC""" - vlan: str - mac_address: str - type: str - port: str - - -class CiscoSSHConnector: - """Conector SSH para dispositivos Cisco""" - - def __init__(self, device_config: DeviceConfig): - self.device_config = device_config - self.ssh_client = None - self.logger = logging.getLogger(__name__) - - def connect(self) -> bool: - """Establece conexión SSH con el dispositivo""" - try: - self.ssh_client = paramiko.SSHClient() - self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - self.logger.info(f"Conectando a {self.device_config.hostname} ({self.device_config.ip})") - - self.ssh_client.connect( - hostname=self.device_config.ip, - port=self.device_config.port, - username=self.device_config.username, - password=self.device_config.password, - timeout=self.device_config.timeout, - look_for_keys=False, - allow_agent=False - ) - - self.logger.info(f"Conexión exitosa a {self.device_config.hostname}") - return True - - except Exception as e: - self.logger.error(f"Error conectando a {self.device_config.hostname}: {str(e)}") - return False - - def execute_command(self, command: str) -> str: - """Ejecuta comando en el dispositivo""" - if not self.ssh_client: - raise Exception("No hay conexión SSH activa") - - try: - self.logger.debug(f"Ejecutando comando: {command}") - stdin, stdout, stderr = self.ssh_client.exec_command(command) - - output = stdout.read().decode('utf-8') - error = stderr.read().decode('utf-8') - - if error: - self.logger.warning(f"Error en comando '{command}': {error}") - - return output - - except Exception as e: - self.logger.error(f"Error ejecutando comando '{command}': {str(e)}") - raise - - def disconnect(self): - """Cierra la conexión SSH""" - if self.ssh_client: - self.ssh_client.close() - self.logger.info(f"Desconectado de {self.device_config.hostname}") - - -class CiscoMacTableParser: - """Parser para tabla MAC de dispositivos Cisco""" - - def __init__(self): - self.logger = logging.getLogger(__name__) - - def get_mac_table(self, ssh_connector: CiscoSSHConnector) -> List[MacEntry]: - """Obtiene la tabla MAC del dispositivo""" - try: - # Comando para obtener tabla MAC (compatible con IOS 12.x y 15.x) - command = "show mac address-table" - output = ssh_connector.execute_command(command) - - mac_entries = self._parse_mac_table_output(output) - self.logger.info(f"Obtenidas {len(mac_entries)} entradas de tabla MAC") - - return mac_entries - - except Exception as e: - self.logger.error(f"Error obteniendo tabla MAC: {str(e)}") - return [] - - def _parse_mac_table_output(self, output: str) -> List[MacEntry]: - """Parsea la salida del comando show mac address-table""" - mac_entries = [] - - # Patrones para diferentes formatos de salida de IOS - # Formato típico: VLAN MAC Address Type Ports - patterns = [ - # IOS 12.x y 15.x estándar - r'^\s*(\d+)\s+([0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4})\s+(\w+)\s+(.+)$', - # Formato alternativo con guiones - r'^\s*(\d+)\s+([0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2})\s+(\w+)\s+(.+)$', - # Formato con dos puntos - r'^\s*(\d+)\s+([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2})\s+(\w+)\s+(.+)$' - ] - - lines = output.split('\n') - - for line in lines: - line = line.strip() - if not line or line.startswith('-') or 'VLAN' in line.upper() or 'MAC Address' in line: - continue - - for pattern in patterns: - match = re.match(pattern, line) - if match: - vlan = match.group(1) - mac_address = self._normalize_mac_address(match.group(2)) - mac_type = match.group(3) - port = match.group(4).strip() - - # Filtrar MACs del CPU y otras no relevantes - if not self._is_valid_port(port): - continue - - mac_entries.append(MacEntry( - vlan=vlan, - mac_address=mac_address, - type=mac_type, - port=port - )) - break - - return mac_entries - - def _normalize_mac_address(self, mac: str) -> str: - """Normaliza formato de dirección MAC""" - # Remover todos los separadores y convertir a minúsculas - clean_mac = re.sub(r'[.:-]', '', mac.lower()) - - # Formatear como XX:XX:XX:XX:XX:XX - if len(clean_mac) == 12: - return ':'.join([clean_mac[i:i+2] for i in range(0, 12, 2)]) - - return mac - - def _is_valid_port(self, port: str) -> bool: - """Verifica si el puerto es válido para monitorización""" - # Excluir puertos del CPU, uplinks management, etc. - exclude_patterns = [ - r'cpu', - r'switch', - r'router', - r'null', - r'drop', - r'punt' - ] - - port_lower = port.lower() - for pattern in exclude_patterns: - if re.search(pattern, port_lower): - return False - - return True - - -class ZabbixLLDFormatter: - """Formateador de datos para Zabbix Low Level Discovery""" - - def __init__(self): - self.logger = logging.getLogger(__name__) - - def format_for_zabbix(self, device_data: Dict[str, List[MacEntry]]) -> Dict[str, Any]: - """Formatea datos para Zabbix LLD""" - discovery_data = [] - - timestamp = datetime.now().isoformat() - - for hostname, mac_entries in device_data.items(): - # Agrupar MACs por puerto - ports_data = {} - - for entry in mac_entries: - port = entry.port - if port not in ports_data: - ports_data[port] = { - "port_name": port, - "device_hostname": hostname, - "mac_addresses": [], - "mac_count": 0, - "vlans": set() - } - - ports_data[port]["mac_addresses"].append({ - "mac": entry.mac_address, - "vlan": entry.vlan, - "type": entry.type - }) - ports_data[port]["vlans"].add(entry.vlan) - - # Crear entradas para LLD - for port, data in ports_data.items(): - data["mac_count"] = len(data["mac_addresses"]) - data["vlans"] = list(data["vlans"]) - - discovery_item = { - "{#DEVICE_HOSTNAME}": hostname, - "{#PORT_NAME}": port, - "{#MAC_COUNT}": data["mac_count"], - "{#VLANS}": ",".join(data["vlans"]), - "{#MAC_ADDRESSES}": json.dumps(data["mac_addresses"]), - "{#LAST_UPDATE}": timestamp - } - - discovery_data.append(discovery_item) - - result = { - "data": discovery_data, - "timestamp": timestamp, - "total_devices": len(device_data), - "total_ports": len(discovery_data) - } - - self.logger.info(f"Formateados datos para {len(device_data)} dispositivos, {len(discovery_data)} puertos") - - return result - - -class CiscoMacMonitor: - """Monitor principal para dispositivos Cisco""" - - def __init__(self, config_file: str = "config.yaml"): - self.config_file = config_file - self.config = None - self.devices = [] - self.logger = self._setup_logging() - - self.mac_parser = CiscoMacTableParser() - self.zabbix_formatter = ZabbixLLDFormatter() - - def _setup_logging(self) -> logging.Logger: - """Configura el sistema de logging""" - logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - handlers=[ - logging.FileHandler('/tmp/cisco_mac_monitor.log'), - logging.StreamHandler(sys.stdout) - ] - ) - return logging.getLogger(__name__) - - def load_config(self) -> bool: - """Carga configuración desde archivo YAML""" - try: - with open(self.config_file, 'r', encoding='utf-8') as f: - self.config = yaml.safe_load(f) - - # Validar configuración - if 'devices' not in self.config: - raise ValueError("No se encontró sección 'devices' en configuración") - - if 'monitoring' not in self.config: - raise ValueError("No se encontró sección 'monitoring' en configuración") - - # Crear objetos DeviceConfig - self.devices = [] - for device_data in self.config['devices']: - device = DeviceConfig(**device_data) - self.devices.append(device) - - self.logger.info(f"Configuración cargada: {len(self.devices)} dispositivos") - return True - - except Exception as e: - self.logger.error(f"Error cargando configuración: {str(e)}") - return False - - def monitor_single_device(self, device: DeviceConfig) -> List[MacEntry]: - """Monitoriza un dispositivo individual""" - ssh_connector = CiscoSSHConnector(device) - mac_entries = [] - - try: - if ssh_connector.connect(): - mac_entries = self.mac_parser.get_mac_table(ssh_connector) - - except Exception as e: - self.logger.error(f"Error monitorizando {device.hostname}: {str(e)}") - - finally: - ssh_connector.disconnect() - - return mac_entries - - def monitor_all_devices(self) -> Dict[str, List[MacEntry]]: - """Monitoriza todos los dispositivos configurados""" - all_data = {} - - for device in self.devices: - self.logger.info(f"Monitorizando dispositivo: {device.hostname}") - mac_entries = self.monitor_single_device(device) - all_data[device.hostname] = mac_entries - - return all_data - - def run_monitoring_cycle(self) -> Dict[str, Any]: - """Ejecuta un ciclo completo de monitorización""" - self.logger.info("Iniciando ciclo de monitorización") - - # Monitorizar todos los dispositivos - device_data = self.monitor_all_devices() - - # Formatear para Zabbix - zabbix_data = self.zabbix_formatter.format_for_zabbix(device_data) - - self.logger.info("Ciclo de monitorización completado") - - return zabbix_data - - def run_continuous_monitoring(self): - """Ejecuta monitorización continua según configuración""" - if not self.config: - self.logger.error("No hay configuración cargada") - return - - interval = self.config['monitoring'].get('interval_minutes', 5) - output_file = self.config['monitoring'].get('output_file', '/tmp/cisco_mac_data.json') - - self.logger.info(f"Iniciando monitorización continua cada {interval} minutos") - - while True: - try: - # Ejecutar ciclo de monitorización - zabbix_data = self.run_monitoring_cycle() - - # Guardar datos - with open(output_file, 'w', encoding='utf-8') as f: - json.dump(zabbix_data, f, indent=2, ensure_ascii=False) - - self.logger.info(f"Datos guardados en: {output_file}") - - # Esperar hasta el siguiente ciclo - time.sleep(interval * 60) - - except KeyboardInterrupt: - self.logger.info("Monitorización detenida por usuario") - break - except Exception as e: - self.logger.error(f"Error en ciclo de monitorización: {str(e)}") - time.sleep(60) # Esperar 1 minuto antes de reintentar - - -def main(): - """Función principal""" - import argparse - - parser = argparse.ArgumentParser(description='Monitor de direcciones MAC para dispositivos Cisco') - parser.add_argument('--config', '-c', default='config.yaml', help='Archivo de configuración') - parser.add_argument('--single-run', '-s', action='store_true', help='Ejecutar una sola vez') - parser.add_argument('--output', '-o', help='Archivo de salida para modo single-run') - - args = parser.parse_args() - - monitor = CiscoMacMonitor(args.config) - - if not monitor.load_config(): - sys.exit(1) - - if args.single_run: - # Ejecución única - zabbix_data = monitor.run_monitoring_cycle() - - output_file = args.output or '/tmp/cisco_mac_data.json' - with open(output_file, 'w', encoding='utf-8') as f: - json.dump(zabbix_data, f, indent=2, ensure_ascii=False) - - print(f"Datos guardados en: {output_file}") - else: - # Monitorización continua - monitor.run_continuous_monitoring() - - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/old/config.yaml b/old/config.yaml deleted file mode 100644 index ceac763..0000000 --- a/old/config.yaml +++ /dev/null @@ -1,93 +0,0 @@ -# Configuración para Cisco MAC Monitor -# =================================== - -# Configuración de monitorización -monitoring: - # Intervalo de monitorización en minutos - interval_minutes: 5 - - # Archivo donde se guardarán los datos JSON para Zabbix - output_file: "/tmp/cisco_mac_data.json" - - # Nivel de logging (DEBUG, INFO, WARNING, ERROR) - log_level: "INFO" - - # Archivo de log - log_file: "/tmp/cisco_mac_monitor.log" - -# Configuración de dispositivos de red -devices: - # Switch Cisco Catalyst 2960 - Planta Baja - - hostname: "SW-PB-001" - ip: "192.168.1.10" - username: "admin" - password: "cisco123" - port: 22 - device_type: "cisco_ios" - timeout: 30 - - # Switch Cisco Catalyst 2960 - Primera Planta - - hostname: "SW-P1-001" - ip: "192.168.1.11" - username: "admin" - password: "cisco123" - port: 22 - device_type: "cisco_ios" - timeout: 30 - - # Switch Cisco Catalyst 3560 - Core - - hostname: "SW-CORE-001" - ip: "192.168.1.5" - username: "admin" - password: "cisco123" - port: 22 - device_type: "cisco_ios" - timeout: 30 - - # Switch Cisco Catalyst 2960X - DMZ - - hostname: "SW-DMZ-001" - ip: "192.168.100.10" - username: "admin" - password: "cisco123" - port: 22 - device_type: "cisco_ios" - timeout: 30 - -# Configuración de filtros (opcional) -filters: - # Excluir puertos específicos del monitoreo - exclude_ports: - - "Gi0/1" # Uplink al core - - "Gi0/2" # Uplink redundante - - # Excluir VLANs específicas - exclude_vlans: - - "999" # VLAN de management - - # Excluir MACs específicas (ejemplo: servidores conocidos) - exclude_macs: - - "00:1a:2b:3c:4d:5e" # Servidor principal - -# Configuración específica para Zabbix -zabbix: - # Macros personalizadas para LLD - custom_macros: - discovery_rule: "cisco.mac.discovery" - item_prefix: "cisco.mac" - - # Configuración de metadatos adicionales - metadata: - include_device_info: true - include_vlan_info: true - include_port_status: true - -# Configuración de alertas (para futuro uso) -alerts: - # Habilitar detección de cambios en MACs - detect_mac_changes: true - - # Habilitar detección de MACs no autorizadas - detect_unauthorized_macs: true - - # Archivo con MACs autorizadas (opcional) - authorized_macs_file: "/config/authorized_macs.txt" \ No newline at end of file diff --git a/old/docker-compose.yml b/old/docker-compose.yml deleted file mode 100644 index 8ceede2..0000000 --- a/old/docker-compose.yml +++ /dev/null @@ -1,79 +0,0 @@ -# Docker Compose para Cisco MAC Monitor -# ==================================== - -version: '3.8' - -services: - cisco-mac-monitor: - build: - context: . - dockerfile: Dockerfile - image: cisco-mac-monitor:latest - container_name: cisco-mac-monitor - - # Reiniciar automáticamente - restart: unless-stopped - - # Variables de entorno - environment: - - CONFIG_FILE=/config/config.yaml - - LOG_LEVEL=INFO - - MONITORING_INTERVAL=5 - - TZ=Europe/Madrid - - # Volúmenes para persistencia - volumes: - # Configuración (REQUERIDO) - - ./config:/config:ro - - # Logs (opcional) - - ./logs:/logs - - # Datos de salida (opcional) - - ./data:/data - - # Zona horaria del host - - /etc/localtime:/etc/localtime:ro - - # Red del host para acceso a dispositivos locales - network_mode: host - - # Límites de recursos - deploy: - resources: - limits: - cpus: '0.5' - memory: 256M - reservations: - cpus: '0.1' - memory: 64M - - # Healthcheck - healthcheck: - test: ["CMD", "python3", "/app/cisco_mac_monitor.py", "--config", "/config/config.yaml", "--single-run"] - interval: 5m - timeout: 30s - retries: 3 - start_period: 30s - - # Logging del contenedor - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" - -# Redes (opcional si no usas host) -networks: - cisco-network: - driver: bridge - ipam: - config: - - subnet: 172.20.0.0/16 - -# Volúmenes con nombre (alternativo) -volumes: - cisco-logs: - driver: local - cisco-data: - driver: local \ No newline at end of file diff --git a/old/entrypoint.sh b/old/entrypoint.sh deleted file mode 100755 index b6a6b96..0000000 --- a/old/entrypoint.sh +++ /dev/null @@ -1,302 +0,0 @@ -#!/bin/bash -# Entrypoint script para Cisco MAC Monitor -# ======================================== - -set -e - -# Colores para output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Función para logging -log() { - echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" -} - -error() { - echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1" >&2 -} - -warn() { - echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING:${NC} $1" -} - -success() { - echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] SUCCESS:${NC} $1" -} - -# Verificar archivo de configuración -check_config() { - if [[ ! -f "${CONFIG_FILE}" ]]; then - error "Archivo de configuración no encontrado: ${CONFIG_FILE}" - error "Asegúrese de montar el volumen /config con config.yaml" - exit 1 - fi - - log "Usando archivo de configuración: ${CONFIG_FILE}" -} - -# Crear directorios necesarios -setup_directories() { - log "Configurando directorios..." - - # Crear directorios si no existen - mkdir -p /logs /data /tmp - - # Verificar permisos - if [[ ! -w /logs ]]; then - warn "Sin permisos de escritura en /logs, usando /tmp" - export LOG_DIR=/tmp - else - export LOG_DIR=/logs - fi - - if [[ ! -w /data ]]; then - warn "Sin permisos de escritura en /data, usando /tmp" - export DATA_DIR=/tmp - else - export DATA_DIR=/data - fi - - success "Directorios configurados correctamente" -} - -# Validar conectividad de red -test_network() { - log "Verificando conectividad de red..." - - # Verificar si podemos resolver DNS - if ! nslookup google.com > /dev/null 2>&1; then - warn "No se puede resolver DNS externo, verificando conectividad local..." - fi - - # Extraer IPs de dispositivos del config para test básico - if command -v python3 > /dev/null 2>&1 && [[ -f "${CONFIG_FILE}" ]]; then - python3 -c " -import yaml -try: - with open('${CONFIG_FILE}', 'r') as f: - config = yaml.safe_load(f) - for device in config.get('devices', []): - ip = device.get('ip') - if ip: - print(f'Testing connectivity to {ip}...') - import subprocess - result = subprocess.run(['ping', '-c', '1', '-W', '3', ip], - capture_output=True, timeout=5) - if result.returncode == 0: - print(f'✓ {ip} reachable') - else: - print(f'✗ {ip} not reachable') -except Exception as e: - print(f'Error testing network connectivity: {e}') -" - fi -} - -# Función para mostrar ayuda -show_help() { - cat << EOF -Cisco MAC Monitor - Contenedor Docker - -USAGE: - docker run cisco-mac-monitor [COMMAND] [OPTIONS] - -COMMANDS: - continuous Ejecutar monitorización continua (por defecto) - single-run Ejecutar una sola vez y salir - test-config Validar archivo de configuración - test-ssh Probar conexiones SSH a dispositivos - shell Abrir shell interactivo - help Mostrar esta ayuda - -VARIABLES DE ENTORNO: - CONFIG_FILE Ruta al archivo de configuración (default: /config/config.yaml) - LOG_LEVEL Nivel de logging (default: INFO) - MONITORING_INTERVAL Intervalo de monitorización en minutos (default: 5) - -VOLÚMENES: - /config Directorio de configuración (contiene config.yaml) - /logs Directorio de logs - /data Directorio de datos de salida - -EJEMPLOS: - # Ejecutar monitorización continua - docker run -v /host/config:/config cisco-mac-monitor - - # Ejecutar una sola vez - docker run -v /host/config:/config cisco-mac-monitor single-run - - # Probar configuración - docker run -v /host/config:/config cisco-mac-monitor test-config - -EOF -} - -# Función para probar configuración -test_config() { - log "Validando configuración..." - - if ! python3 -c " -import yaml -import sys - -try: - with open('${CONFIG_FILE}', 'r') as f: - config = yaml.safe_load(f) - - # Validar estructura básica - if 'devices' not in config: - print('ERROR: Sección devices no encontrada') - sys.exit(1) - - if 'monitoring' not in config: - print('ERROR: Sección monitoring no encontrada') - sys.exit(1) - - # Validar dispositivos - devices = config['devices'] - if not devices: - print('ERROR: No hay dispositivos configurados') - sys.exit(1) - - print(f'✓ Configuración válida: {len(devices)} dispositivos') - - for i, device in enumerate(devices): - hostname = device.get('hostname', f'device-{i}') - ip = device.get('ip') - username = device.get('username') - - if not ip: - print(f'ERROR: Device {hostname} sin IP') - sys.exit(1) - if not username: - print(f'ERROR: Device {hostname} sin username') - sys.exit(1) - - print(f' - {hostname} ({ip})') - -except Exception as e: - print(f'ERROR: {e}') - sys.exit(1) -"; then - error "Configuración inválida" - exit 1 - fi - - success "Configuración válida" -} - -# Función para probar conexiones SSH -test_ssh() { - log "Probando conexiones SSH a dispositivos..." - - python3 << EOF -import yaml -import paramiko -import sys - -try: - with open('${CONFIG_FILE}', 'r') as f: - config = yaml.safe_load(f) - - devices = config.get('devices', []) - success_count = 0 - - for device in devices: - hostname = device.get('hostname', 'unknown') - ip = device.get('ip') - username = device.get('username') - password = device.get('password') - port = device.get('port', 22) - - print(f'Probando {hostname} ({ip})...', end=' ') - - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh.connect( - hostname=ip, - port=port, - username=username, - password=password, - timeout=10, - look_for_keys=False, - allow_agent=False - ) - ssh.close() - print('✓ CONECTADO') - success_count += 1 - except Exception as e: - print(f'✗ ERROR: {e}') - - print(f'\\nResultado: {success_count}/{len(devices)} dispositivos conectados') - - if success_count == 0: - sys.exit(1) - -except Exception as e: - print(f'ERROR: {e}') - sys.exit(1) -EOF -} - -# Función principal -main() { - log "Iniciando Cisco MAC Monitor..." - - # Configurar directorios - setup_directories - - # Verificar configuración - check_config - - # Procesar comando - COMMAND=${1:-continuous} - - case "${COMMAND}" in - "continuous") - log "Modo: Monitorización continua" - test_config - warn "Iniciando monitorización continua - Presiona Ctrl+C para detener" - exec python3 /app/cisco_mac_monitor.py --config "${CONFIG_FILE}" - ;; - "single-run") - log "Modo: Ejecución única" - test_config - exec python3 /app/cisco_mac_monitor.py --config "${CONFIG_FILE}" --single-run --output "${DATA_DIR}/cisco_mac_data.json" - ;; - "test-config") - test_config - success "Configuración OK" - ;; - "test-ssh") - test_config - test_ssh - ;; - "test-network") - test_network - ;; - "shell") - log "Abriendo shell interactivo..." - exec /bin/bash - ;; - "help"|"--help"|"-h") - show_help - ;; - *) - error "Comando desconocido: ${COMMAND}" - show_help - exit 1 - ;; - esac -} - -# Manejo de señales -trap 'log "Recibida señal de terminación, cerrando..."; exit 0' SIGTERM SIGINT - -# Ejecutar función principal -main "$@" \ No newline at end of file diff --git a/old/example_output.json b/old/example_output.json deleted file mode 100644 index 488be4b..0000000 --- a/old/example_output.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "data": [ - { - "{#DEVICE_HOSTNAME}": "SW-PB-001", - "{#PORT_NAME}": "Fa0/1", - "{#MAC_COUNT}": 2, - "{#VLANS}": "10,20", - "{#MAC_ADDRESSES}": "[{\"mac\": \"aa:bb:cc:dd:ee:01\", \"vlan\": \"10\", \"type\": \"DYNAMIC\"}, {\"mac\": \"aa:bb:cc:dd:ee:02\", \"vlan\": \"20\", \"type\": \"DYNAMIC\"}]", - "{#LAST_UPDATE}": "2024-10-24T14:30:00.123456" - }, - { - "{#DEVICE_HOSTNAME}": "SW-PB-001", - "{#PORT_NAME}": "Fa0/2", - "{#MAC_COUNT}": 1, - "{#VLANS}": "10", - "{#MAC_ADDRESSES}": "[{\"mac\": \"bb:cc:dd:ee:ff:01\", \"vlan\": \"10\", \"type\": \"DYNAMIC\"}]", - "{#LAST_UPDATE}": "2024-10-24T14:30:00.123456" - }, - { - "{#DEVICE_HOSTNAME}": "SW-P1-001", - "{#PORT_NAME}": "Gi0/1", - "{#MAC_COUNT}": 3, - "{#VLANS}": "10,30", - "{#MAC_ADDRESSES}": "[{\"mac\": \"cc:dd:ee:ff:00:01\", \"vlan\": \"10\", \"type\": \"DYNAMIC\"}, {\"mac\": \"cc:dd:ee:ff:00:02\", \"vlan\": \"10\", \"type\": \"DYNAMIC\"}, {\"mac\": \"cc:dd:ee:ff:00:03\", \"vlan\": \"30\", \"type\": \"DYNAMIC\"}]", - "{#LAST_UPDATE}": "2024-10-24T14:30:00.123456" - }, - { - "{#DEVICE_HOSTNAME}": "SW-CORE-001", - "{#PORT_NAME}": "Gi0/1", - "{#MAC_COUNT}": 5, - "{#VLANS}": "10,20,30", - "{#MAC_ADDRESSES}": "[{\"mac\": \"dd:ee:ff:00:11:01\", \"vlan\": \"10\", \"type\": \"DYNAMIC\"}, {\"mac\": \"dd:ee:ff:00:11:02\", \"vlan\": \"20\", \"type\": \"DYNAMIC\"}, {\"mac\": \"dd:ee:ff:00:11:03\", \"vlan\": \"30\", \"type\": \"DYNAMIC\"}, {\"mac\": \"dd:ee:ff:00:11:04\", \"vlan\": \"10\", \"type\": \"DYNAMIC\"}, {\"mac\": \"dd:ee:ff:00:11:05\", \"vlan\": \"20\", \"type\": \"DYNAMIC\"}]", - "{#LAST_UPDATE}": "2024-10-24T14:30:00.123456" - } - ], - "timestamp": "2024-10-24T14:30:00.123456", - "total_devices": 3, - "total_ports": 4 -} \ No newline at end of file diff --git a/old/test_suite.sh b/old/test_suite.sh deleted file mode 100755 index 4605911..0000000 --- a/old/test_suite.sh +++ /dev/null @@ -1,348 +0,0 @@ -#!/bin/bash -# Script de prueba para Cisco MAC Monitor -# ====================================== - -set -e - -# Colores para output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Contadores -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Función para logging -log() { - echo -e "${BLUE}[TEST]${NC} $1" -} - -success() { - echo -e "${GREEN}[PASS]${NC} $1" - ((TESTS_PASSED++)) -} - -error() { - echo -e "${RED}[FAIL]${NC} $1" - ((TESTS_FAILED++)) -} - -warn() { - echo -e "${YELLOW}[WARN]${NC} $1" -} - -# Test 1: Verificar archivos necesarios -test_files() { - log "Verificando archivos necesarios..." - - local files=( - "cisco_mac_monitor.py" - "config.yaml" - "requirements.txt" - "Dockerfile" - "docker-compose.yml" - "entrypoint.sh" - "README.md" - ) - - for file in "${files[@]}"; do - if [[ -f "$file" ]]; then - success "Archivo encontrado: $file" - else - error "Archivo faltante: $file" - fi - done -} - -# Test 2: Validar sintaxis Python -test_python_syntax() { - log "Validando sintaxis de Python..." - - if command -v python3 >/dev/null 2>&1; then - if python3 -m py_compile cisco_mac_monitor.py; then - success "Sintaxis de Python válida" - else - error "Error de sintaxis en Python" - fi - else - warn "Python3 no disponible para test de sintaxis" - fi -} - -# Test 3: Validar archivo YAML -test_yaml_syntax() { - log "Validando sintaxis de YAML..." - - if command -v python3 >/dev/null 2>&1; then - if python3 -c " -import yaml -try: - with open('config.yaml', 'r') as f: - yaml.safe_load(f) - print('YAML syntax valid') -except Exception as e: - print(f'YAML error: {e}') - exit(1) -"; then - success "Sintaxis de YAML válida" - else - error "Error de sintaxis en YAML" - fi - else - warn "Python3 no disponible para test de YAML" - fi -} - -# Test 4: Validar Dockerfile -test_dockerfile() { - log "Validando Dockerfile..." - - if command -v docker >/dev/null 2>&1; then - if docker build -t cisco-mac-monitor-test . >/dev/null 2>&1; then - success "Dockerfile válido - imagen construida" - # Limpiar imagen de test - docker rmi cisco-mac-monitor-test >/dev/null 2>&1 || true - else - error "Error en construcción de Dockerfile" - fi - else - warn "Docker no disponible para test de Dockerfile" - fi -} - -# Test 5: Validar docker-compose -test_docker_compose() { - log "Validando docker-compose.yml..." - - if command -v docker-compose >/dev/null 2>&1; then - if docker-compose config >/dev/null 2>&1; then - success "docker-compose.yml válido" - else - error "Error en docker-compose.yml" - fi - else - warn "docker-compose no disponible para test" - fi -} - -# Test 6: Verificar dependencias Python -test_python_deps() { - log "Verificando dependencias de Python..." - - if command -v pip3 >/dev/null 2>&1; then - # Crear entorno virtual temporal - local temp_venv=$(mktemp -d) - python3 -m venv "$temp_venv" >/dev/null 2>&1 - - if source "$temp_venv/bin/activate" && pip install -r requirements.txt >/dev/null 2>&1; then - success "Dependencias de Python instalables" - else - error "Error instalando dependencias de Python" - fi - - # Limpiar entorno temporal - rm -rf "$temp_venv" - deactivate 2>/dev/null || true - else - warn "pip3 no disponible para test de dependencias" - fi -} - -# Test 7: Validar permisos de entrypoint -test_entrypoint_permissions() { - log "Verificando permisos de entrypoint.sh..." - - if [[ -x "entrypoint.sh" ]]; then - success "entrypoint.sh tiene permisos de ejecución" - else - warn "entrypoint.sh no tiene permisos de ejecución" - chmod +x entrypoint.sh - success "Permisos de ejecución agregados a entrypoint.sh" - fi -} - -# Test 8: Validar estructura de configuración -test_config_structure() { - log "Validando estructura de configuración..." - - if command -v python3 >/dev/null 2>&1; then - if python3 -c " -import yaml - -try: - with open('config.yaml', 'r') as f: - config = yaml.safe_load(f) - - # Verificar secciones requeridas - required_sections = ['devices', 'monitoring'] - for section in required_sections: - if section not in config: - print(f'Missing section: {section}') - exit(1) - - # Verificar estructura de devices - devices = config['devices'] - if not isinstance(devices, list) or len(devices) == 0: - print('Devices section must be a non-empty list') - exit(1) - - # Verificar campos requeridos en devices - required_device_fields = ['hostname', 'ip', 'username', 'password'] - for i, device in enumerate(devices): - for field in required_device_fields: - if field not in device: - print(f'Device {i} missing field: {field}') - exit(1) - - print('Configuration structure valid') - -except Exception as e: - print(f'Configuration error: {e}') - exit(1) -"; then - success "Estructura de configuración válida" - else - error "Error en estructura de configuración" - fi - else - warn "Python3 no disponible para test de configuración" - fi -} - -# Test 9: Validar JSON de ejemplo -test_example_json() { - log "Validando JSON de ejemplo..." - - if [[ -f "example_output.json" ]]; then - if command -v python3 >/dev/null 2>&1; then - if python3 -m json.tool example_output.json >/dev/null 2>&1; then - success "JSON de ejemplo válido" - else - error "JSON de ejemplo inválido" - fi - else - warn "Python3 no disponible para validación de JSON" - fi - else - warn "Archivo example_output.json no encontrado" - fi -} - -# Test 10: Verificar documentación -test_documentation() { - log "Verificando documentación..." - - if [[ -f "README.md" ]]; then - local readme_size=$(wc -c < README.md) - if [[ $readme_size -gt 1000 ]]; then - success "README.md presente y completo" - else - warn "README.md muy pequeño (${readme_size} bytes)" - fi - else - error "README.md faltante" - fi - - if [[ -f "zabbix_template_examples.md" ]]; then - success "Documentación de Zabbix presente" - else - warn "Documentación de Zabbix faltante" - fi -} - -# Test de integración básica -test_integration() { - log "Ejecutando test de integración básica..." - - if command -v docker >/dev/null 2>&1; then - # Construir imagen - if docker build -t cisco-mac-monitor-test . >/dev/null 2>&1; then - success "Imagen Docker construida correctamente" - - # Test de configuración en contenedor - if docker run --rm cisco-mac-monitor-test test-config >/dev/null 2>&1; then - success "Test de configuración en contenedor exitoso" - else - error "Test de configuración en contenedor falló" - fi - - # Limpiar - docker rmi cisco-mac-monitor-test >/dev/null 2>&1 || true - else - error "Error construyendo imagen para test de integración" - fi - else - warn "Docker no disponible para test de integración" - fi -} - -# Función principal -main() { - echo "==========================================" - echo " Cisco MAC Monitor - Test Suite" - echo "==========================================" - echo - - # Verificar que estamos en el directorio correcto - if [[ ! -f "cisco_mac_monitor.py" ]]; then - error "No se encontró cisco_mac_monitor.py. ¿Estás en el directorio correcto?" - exit 1 - fi - - # Ejecutar tests - test_files - test_python_syntax - test_yaml_syntax - test_config_structure - test_entrypoint_permissions - test_example_json - test_documentation - - # Tests que requieren herramientas externas - if command -v docker >/dev/null 2>&1; then - test_dockerfile - test_integration - else - warn "Docker no disponible - saltando tests de Docker" - fi - - if command -v docker-compose >/dev/null 2>&1; then - test_docker_compose - else - warn "docker-compose no disponible - saltando test" - fi - - if command -v pip3 >/dev/null 2>&1; then - test_python_deps - else - warn "pip3 no disponible - saltando test de dependencias" - fi - - # Resumen - echo - echo "==========================================" - echo " Resumen de Tests" - echo "==========================================" - echo -e "${GREEN}Tests exitosos: $TESTS_PASSED${NC}" - echo -e "${RED}Tests fallidos: $TESTS_FAILED${NC}" - echo - - if [[ $TESTS_FAILED -eq 0 ]]; then - echo -e "${GREEN}🎉 Todos los tests pasaron exitosamente!${NC}" - echo - echo "El proyecto está listo para usar:" - echo "1. Edita config.yaml con tus dispositivos" - echo "2. Ejecuta: docker-compose up --build -d" - echo "3. Verifica logs: docker-compose logs -f" - exit 0 - else - echo -e "${RED}❌ Algunos tests fallaron. Revisa los errores arriba.${NC}" - exit 1 - fi -} - -# Ejecutar tests -main "$@" \ No newline at end of file diff --git a/old/zabbix_integration.sh b/old/zabbix_integration.sh deleted file mode 100755 index 27a5ec7..0000000 --- a/old/zabbix_integration.sh +++ /dev/null @@ -1,132 +0,0 @@ -#!/bin/bash -# Script de integración para Zabbix External Scripts -# ================================================== -# -# Este script debe colocarse en: -# /usr/lib/zabbix/externalscripts/cisco_mac_discovery.sh -# -# Y configurar permisos: -# chmod +x /usr/lib/zabbix/externalscripts/cisco_mac_discovery.sh -# chown zabbix:zabbix /usr/lib/zabbix/externalscripts/cisco_mac_discovery.sh - -# Configuración -CONTAINER_NAME="cisco-mac-monitor" -DATA_FILE="/data/cisco_mac_data.json" -TIMEOUT=60 - -# Función de logging -log() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" >&2 -} - -error() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1" >&2 - exit 1 -} - -# Verificar que el contenedor existe y está corriendo -check_container() { - if ! docker ps --format "table {{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then - error "Contenedor ${CONTAINER_NAME} no está corriendo" - fi -} - -# Ejecutar discovery y obtener datos -run_discovery() { - log "Ejecutando discovery en contenedor ${CONTAINER_NAME}" - - # Ejecutar single-run en el contenedor - if ! timeout ${TIMEOUT} docker exec ${CONTAINER_NAME} single-run >/dev/null 2>&1; then - error "Falló la ejecución de discovery (timeout: ${TIMEOUT}s)" - fi - - # Verificar que el archivo de datos existe - if ! docker exec ${CONTAINER_NAME} test -f ${DATA_FILE}; then - error "Archivo de datos no encontrado: ${DATA_FILE}" - fi - - # Obtener y validar JSON - local json_data - json_data=$(docker exec ${CONTAINER_NAME} cat ${DATA_FILE}) - - if [[ -z "${json_data}" ]]; then - error "Archivo de datos está vacío" - fi - - # Validar formato JSON básico - if ! echo "${json_data}" | python3 -m json.tool >/dev/null 2>&1; then - error "Datos no están en formato JSON válido" - fi - - # Retornar datos - echo "${json_data}" -} - -# Función principal -main() { - case "${1:-discovery}" in - "discovery"|"") - check_container - run_discovery - ;; - "status") - if docker ps --format "table {{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then - echo "Container ${CONTAINER_NAME} is running" - exit 0 - else - echo "Container ${CONTAINER_NAME} is not running" - exit 1 - fi - ;; - "logs") - docker logs --tail 50 ${CONTAINER_NAME} - ;; - "help"|"-h"|"--help") - cat << EOF -Cisco MAC Discovery - Zabbix Integration Script - -USAGE: - $0 [COMMAND] - -COMMANDS: - discovery Ejecutar discovery y retornar JSON (default) - status Verificar estado del contenedor - logs Mostrar logs recientes del contenedor - help Mostrar esta ayuda - -EJEMPLOS: - # Usar en Zabbix Discovery Rule - cisco_mac_discovery.sh - - # Verificar estado - cisco_mac_discovery.sh status - - # Ver logs para troubleshooting - cisco_mac_discovery.sh logs - -CONFIGURACIÓN ZABBIX: - Discovery Rule: - - Name: Cisco MAC Discovery - - Type: External check - - Key: cisco_mac_discovery.sh - - Update interval: 5m - -EOF - ;; - *) - error "Comando desconocido: ${1}. Usa 'help' para ver opciones disponibles." - ;; - esac -} - -# Verificar dependencias -if ! command -v docker >/dev/null 2>&1; then - error "Docker no está instalado o no está en PATH" -fi - -if ! command -v python3 >/dev/null 2>&1; then - error "Python3 no está instalado o no está en PATH" -fi - -# Ejecutar función principal -main "$@" \ No newline at end of file diff --git a/old/zabbix_template_examples.md b/old/zabbix_template_examples.md deleted file mode 100644 index c43371f..0000000 --- a/old/zabbix_template_examples.md +++ /dev/null @@ -1,308 +0,0 @@ -# Plantilla de Zabbix para Cisco MAC Monitor -# ========================================== - -## Discovery Rule - -```xml - - Cisco MAC Discovery - EXTERNAL - cisco_mac_discovery.sh - 5m - Descubre puertos de switches Cisco con direcciones MAC conectadas - - - MAC Count on {#DEVICE_HOSTNAME} port {#PORT_NAME} - CALCULATED - cisco.mac.count[{#DEVICE_HOSTNAME},{#PORT_NAME}] - 0 - UNSIGNED - {#MAC_COUNT} - Número de direcciones MAC en el puerto - - - - MAC Addresses on {#DEVICE_HOSTNAME} port {#PORT_NAME} - CALCULATED - cisco.mac.addresses[{#DEVICE_HOSTNAME},{#PORT_NAME}] - 0 - TEXT - {#MAC_ADDRESSES} - Lista de direcciones MAC en formato JSON - - - - VLANs on {#DEVICE_HOSTNAME} port {#PORT_NAME} - CALCULATED - cisco.mac.vlans[{#DEVICE_HOSTNAME},{#PORT_NAME}] - 0 - TEXT - {#VLANS} - VLANs presentes en el puerto - - - - Last Update {#DEVICE_HOSTNAME} port {#PORT_NAME} - CALCULATED - cisco.mac.lastupdate[{#DEVICE_HOSTNAME},{#PORT_NAME}] - 0 - TEXT - {#LAST_UPDATE} - Timestamp de última actualización - - - - - - {Template Cisco MAC:cisco.mac.count[{#DEVICE_HOSTNAME},{#PORT_NAME}].change()}>0 - MAC count changed on {#DEVICE_HOSTNAME} port {#PORT_NAME} - INFO - El número de direcciones MAC en el puerto ha cambiado - - - - {Template Cisco MAC:cisco.mac.count[{#DEVICE_HOSTNAME},{#PORT_NAME}].last()}>5 - Too many MACs on {#DEVICE_HOSTNAME} port {#PORT_NAME} - WARNING - Demasiadas direcciones MAC en un solo puerto (posible loop o hub) - - - - {Template Cisco MAC:cisco.mac.addresses[{#DEVICE_HOSTNAME},{#PORT_NAME}].change()}>0 - MAC addresses changed on {#DEVICE_HOSTNAME} port {#PORT_NAME} - INFO - Las direcciones MAC en el puerto han cambiado - - - - - - MAC Count - {#DEVICE_HOSTNAME} {#PORT_NAME} - - - cisco.mac.count[{#DEVICE_HOSTNAME},{#PORT_NAME}] - - - - - -``` - -## Items Adicionales (nivel de host) - -```xml - - - Cisco MAC Monitor - Total Devices - DEPENDENT - cisco.mac.total_devices - cisco_mac_discovery.sh - - - JSONPATH - $.total_devices - - - Número total de dispositivos monitorizados - - - - Cisco MAC Monitor - Total Ports - DEPENDENT - cisco.mac.total_ports - cisco_mac_discovery.sh - - - JSONPATH - $.total_ports - - - Número total de puertos con MACs - - - - Cisco MAC Monitor - Last Timestamp - DEPENDENT - cisco.mac.timestamp - cisco_mac_discovery.sh - - - JSONPATH - $.timestamp - - - Timestamp de la última ejecución - -``` - -## Triggers de Host - -```xml - - {Template Cisco MAC:cisco.mac.timestamp.nodata(15m)}>0 - Cisco MAC Monitor - No data received - WARNING - No se han recibido datos del monitor MAC en los últimos 15 minutos - - - - {Template Cisco MAC:cisco.mac.total_devices.last()}<1 - Cisco MAC Monitor - No devices monitored - HIGH - No hay dispositivos siendo monitorizados - -``` - -## Macros de Template - -```xml - - - {$CISCO.MAC.MAX_PER_PORT} - 5 - Máximo número de MACs permitidas por puerto - - - - {$CISCO.MAC.DATA_TIMEOUT} - 15m - Timeout para considerar datos obsoletos - - - - {$CISCO.MAC.DISCOVERY_INTERVAL} - 5m - Intervalo de discovery - - -``` - -## Scripts de Actions - -### Script para notificaciones con detalles de MACs - -```bash -#!/bin/bash -# Script para enviar detalles de cambios MAC - -DEVICE="{ALERT.SUBJECT}" -PORT="{ITEM.KEY1}" -MAC_DATA="{ITEM.VALUE}" - -# Formatear mensaje -MESSAGE=" -Cambio detectado en tabla MAC: -Dispositivo: ${DEVICE} -Puerto: ${PORT} -Timestamp: $(date) - -Direcciones MAC actuales: -${MAC_DATA} -" - -# Enviar notificación (ejemplo con email) -echo "${MESSAGE}" | mail -s "Alerta MAC - ${DEVICE}" admin@empresa.com -``` - -### Script para análisis de MACs no autorizadas - -```python -#!/usr/bin/env python3 -import json -import sys - -# Cargar lista de MACs autorizadas -authorized_macs = { - "aa:bb:cc:dd:ee:01": "Servidor Principal", - "aa:bb:cc:dd:ee:02": "Workstation Admin", - # ... más MACs autorizadas -} - -# Obtener datos del item -mac_data = sys.argv[1] if len(sys.argv) > 1 else "{}" - -try: - macs = json.loads(mac_data) - unauthorized = [] - - for mac_entry in macs: - mac = mac_entry.get('mac') - if mac and mac not in authorized_macs: - unauthorized.append({ - 'mac': mac, - 'vlan': mac_entry.get('vlan'), - 'type': mac_entry.get('type') - }) - - if unauthorized: - print(f"MACs no autorizadas detectadas: {len(unauthorized)}") - for mac in unauthorized: - print(f" - {mac['mac']} (VLAN {mac['vlan']})") - else: - print("Todas las MACs están autorizadas") - -except Exception as e: - print(f"Error procesando datos MAC: {e}") -``` - -## Configuración de Web Scenarios - -```xml - - Cisco MAC Monitor Container Health - 5m - - - Check Container Status - http://localhost:8080/health - 200 - OK - - - -``` - -## Dashboard Widget - -```json -{ - "type": "graph", - "name": "Cisco MAC Activity", - "fields": { - "source_type": "1", - "itemid": { - "cisco.mac.total_ports": "Total Ports with MACs" - } - } -} -``` - -## Media Types para Alertas - -### Webhook para Slack - -```json -{ - "name": "Cisco MAC Slack Alert", - "type": "webhook", - "parameters": { - "url": "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK", - "channel": "#network-alerts", - "message": "🚨 Cisco MAC Alert\nDevice: {ALERT.SUBJECT}\nMessage: {ALERT.MESSAGE}\nTime: {DATE} {TIME}" - } -} -``` - -### Webhook para Teams - -```json -{ - "name": "Cisco MAC Teams Alert", - "type": "webhook", - "parameters": { - "url": "https://outlook.office.com/webhook/YOUR/TEAMS/WEBHOOK", - "title": "Cisco MAC Monitor Alert", - "text": "Device: {ALERT.SUBJECT}\\nStatus: {TRIGGER.STATUS}\\nMessage: {ALERT.MESSAGE}" - } -} -``` \ No newline at end of file diff --git a/run.sh b/run.sh old mode 100644 new mode 100755 diff --git a/test_new_format.py b/test_new_format.py old mode 100644 new mode 100755