mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-01 10:22:08 +00:00
304 lines
19 KiB
JSON
304 lines
19 KiB
JSON
{
|
|
"meta": {
|
|
"title": "Arquitectura de ProxMenux Monitor — AppImage, Flask, SQLite, WebSocket | ProxMenux",
|
|
"description": "Cómo se construye ProxMenux Monitor: estructura AppImage, blueprints Flask, tareas de fondo, datos de psutil, pvesh, smartctl y journalctl, almacenamiento SQLite, terminal WebSocket, proveedores de IA, canales de aviso, proxy inverso y Fail2Ban opcional.",
|
|
"ogTitle": "Arquitectura de ProxMenux Monitor",
|
|
"ogDescription": "Dentro de ProxMenux Monitor: AppImage, blueprints Flask, tareas de fondo, SQLite, WebSocket, proveedores de IA y canales de aviso.",
|
|
"twitterTitle": "Arquitectura de ProxMenux Monitor",
|
|
"twitterDescription": "AppImage, Flask, SQLite, WebSocket, proveedores de IA y canales de notificación — por dentro del Monitor."
|
|
},
|
|
"header": {
|
|
"title": "Arquitectura",
|
|
"description": "Cómo se distribuye ProxMenux Monitor, qué contiene el AppImage y cómo las peticiones del navegador pasan por Flask hasta las herramientas del host y el almacenamiento SQLite.",
|
|
"section": "ProxMenux Monitor"
|
|
},
|
|
"intro": {
|
|
"title": "Un proceso, muchas responsabilidades",
|
|
"body": "Un proceso Python escucha en TCP 8008. Sirve la web estática Next.js, ofrece la API REST, gestiona el terminal WebSocket, ejecuta Health Monitor y envía notificaciones. No necesita un servidor web separado, un intermediario de mensajes ni una base de datos externa."
|
|
},
|
|
"requestFlow": {
|
|
"heading": "Recorrido de una petición",
|
|
"intro": "Una petición de datos típica sigue este recorrido; las preferencias del navegador pueden gestionarse localmente:",
|
|
"diagramCaption": "Las rutas protegidas validan JWT si la autenticación está activa. Flask devuelve datos del host almacenados en caché o recién obtenidos. El control opcional de Fail2Ban consulta su lista de bloqueos en caché. Solo se confía en cabeceras de IP reenviadas con PROXMENUX_TRUST_PROXY=1; si no, se usa la IP de la conexión. El estado persistente incluye SQLite y archivos JSON como auth.json.",
|
|
"diagramArrowLabel": "HTTP / WS",
|
|
"nodes": {
|
|
"clientLabel": "Cliente",
|
|
"clientDetail": "Navegador o PWA\n+ proxy opcional\nNginx / Caddy /\nTraefik",
|
|
"flaskLabel": "Flask :8008",
|
|
"flaskDetail": "Blueprints\nMiddleware JWT\nControl Fail2Ban\n(si está instalado)",
|
|
"hostLabel": "Herramientas del host",
|
|
"hostDetail": "psutil\npvesh\nsmartctl\njournalctl",
|
|
"stateLabel": "Estado local",
|
|
"stateDetail": "DB SQLite\n+ auth.json"
|
|
},
|
|
"threadsIntro": "El proceso también ejecuta <strong>hilos en segundo plano</strong>. Estos ejemplos recogen datos, actualizan cachés y SQLite, y envían notificaciones. Hay más tareas y el envío depende de que esté activado:",
|
|
"headerThread": "Hilo",
|
|
"headerCadence": "Cadencia",
|
|
"headerJob": "Tarea",
|
|
"rows": [
|
|
{
|
|
"thread": "_temperature_collector_loop",
|
|
"cadence": "60 s",
|
|
"job": "Guarda temperatura CPU y muestras de latencia en la base de historial, de modo que haya datos para gráficas sin un navegador conectado."
|
|
},
|
|
{
|
|
"thread": "_health_collector_loop",
|
|
"cadence": "5 min",
|
|
"job": "Ejecuta el ciclo Health Monitor de diez categorías, guarda errores y observaciones de disco, mantiene los descartes y envía nuevos eventos al motor de avisos."
|
|
},
|
|
{
|
|
"thread": "_vital_signs_sampler",
|
|
"cadence": "CPU 5 s; temperatura 15 s; memoria 30 s",
|
|
"job": "Muestrea uso CPU, memoria y temperatura CPU para los indicadores y la detección sostenida, con independencia del análisis general de cinco minutos."
|
|
},
|
|
{
|
|
"thread": "notification_manager.start()",
|
|
"cadence": "dirigido por eventos",
|
|
"job": "Inicia observadores del journal, tareas y hooks (<code>JournalWatcher</code>, <code>TaskWatcher</code>, <code>ProxmoxHookWatcher</code>) y envía a los canales configurados, con reformulación IA opcional."
|
|
}
|
|
]
|
|
},
|
|
"systemd": {
|
|
"heading": "Unidad systemd",
|
|
"intro": "El instalador crea la unidad <code>/etc/systemd/system/proxmenux-monitor.service</code>. Configuración predeterminada:",
|
|
"items": [
|
|
"<strong><code>User=root</code></strong>: el servicio usa privilegios root para operaciones del host como SMART, <code>pvesh</code> y gestión ZFS. Los procesos del terminal heredan esos permisos.",
|
|
"<strong><code>Restart=on-failure</code></strong> con espera de 10 segundos: se reinicia automáticamente tras una salida con error.",
|
|
"<strong><code>After=network.target</code></strong>: ordena el inicio después de ese objetivo; no garantiza IP asignada, DNS ni conectividad externa."
|
|
],
|
|
"inspectTitle": "Consultar la unidad actual"
|
|
},
|
|
"appimage": {
|
|
"heading": "Qué contiene el AppImage",
|
|
"intro": "AppImage es un sistema de archivos que se monta al ejecutarlo. <code>AppRun</code> en su raíz prepara el entorno y lanza <code>flask_server.py</code>:",
|
|
"consequencesIntro": "Dos consecuencias de esta estructura:",
|
|
"consequences": [
|
|
"<strong>Paquetes Python incluidos.</strong> Los paquetes Python vienen en la AppImage y se ejecutan con el intérprete Python 3.11 (PVE 8) o 3.13 (PVE 9) del propio host, sin instalar nada en su Python del sistema. La aplicación sigue usando interfaces del kernel y herramientas externas del host.",
|
|
"<strong>Clientes de hardware incluidos.</strong> Contiene <code>ipmitool</code>, <code>lm-sensors</code> y <code>upsc</code>. Las lecturas dependen del hardware compatible, acceso a sensores y, para el SAI, un servidor NUT configurado."
|
|
]
|
|
},
|
|
"flask": {
|
|
"heading": "Estructura de la aplicación Flask",
|
|
"intro": "<code>flask_server.py</code> crea una instancia <code>Flask(__name__)</code> con CORS restringido, registra directamente diez blueprints e inicializa el terminal. La tabla destaca módulos principales; también registra PBS, enlaces del Monitor, auditoría y backups de VM. Las precargas PVE solo se ejecutan en hosts PVE.",
|
|
"headerBlueprint": "Blueprint / módulo",
|
|
"headerPrefix": "Prefijo de rutas",
|
|
"headerOwns": "Responsabilidad",
|
|
"rows": [
|
|
{
|
|
"blueprint": "flask_server.py",
|
|
"prefix": [
|
|
"/api/system",
|
|
"/api/storage",
|
|
"/api/network",
|
|
"/api/vms",
|
|
"/api/hardware",
|
|
"/api/logs",
|
|
"/api/prometheus"
|
|
],
|
|
"owns": "Endpoints principales, web estática y control opcional Fail2Ban, activo si existe el jail <code>proxmenux</code> en el host."
|
|
},
|
|
{
|
|
"blueprint": "flask_auth_routes.py",
|
|
"prefix": [
|
|
"/api/auth/*"
|
|
],
|
|
"owns": "Inicio de sesión, emisión JWT, configuración y verificación TOTP, cambio de contraseña y tokens API."
|
|
},
|
|
{
|
|
"blueprint": "flask_health_routes.py",
|
|
"prefix": [
|
|
"/api/health/*"
|
|
],
|
|
"owns": "Comprobaciones de salud, estado detallado, errores activos y descartados, y silenciamientos. Las rutas protegidas exigen autenticación si está activada."
|
|
},
|
|
{
|
|
"blueprint": "flask_terminal_routes.py",
|
|
"prefix": [
|
|
"/api/terminal/* + WS"
|
|
],
|
|
"owns": "Crea un PTY por sesión y lo conecta por WebSocket con <code>xterm.js</code> del navegador."
|
|
},
|
|
{
|
|
"blueprint": "flask_notification_routes.py",
|
|
"prefix": [
|
|
"/api/notifications/*"
|
|
],
|
|
"owns": "Crear, consultar, editar y borrar canales; pruebas, IA, historial y envíos manuales."
|
|
},
|
|
{
|
|
"blueprint": "flask_security_routes.py",
|
|
"prefix": [
|
|
"/api/security/*"
|
|
],
|
|
"owns": "Fallos de acceso y, con Fail2Ban instalado, estado del jail, bloqueos y desbloqueo manual."
|
|
},
|
|
{
|
|
"blueprint": "flask_proxmenux_routes.py",
|
|
"prefix": [
|
|
"/api/proxmenux/*"
|
|
],
|
|
"owns": "Lee las optimizaciones ProxMenux registradas en el host."
|
|
},
|
|
{
|
|
"blueprint": "flask_oci_routes.py",
|
|
"prefix": [
|
|
"/api/oci/*"
|
|
],
|
|
"owns": "Funciones de despliegue de aplicaciones OCI y contenedores en Proxmox VE 9.1+."
|
|
}
|
|
],
|
|
"endpointsLink": "Los endpoints de integración y sus formatos se explican en <link>Referencia de la API</link>."
|
|
},
|
|
"dataSources": {
|
|
"heading": "Fuentes de datos",
|
|
"intro": "Los datos del host proceden principalmente de bibliotecas, archivos y comandos habituales de administración. Algunos datos del invitado dependen además de QEMU Guest Agent o de comandos ejecutados en el CT:",
|
|
"headerSource": "Fuente",
|
|
"headerUsedFor": "Usado para",
|
|
"rows": [
|
|
{
|
|
"source": "psutil",
|
|
"usedFor": "Carga CPU, memoria, swap, ocupación de montajes, contadores de red y procesos."
|
|
},
|
|
{
|
|
"source": "pvesh / qm / pct",
|
|
"usedFor": "Información del nodo Proxmox, inventario y configuración VM/CT, almacenamiento e historial de tareas."
|
|
},
|
|
{
|
|
"source": "smartctl",
|
|
"usedFor": "Atributos SATA/NVMe, estado SMART, desgaste, vida útil, modelo y número de serie."
|
|
},
|
|
{
|
|
"source": "zpool / zfs",
|
|
"usedFor": "Estado del pool (ONLINE / DEGRADED / FAULTED / UNAVAIL), progreso del scrub y uso de datasets."
|
|
},
|
|
{
|
|
"source": "journalctl",
|
|
"usedFor": "Registros del sistema, procesos terminados por OOM, errores ATA/NVMe/dm, seguridad y unidades de servicio propias."
|
|
},
|
|
{
|
|
"source": "ip / iproute2",
|
|
"usedFor": "Interfaces, direcciones, bridges, bonds y dispositivos gestionados por OVS."
|
|
},
|
|
{
|
|
"source": "nvidia-smi · intel_gpu_top",
|
|
"usedFor": "Datos GPU disponibles de utilización, VRAM, temperatura y codificación/decodificación. Los campos dependen del fabricante, herramienta y controlador."
|
|
},
|
|
{
|
|
"source": "lspci · lscpu · dmidecode",
|
|
"usedFor": "Topología PCIe, modelo y topología CPU, placa base y BIOS."
|
|
},
|
|
{
|
|
"source": "ipmitool · sensors",
|
|
"usedFor": "Sensores fuera de banda, ventiladores y temperaturas de la placa, si el hardware lo admite."
|
|
},
|
|
{
|
|
"source": "upsc (NUT)",
|
|
"usedFor": "Estado de batería, carga y autonomía del SAI si hay un servidor NUT configurado."
|
|
}
|
|
],
|
|
"cacheTitle": "Caché para reducir las consultas al host",
|
|
"cacheBody": "Los resultados costosos de <code>smartctl -a</code> y <code>pvesh get</code> se guardan temporalmente en el proceso Flask para reducir consultas repetidas al disco y a la API del clúster. La duración depende de la fuente: segundos para métricas y minutos para SMART."
|
|
},
|
|
"persistence": {
|
|
"heading": "Persistencia",
|
|
"intro": "Varios archivos guardan estados con distinto contenido y requisitos de acceso:",
|
|
"headerPath": "Path",
|
|
"headerOwner": "Propietario",
|
|
"headerContents": "Contenido",
|
|
"rows": [
|
|
{
|
|
"path": "/usr/local/share/proxmenux/health_monitor.db",
|
|
"owner": "root:root",
|
|
"contents": "Base SQLite. Tablas: <code>errors</code>, <code>events</code>, <code>disk_registry</code>, <code>disk_observations</code>, <code>user_settings</code>, <code>notification_history</code>, <code>excluded_storages</code>, <code>excluded_interfaces</code>. Modo de registro WAL."
|
|
},
|
|
{
|
|
"path": "/usr/local/share/proxmenux/.notification_key",
|
|
"owner": "root <code>0600</code>",
|
|
"contents": "Clave maestra de 32 bytes para secretos de notificación ENC2. Usa sal aleatoria por valor, claves derivadas y MAC. Los valores XOR antiguos se admiten para migrarlos."
|
|
},
|
|
{
|
|
"path": "/root/.config/proxmenux-monitor/auth.json",
|
|
"owner": "root:root",
|
|
"contents": "Configuración de acceso: activación, usuario, hash de contraseña PBKDF2-HMAC-SHA256 con sal, secreto TOTP, códigos de recuperación, clave JWT propia de la instalación, metadatos de tokens API y hashes de revocación. Los hashes SHA-256 antiguos migran al iniciar sesión correctamente."
|
|
},
|
|
{
|
|
"path": "/var/log/proxmenux-auth.log",
|
|
"owner": "root:root",
|
|
"contents": "Registro de autenticación en texto plano, independiente de Fail2Ban. Si el jail <code>[proxmenux]</code> está instalado y activo, lee este archivo para detectar fallos repetidos de acceso. Sin ese jail, el registro sigue disponible para consulta."
|
|
}
|
|
],
|
|
"backupTitle": "Guarda auth.json antes de reinstalar",
|
|
"backupBody": "Sustituir AppImage suele conservar <code>/root/.config/proxmenux-monitor/auth.json</code> y <code>/usr/local/share/proxmenux/health_monitor.db</code>. Conserva <code>auth.json</code> con su jwt_secret para mantener validez y metadatos de tokens. La base guarda estado de salud y avisos por separado. Guarda <code>.notification_key</code> con ella para restaurar secretos de canales; restaurar solo la base no restaura la autenticación API."
|
|
},
|
|
"health": {
|
|
"heading": "Ciclo Health Monitor",
|
|
"intro": "Aproximadamente cada cinco minutos, el proceso de fondo llama a <code>health_monitor.py</code> para comprobar las diez categorías del panel:",
|
|
"items": [
|
|
"Servicios críticos de PVE (<code>pveproxy</code>, <code>pvedaemon</code>, <code>pvestatd</code>, <code>pve-cluster</code>).",
|
|
"Almacenamientos Proxmox: <code>pvesh get /storage</code> y disponibilidad de cada uno.",
|
|
"Discos y sistemas de archivos: SMART, errores de E/S en dmesg, estado ZFS y capacidad de montajes.",
|
|
"VM y CT: fallos de arranque, caídas, errores QMP y fallos de apagado.",
|
|
"Red: estado de bridges y bonds, enlaces y latencia hasta la puerta de enlace.",
|
|
"Actualizaciones: paquetes pendientes y parches de seguridad.",
|
|
"Registros: patrones persistentes, picos y cascadas en el journal del sistema.",
|
|
"Memoria: actividad del OOM killer y presión RAM sostenida.",
|
|
"CPU: uso y temperatura de la CPU frente a los umbrales configurables; los sensores disponibles dependen del hardware.",
|
|
"Seguridad: fallos de acceso, bloqueos y estado del jail Fail2Ban."
|
|
],
|
|
"afterIntro": "Los hallazgos usan <code>error_key</code>, categoría y gravedad estables. Las filas de <code>errors</code> se actualizan en el sitio (<code>last_seen</code>, gravedad, motivo), sin duplicarlas. El envío de avisos tiene además sus propias huellas y tiempos de espera.",
|
|
"cycleEnd": "La limpieza puede resolver errores obsoletos con la <em>duración de silenciamiento</em> por categoría y reglas de estado, retirar registros huérfanos de recursos eliminados y borrar <code>events</code> de más de 30 días. Las categorías se explican en <link>Panel → Health Monitor</link>."
|
|
},
|
|
"notifications": {
|
|
"heading": "Motor de notificaciones",
|
|
"intro": "<code>notification_manager.py</code> coordina el envío: carga canales, gestiona la cola y ofrece una API Python para Flask y Health Monitor, además de una entrada de consola para los hooks <code>.sh</code> de ProxMenux.",
|
|
"items": [
|
|
"Los <strong>observadores</strong> generan eventos: <code>JournalWatcher</code> sigue el journal, <code>TaskWatcher</code> consulta tareas Proxmox, <code>ProxmoxHookWatcher</code> recibe hooks de backups, replicación y snapshots, y <code>PollingCollector</code> gestiona fuentes lentas.",
|
|
"Las <strong>plantillas</strong> convierten eventos en título y texto. El proveedor de IA configurado (OpenAI / Anthropic / Gemini / Groq / Ollama / OpenRouter) puede reformularlos en lenguaje natural. Ambas versiones se guardan en <code>notification_history</code>.",
|
|
"Los <strong>canales</strong> envían por Telegram, Discord, correo, Gotify, Pushover y Apprise, entre otros. Las implementaciones en <code>notification_channels.py</code> comparten <code>create_channel()</code> / <code>send()</code>.",
|
|
"<strong>Cifrado.</strong> Los ajustes sensibles (<code>telegram.bot_token</code>, <code>discord.webhook_url</code>, <code>pushover.user_key</code>, <code>pushover.api_token</code>, <code>ai_api_key_*</code>, <code>email.password</code>) se cifran con la clave de <code>.notification_key</code> antes de guardarse en la base de datos y se muestran enmascarados en la interfaz."
|
|
],
|
|
"linksFooter": "Los controles por evento, ajustes por canal e IA se explican en <notifLink>Ajustes → Notificaciones</notifLink> y <aiLink>Ajustes → Asistente de IA</aiLink>."
|
|
},
|
|
"websocket": {
|
|
"heading": "Terminal WebSocket",
|
|
"intro": "La pestaña <em>Terminal</em> conecta un cliente ligero <code>xterm.js</code> a un PTY del servidor por WebSocket. Hay dos modos:",
|
|
"items": [
|
|
"<strong>Modo HTTP, predeterminado:</strong> el servidor Flask incluido y <code>flask-sock</code> gestionan el cambio de protocolo a WebSocket.",
|
|
"<strong>Modo HTTPS / WSS:</strong> usa <code>gevent.pywsgi.WSGIServer</code> con su gestor predeterminado; <code>flask-sock</code> atiende las conexiones WebSocket sobre TLS."
|
|
],
|
|
"outro": "Los procesos del terminal heredan <code>User=root</code>. Abrir WebSocket exige un ticket breve y de un solo uso, obtenido del endpoint de tickets autenticado con permisos administrativos.",
|
|
"proxyNote": "Si usas un proxy inverso, permite el tráfico WebSocket y sus cabeceras <code>Upgrade</code> y <code>Connection</code>. El terminal necesita ese reenvío."
|
|
},
|
|
"proxy": {
|
|
"heading": "Proxy inverso y Fail2Ban",
|
|
"intro": "La elección de IP del cliente y el bloqueo opcional en la aplicación dependen de estos ajustes y componentes:",
|
|
"items": [
|
|
"<strong>Elección de IP.</strong> El control <code>before_request</code> y el registro de acceso solo confían en <code>X-Forwarded-For</code> y después <code>X-Real-IP</code> con PROXMENUX_TRUST_PROXY=1. Si no, usan <code>request.remote_addr</code>. Las cabeceras reenviadas no se aceptan automáticamente.",
|
|
"<strong>Fail2Ban opcional en la aplicación.</strong> Una conexión por proxy puede mostrar la IP del proxy como origen TCP. Si se obtiene la IP fiable del cliente y está activo el jail <code>proxmenux</code>, Flask rechaza las IP bloqueadas con HTTP 403. La lista se guarda 30 segundos y se renueva al procesar peticiones tras caducar, sin un temporizador de consultas."
|
|
],
|
|
"calloutTitle": "Fail2Ban no viene incluido",
|
|
"calloutBody": "Fail2Ban <strong>no</strong> viene incluido ni se instala automáticamente. Puede instalarse desde Seguridad del Monitor o <link>Seguridad → Fail2Ban</link> en ProxMenux Scripts. Si faltan <code>fail2ban-client</code> o el jail <code>proxmenux</code>, este control no bloquea peticiones. La autenticación sigue la política configurada.",
|
|
"outro": "Los ejemplos de proxy inverso Nginx/Caddy/Traefik y la guía del jail están en <accessLink>Acceso y autenticación</accessLink> y <fail2banLink>Seguridad → Fail2Ban</fail2banLink>."
|
|
},
|
|
"whereNext": {
|
|
"heading": "Por dónde seguir",
|
|
"items": [
|
|
{
|
|
"label": "Acceso y autenticación",
|
|
"href": "/docs/monitor/access-auth",
|
|
"tail": " — primer inicio, contraseña y TOTP 2FA, ejemplos de proxy inverso y jail Fail2Ban."
|
|
},
|
|
{
|
|
"label": "Referencia de la API",
|
|
"href": "/docs/monitor/api",
|
|
"tail": " — endpoints de integración, tokens y convenciones de autenticación."
|
|
},
|
|
{
|
|
"label": "Ajustes → ProxMenux Monitor",
|
|
"href": "/docs/settings/proxmenux-monitor",
|
|
"tail": " — control del servicio y comprobación del estado en el menú de texto de ProxMenux."
|
|
}
|
|
]
|
|
}
|
|
}
|