Esta es la guía de instalación, configuración y mantenimiento de Arcivéo Monitor. Las secciones están agrupadas: visión general, despliegue del panel, conexión de herramientas de seguridad, módulos integrados y diagnóstico. Los comandos se pueden copiar con el botón de la derecha.
La instalación del panel está en páginas paso a paso independientes. Elija un método:
Arcivéo Monitor es un panel de seguridad del servidor. Recopila datos de las herramientas instaladas (Fail2ban, UFW, Lynis, ModSecurity, AIDE, ClamAV, Auditd, CrowdSec, Suricata, Falco, etc.) y los muestra en una interfaz unificada con panel de control, mapa de ataques y páginas detalladas de cada herramienta.
El monitor no es un medio de protección activo: no bloquea ataques por sí mismo. Su función es agregar la información de las herramientas ya en funcionamiento y presentarla de forma cómoda.
El monitor funciona solo de forma local: debe instalarse en el mismo servidor que supervisa. No usa SSH ni API remota.
Todos los comandos (fail2ban-client, ufw status, ipset list, etc.) los ejecuta el panel con el usuario del servidor web (normalmente www-data; en paneles de hosting, la cuenta del sitio) con un conjunto reducido de permisos sudo: solo sobre utilidades concretas, sin acceso root general. Los resultados se analizan y se muestran en el navegador.
La puntuación parte del máximo y baja por cada problema detectado:
PermitRootLogin yes) — −20Resultado: 80+ = Protegido, 60–79 = Atención, <60 = En riesgo.
WebAuthn es un estándar de autenticación sin contraseña mediante una clave física. Es compatible con YubiKey, Touch ID, Face ID, Windows Hello y Passkey.
Tras iniciar sesión con la contraseña, el sistema solicita confirmación mediante la clave registrada. Aunque la contraseña se filtre, sin la clave física o la biometría es imposible acceder.
Para configurarlo, abra Claves WebAuthn en el menú lateral y pulse «Registrar clave». Registre dos claves a la vez: si la única clave se pierde o se estropea, no será posible acceder al panel con ella.
El panel puede enviar el informe de seguridad a Telegram y al correo (con un botón y de forma programada). Se configura en la sección «Ajustes».
Telegram. Necesita el token del bot y el chat id:
@BotFather → /newbot → obtendrá un token con el formato 123456:ABC....@userinfobot, o abra https://api.telegram.org/bot<TOKEN>/getUpdates y busque "chat":{"id":...}.Email. Dos métodos a elegir en «Ajustes» → Email:
re_...) y un dominio de remitente verificado.Estado del informe: «ATENCIÓN» u «OK». El título pasa a «ATENCIÓN» solo ante un problema real o una acción pendiente: se detecta una amenaza de ClamAV, cambios de archivos en AIDE, eventos críticos de Falco (Emergency/Alert/Critical en las últimas 24 h), un servicio caído en Monit, se requiere reinicio, caduca el SSL (≤14 días) o hay actualizaciones de seguridad pendientes. El ruido de fondo —intentos de fuerza bruta SSH de bots, IP baneadas por fail2ban, alertas de Suricata, advertencias de Lynis y peticiones ya bloqueadas por ModSecurity— no eleva el estado, por lo que esas cifras en el informe no significan «ATENCIÓN» por sí solas.
Los módulos de monitorización detallados (Lynis, UFW, ModSecurity, mapa de ataques, AIDE, ClamAV, etc.) se activan con una licencia vigente. Sin ella, el panel, los ajustes y la cuenta funcionan, pero los módulos muestran la tarjeta «Se requiere licencia».
Tras la compra en el área personal, dispone de un código de activación del tipo ARCIVEO-XXXX-XXXX-XXXX-XXXX. Debe «activarlo» para el dominio de su panel: esto convierte el código en un archivo de licencia firmado (bloque [license]) que usted pega en el panel.
Cómo activar (3 pasos):
my.arciveo.com → sección «Licencias» / «Activación de licencia»: copie el código ARCIVEO-….monitor.example.com). Pulse activar: el sistema generará un archivo de licencia vinculado a ese dominio y lo mostrará en un campo con el botón «Copiar».El panel verifica la clave criptográficamente: la firma, la vinculación al dominio y la vigencia.
APP_URL en config.php e introduzca solo el nombre del host, sin https:// ni el prefijo www. La activación es de un solo uso: el código se convierte en una licencia para el dominio introducido y no se puede reactivar; si se equivoca en el dominio, la clave no servirá para su panel y el código quedará gastado. Por eso, introduzca el dominio con atención.
Todos los parámetros principales del panel se definen en un único archivo config.php en la raíz (junto a la carpeta public/) mediante constantes define() normales. El archivo se crea durante la instalación; rara vez hay que editarlo a mano, sobre todo al cambiar de dominio, migrar o conectar a otra base de datos. Tras cualquier cambio, reinicie PHP-FPM (de lo contrario, debido a OPcache, los cambios no se aplicarán).
Introduzca sus valores en los lugares resaltados; deje el resto tal cual:
Base de datos. Credenciales de conexión a MySQL/MariaDB:
DB_HOST — host del SGBD, casi siempre localhost;DB_NAME — nombre de la base de datos del panel;DB_USER — usuario de la BD (acceso solo a su propia base);DB_PASS — contraseña de este usuario;DB_CHARSET — codificación de la conexión, deje utf8mb4.Aplicación.
APP_URL — dirección completa del panel (p. ej. https://monitor.example.com). Debe coincidir con el dominio en el que se activó la licencia; de lo contrario, la clave será rechazada (véase la sección «Licencia»);TIMEZONE — zona horaria de PHP: afecta solo a cómo el panel muestra las fechas y las horas. No afecta a la hora de ejecución de las tareas cron; allí rige la zona horaria del sistema (véase «Todas las tareas cron»).Tiempo de sesión. SESSION_LIFETIME — tiempo de espera de inactividad de la sesión en segundos (deslizante: se renueva con la actividad). Por defecto 28800 = 8 horas; tras ese tiempo de inactividad el panel pedirá iniciar sesión de nuevo. Por ejemplo, 3600 = 1 hora, 86400 = un día.
Registro de errores. Los errores nunca se muestran a los visitantes, sino que se escriben en logs/php_errors.log — se ven en la página «Registros de la aplicación». Estas líneas (display_errors=0, log_errors=1, la ruta error_log) normalmente no hay que cambiarlas: los ajustes están definidos en el propio archivo y no dependen de php.ini.
public/), y este panel tiene como raíz web (DocumentRoot) precisamente la raíz del panel, no public/. El archivo en sí no se «filtra»: en el .htaccess de la raíz hay una prohibición explícita para él (Require all denied) — el servidor devuelve 403. Incluso sin esa regla el código fuente no se filtraría: es PHP — el servidor lo ejecuta, no lo entrega como texto. Por si acaso: no lo publique en repositorios públicos ni lo envíe al soporte con la contraseña real. Los permisos del archivo son 640.
UFW (Uncomplicated Firewall) es una interfaz sencilla para nftables/iptables. Cierra todos los puertos entrantes salvo los permitidos explícitamente. La página «Cortafuegos UFW» muestra el estado y las reglas.
ufw enable, permita obligatoriamente SSH (ufw allow OpenSSH), de lo contrario perderá el acceso al servidor.
deny no se considera accesible desde el exterior.
Skipping adding existing rule no es un error. Así indica UFW que esa misma regla ya existe y no la vuelve a añadir. Al reejecutar la configuración automática (que es idempotente), este es un mensaje normal: no hace falta reaccionar.
Bloquea automáticamente la IP tras superar el número de intentos de inicio de sesión fallidos. Analiza los registros de SSH, nginx, Apache y otros servicios.
La instalación básica está arriba. Aquí tiene una configuración operativa que genera decenas de jails activos y miles de bloqueos: ajustes generales, jails clave y baneo automático de IP maliciosas de la lista ipsum.
El archivo /etc/fail2ban/jail.local — ajustes generales y jails esenciales:
ignoreip anote obligatoriamente su propia IP y las redes de confianza; de lo contrario, podría banearse a sí mismo. Tras los cambios: sudo fail2ban-client reload.
Carga automática de la lista de bloqueo ipsum — en el cron de root (sudo crontab -e): el level 1 (más de 100 mil IP) se carga en el conjunto ipsum, que se filtra en el cortafuegos (más detalles en la sección «Lista de bloqueo IPset»):
ipsum: es exactamente el que lee el panel (tarjeta «IPset ipsum»). Niveles: levels/1.txt — máxima cobertura, levels/3.txt — más preciso (3 o más fuentes).
Por qué el «Monitor de seguridad» se divide en dos zonas. La protección funciona en dos niveles, y el panel no los mezcla:
sshd, apache-*, nginx-*, etc.) y reincidentes graves (jail recidive — los que ya han sido baneados varias veces). Son IP que realmente intentaron entrar en su servidor: aparecen en el mapa de ataques y en la «Línea de tiempo».ipset ipsum, filtrada en el cortafuegos mediante la regla DROP. En su mayoría, estas direcciones ni siquiera llegaron a tocar su servidor: se cortan por adelantado; el contador «IPset ipsum» muestra cuántas se filtraron de forma preventiva.La diferencia es sencilla: reactivo — «estos atacaron y fueron baneados», preventivo — «estos fueron bloqueados antes de intentarlo». Antes se forzaba artificialmente en recidive la list-3 de ipsum (de ahí la antigua división de «recidive por lista»); ahora recidive contiene solo reincidentes reales, y el bloqueo preventivo está íntegramente en el cortafuegos.
ipsum es una lista pública de IP maliciosas que se actualiza a diario. El monitor muestra el número de direcciones cargadas en el panel y en el mapa de ataques, y lo tiene en cuenta en la Puntuación de seguridad (−10 si el conjunto no está cargado).
La opción mínima sin fail2ban es un conjunto ipsum independiente con bloqueo mediante iptables:
@reboot. De paso, el comando create … -exist fija el límite maxelem 300000 (por defecto 65536 — el level 1 no cabe y aparecerá «Hash is full»):
ipsum y, si el cortafuegos lo gestiona el instalador (VPS nuevo — perfiles «Completa»/«Ligera»), conecta el conjunto a UFW con una regla DROP: el tráfico de esas IP se bloquea realmente. La regla se coloca después de ESTABLISHED,RELATED, por lo que las conexiones actuales (incluido su SSH) no se cortan — solo se cortan las nuevas conexiones de la lista. El conjunto se restaura al cargar el servicio ipsum-load.service antes del cortafuegos (de lo contrario UFW no habría arrancado), y se actualiza con cron a las 04:00. En un servidor ya configurado (panel, cortafuegos propio) el instalador no toca el cortafuegos — allí ipsum queda como una lista para el panel y el mapa de ataques, y la regla DROP se añade a mano si se desea (la opción mínima con iptables … --match-set ipsum … -j DROP está arriba). Con la instalación automática no hay que hacer nada a mano.
Reemplazo moderno de Fail2ban con inteligencia de amenazas colectiva: bloqueos de la comunidad más reglas propias. Requiere un bouncer aparte para aplicar los bloqueos al cortafuegos.
systemctl is-active crowdsec). Para activarlo: sudo systemctl enable --now crowdsec; si falla, consulte sudo journalctl -u crowdsec -n 30. La misma regla aplica a cualquier servicio en estado «No iniciado» (Suricata, Falco, Monit, MySQL).
stream halted / los bloqueos no se aplican. Es una clave de api huérfana: el bouncer se eliminó de cscli bouncers list, pero su clave antigua quedó en /etc/crowdsec/bouncers/*.yaml. Vuelva a registrar el bouncer e indique la clave nueva:
AIDE (Advanced Intrusion Detection Environment) toma una instantánea del sistema de archivos y en cada comprobación informa de los cambios en /etc, /bin, /usr. Tras la instalación es obligatorio inicializar la base (aideinit).
aideinit la terminal permanece 5–15 minutos en la línea Running aide --init... — es normal (hasheo de todo el sistema de archivos, carga sobre el disco). No interrumpa con Ctrl+C. Si el proceso «se cuelga» pero no escribe nada, es posible que esté esperando una respuesta a una petición oculta Overwrite existing aide.db.new [Yn]? (pulse Y). Compruebe la actividad desde otra sesión: pgrep -af aide.
aideinit: «21_aide_spamassassin … printf: invalid number» (return code 20) — un fallo conocido del fragmento de configuración de AIDE en Ubuntu 22.04. La base no se crea. Retire el fragmento defectuoso y vuelva a intentarlo:
aideinit no se ha ejecutado, o bien (Ubuntu 24.04) el directorio /var/lib/aide se creó en modo 700 y es inaccesible para www-data — se soluciona con sudo chmod 755 /var/lib/aide (véase el bloque anterior). «Sin comprobaciones» = la base existe, pero aún no se ha realizado ninguna comprobación — no es un error. El monitor lee los resultados de /var/log/aide/aide.log.
/etc/cron.daily/aide estándar en las nuevas Ubuntu/Debian puede no escribir /var/log/aide/aide.log en el formato necesario (y aide.wrapper ya no está en ellas). Es más fiable añadir su propio cron con un --config explícito — escribe el registro como root en modo 644, y el monitor lo lee sin grupos adicionales:
chmod 755 /var/lib/aide y el cron de comprobación a las 02:00 — no hace falta nada manual.
sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.
Escáner antivirus para Linux. Especialmente útil para revisar /var/www en busca de shells PHP y código malicioso.
enable --now? Tres causas típicas:
1. En la configuración quedó la línea Example: clamd se niega a iniciar mientras esté presente:
2. No se ha descargado la base de firmas: clamd no arranca sin ella:
3. Simplemente está cargando: clamd carga en memoria unos 8 millones de firmas en 30–60 s. Espere y compruebe: systemctl is-active clamav-daemon (estado activating → aún cargando).
sudo journalctl -u clamav-daemon -n 30 --no-pager.
clamd solo mantiene las firmas en memoria; por sí mismo no analiza nada según una programación. El panel muestra los resultados del análisis programado, por lo que se necesita un cron que analice y escriba un registro. La instalación automática coloca el envoltorio /usr/local/bin/clamav-scan.sh y un cron a las 01:30; tras la primera ejecución se rellenarán «Archivos analizados» y «Último análisis». Para ejecutarlo de inmediato, sin esperar a la programación: sudo /usr/local/bin/clamav-scan.sh.
Linux Malware Detect (LMD) — escáner de malware para amenazas web: shells PHP, backdoors web, descargadores. Usa el motor de ClamAV y lo complementa con sus propias firmas.
maldet --report.
update-rc.d: error: unable to read /etc/init.d/maldet: es inofensiva. maldet no usa init.d; la actualización de firmas y los análisis se ejecutan mediante /etc/cron.daily/maldet. Si más abajo aparece installation completed, todo se instaló correctamente.
apt, sino en /usr/local/maldetect, y con open_basedir activado su presencia se comprueba mediante shell; consulte la sección «La página está vacía aunque hay datos en el servidor».
Sistema de detección de intrusiones en la red: analiza el tráfico a nivel de paquetes y conoce miles de firmas de ataques. Complementa a ModSecurity (que actúa a nivel HTTP, mientras que Suricata lo hace a nivel TCP/IP).
/var/log/suricata/eve.json como root con permisos 750 en el directorio, y el servidor web (www-data) no puede leerlo. Habilite el paso al directorio: los archivos internos siguen protegidos:
Intercepta las llamadas al sistema mediante eBPF/kernel module y detecta anomalías en tiempo real: shell desde nginx, lectura de /etc/passwd por un proceso web, escritura en /bin, etc.
journalctl -u falco (sin sudo, a través del grupo systemd-journal). Asegúrese de que www-data pertenezca a ese grupo — consulte «Configuración de sudo» (punto 2) en la página de instalación manual.
/etc/passwd, escritura en directorios del sistema). Cero eventos críticos en un día en un servidor tranquilo es un estado saludable.
journalctl requiere permisos sobre el registro; para que el panel vea los eventos de forma estable, la instalación automática activa en Falco file_output → /var/log/falco/falco.log y asigna al servicio UMask=0022 (el log lo lee el servidor web). En una instalación nueva no es necesario configurarlo manualmente.
ModSecurity — cortafuegos web (WAF) para Apache o Nginx. Bloquea ataques a nivel de aplicación: inyecciones SQL, XSS, salto de rutas, escáneres.
IncludeOptional /etc/modsecurity/*.conf, y el paquete solo coloca modsecurity.conf-recommended, que no coincide con la máscara *.conf. Si no lo copia a modsecurity.conf, SecRuleEngine permanece en Off: el módulo está cargado, las reglas CRS están cargadas, pero el tráfico no se inspecciona y no se crea el registro de auditoría. El modo intermedio DetectionOnly solo anota los eventos en el registro sin bloquear las solicitudes; el panel lo muestra en amarillo.
Acceso del panel al registro de auditoría. El registro /var/log/apache2/modsec_audit.log pertenece a root (permisos 640) y el usuario web no puede leerlo. El panel obtiene los datos mediante un envoltorio; créelo:
SecRuleEngine sin sangría: las líneas con sangría están dentro de bloques <LocationMatch>/<Directory> (por ejemplo, la desactivación del WAF para phpMyAdmin) y no definen el modo global.www-data, en HestiaCP el pool del sitio se ejecuta con el propietario del sitio (por ejemplo, admin) — compruebe grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.X-Forwarded-For. En las estadísticas solo entran las transacciones con una regla activada: la directiva SecAuditLogRelevantStatus anota en el registro de auditoría cualquier respuesta 4xx/5xx, por lo que también entran los 403/500 normales, que el panel no considera eventos WAF.---RULES--- lo necesita la sección «Todas las reglas activas»: el panel muestra no solo las reglas activadas, sino absolutamente todas las reglas CRS cargadas más las personalizadas. Las tres rutas del bucle for f in … son las ubicaciones típicas de las reglas CRS y los añadidos locales; si su disposición es distinta (el paquete coloca los archivos en su propio directorio, o las reglas personalizadas no están en /etc/modsecurity/custom-rules.conf), localice las rutas reales con el comando sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null e insértelas en la lista. Si el envoltorio es antiguo (sin esta sección), la sección simplemente mostrará la advertencia «no disponible» y el resto de la página funciona como antes.
Auditd (Linux Audit Daemon) registra las llamadas al sistema a nivel del núcleo: inicios y cierres de sesión, comandos sudo, intentos fallidos de autenticación, cambios en archivos. El monitor muestra los inicios de sesión, los intentos fallidos y los comandos sudo de hoy.
ausearch (/usr/sbin/ausearch) y, si es necesario, desde /var/log/audit/audit.log con el comando tail. Ambos deben estar en sudoers.
Supervisa los servicios (nginx, php-fpm, mysql, etc.) y los reinicia cuando fallan. Puede enviar alertas por email.
monit status. En /etc/monit/monitrc debe estar habilitada la interfaz HTTP (bloque set httpd con allow localhost), de lo contrario monit status devolverá un error.
monitrc la línea set httpd está comentada (por defecto figura como # set httpd port 2812 …). Descomente el bloque y permita localhost. (2) El httpd activado por sí solo no supervisa nada: Monit solo cuenta lo que se describe con estrofas check; sin ellas la lista está vacía incluso con la interfaz en funcionamiento. Configuración mínima funcional:
conf.d listo con httpd en 2812 y un conjunto de comprobaciones: en una instalación nueva no hay que configurar nada manualmente.
PSAD analiza el registro de iptables y detecta escaneos de puertos y ataques de red, asignando a cada origen un nivel de amenaza (1–5). Complementa a fail2ban y Suricata.
psad --Status (necesario en sudoers). Sin el registro de iptables la página estará vacía — es normal mientras no haya habido escaneos.
Mandatory Access Control limita a qué archivos y recursos puede acceder un programa, incluso si ha sido comprometido. En Ubuntu/Debian se usa AppArmor de forma predeterminada (normalmente ya está instalado y activo).
aa-status (debe estar en sudoers). Muestra el número de perfiles en modo enforce/complain y los procesos sin perfil.
Que haya más «Perfiles cargados» que enforce + complain es normal. En AppArmor 4.x (Ubuntu 24.04 y posterior) apareció el modo unconfined: el perfil está cargado en el núcleo, pero no limita nada. Ubuntu marca así decenas de perfiles de programas que usan user namespaces (navegadores, clientes de torrent y similares). Cuando existen esos perfiles, la tarjeta «Perfiles cargados» se vuelve ámbar y muestra su número — por ejemplo unconfined: 90 con 120 cargados y 26 en enforce. En realidad solo protegen los perfiles en enforce; en Ubuntu 22.04 (AppArmor 3.x) este modo no existe y las cifras siempre cuadran.
unconfined solo tiene sentido hacerlo de forma consciente: no están desactivados por error, sino porque de lo contrario se rompe el funcionamiento de los propios programas. Los perfiles en complain son otra cosa: ahí las reglas ya están escritas y solo no se aplican.
debsums comprueba que los archivos de los paquetes instalados coinciden con las sumas de verificación del repositorio, detectando binarios del sistema alterados (complementa a AIDE). Una comprobación completa tarda 1–2 minutos, por eso se ejecuta mediante cron, y el panel lee el resultado de data/debsums/debsums.log y lo clasifica por categorías (solo importan los binarios y las bibliotecas).
La tarea va en el cron de root (sudo crontab -e). El script listo debsums-scan.sh se coloca en /usr/local/bin/ (chmod +x; consulte el resumen de tareas cron) y escribe él mismo el informe en data/debsums/ del panel.
El script debsums-scan.sh encuentra por sí mismo la carpeta data/ del panel: no hace falta indicar la ruta.
/etc/ (configuraciones) y /usr/share/ (recursos) del servidor suelen ser normales; el panel los marca con un color distinto. Preocupantes son los cambios en binarios y bibliotecas (/bin, /sbin, /usr/lib, etc.): la tarjeta «Binarios / bibliotecas» muestra precisamente esos.
Lynis se ejecuta manualmente o mediante cron. El informe debe guardarse en la carpeta data/lynis/ del proyecto — el monitor lee el archivo lynis-report.dat.
lynis-scan.sh en segundo plano directamente desde el panel (sin esperar a cron): muestra «Escaneando…» y al terminar actualiza el informe por sí mismo. Para ello, el usuario web necesita una línea de sudoers que permita ejecutar el script — el instalador la añade automáticamente en /etc/sudoers.d/monitor. Si el panel se instaló manualmente o anteriormente, añádala con el mismo usuario ya indicado en el archivo:
Logwatch debe guardar los informes diarios en la carpeta data/logwatch/ del proyecto en formato .txt. El monitor muestra el último informe y el archivo.
El monitor de red no requiere instalación: es una página integrada del panel. Muestra el estado de red del servidor a partir de fuentes locales:
/proc/net/dev;ip;ss;journalctl -k.Las tres primeras fuentes funcionan sin sudo, por lo que las interfaces, el tráfico, las conexiones y los puertos se ven de inmediato. El bloque «Eventos del kernel» usa journalctl -k: se lee a través del grupo systemd-journal («Configuración de sudo», punto 2), no hace falta sudo. Compruebe que todo esté accesible para el usuario web:
UFW BLOCK no aparecen aquí: están en las páginas «Cortafuegos UFW» y «Mapa de ataques». Un bloque vacío con la marca verde = no hubo fallos de red en las últimas 24 h.
La página integrada muestra tres cosas:
df); la barra se pone roja al ≥90%;lsblk), solo los reales (loop/snap se ocultan);smartctl).El espacio y la lista de dispositivos funcionan de inmediato, sin configuración. Para SMART se necesita el paquete smartmontools. El proceso web no tiene acceso directo a los dispositivos de disco, por eso SMART se captura mediante cron en el archivo data/disk/smart.txt, y el panel lo lee.
La tarea va en el cron de root (sudo crontab -e). El envoltorio listo smart-scan.sh se coloca en /usr/local/bin/ (chmod +x; consulte el resumen de tareas cron) y escribe por sí mismo en data/disk/ del panel.
El envoltorio smart-scan.sh encuentra por sí mismo el data/ del panel — no hace falta indicar la ruta. Internamente lsblk -e7,11 excluye loop/cdrom.
La página muestra el historial de carga del servidor de las últimas 24 horas: Load Average, uso de CPU y espera de I/O, RAM/Swap, tráfico de red (recepción/envío), I/O de disco (lectura/escritura), ocupación de disco e inodes, descriptores de archivo abiertos y conexiones MySQL, además del número actual de conexiones TCP y procesos.
Los datos los recopila cron/collect_metrics.php: cada 5 minutos escribe una instantánea «cruda» de los contadores (/proc/loadavg, /proc/meminfo, /proc/stat, /proc/net/dev, /proc/diskstats, df/df -i, /proc/sys/fs/file-nr, SHOW GLOBAL STATUS LIKE 'Threads_connected') en la tabla de BD system_metrics; los porcentajes y las velocidades los calcula la propia página por la diferencia entre instantáneas contiguas (ocupación de disco/inodes/descriptores/conexiones MySQL son valores instantáneos, sin recálculo). No se requiere sudo: las fuentes se leen sin permisos de root. Los puntos anteriores a 24 horas se eliminan automáticamente en cada escritura.
El envoltorio collect-metrics-all.sh (véase el resumen de tareas cron) detecta por sí mismo todas las instancias del panel instaladas en el servidor y ejecuta el cron/collect_metrics.php de cada una en nombre del propietario del sitio.
Alertas de carga (sección «Ajustes» → «Alertas de carga»): al superar el umbral de CPU/RAM/disco/inodes, el panel envía una notificación a Telegram/Email (los mismos canales que el informe diario; no es necesario activarlos por separado para las alertas), y otra más cuando la métrica vuelve a la normalidad. No genera spam mientras se mantiene el umbral: la siguiente notificación solo llega tras un ciclo de «recuperado → superado de nuevo».
collect_metrics.php en cada ejecución (cada 5 minutos): no hace falta un cron aparte. El estado «ya notificado / aún no» se guarda en data/alerts_state.json, y los umbrales en los ajustes del panel.
La página «Mapa de ataques» determina el país por IP con el comando geoiplookup. Sin el paquete GeoIP no se detectan los países ni aparecen los puntos en el mapa:
/usr/share/GeoIP/GeoIP.dat la lee cualquiera y los resultados se almacenan en caché en tmp/geoip_cache.json. El propio mapa (Leaflet + teselas de OpenStreetMap) se carga en el navegador: se necesita internet en el equipo donde esté abierto el panel.
Dos tarjetas integradas del panel que muestran no el «encendido/apagado» de una herramienta, sino la protección real del servidor. No requieren instalación y se leen localmente sin sudo.
Exposición externa: cuántos servicios escuchan en todas las interfaces (0.0.0.0/[::]) y son accesibles desde fuera. Resalta en rojo si hay bases de datos o cachés expuestas (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch): es un agujero directo (−10 en la Puntuación de seguridad). Origen: ss -tuln.
127.0.0.1 (bind-address en la configuración de MySQL/PostgreSQL, bind 127.0.0.1 en Redis) o cierre el puerto en UFW.
127.0.0.1 (loopback) solo es visible para el propio servidor: no se puede llegar a él desde fuera, aunque el puerto esté «abierto». Por eso Postfix en el puerto 25, vinculado a loopback, es seguro: la configuración automática establece inet_interfaces = loopback-only (además de un smtpd_banner neutro, que resuelve la advertencia de Lynis MAIL-8818 sobre la revelación de la versión). La tarjeta «Exposición externa» cuenta como expuesto solo lo que escucha en 0.0.0.0/[::]; los servicios en loopback no se incluyen.
/etc/postfix/main.cf defina smtpd_banner = $myhostname ESMTP (sin versión ni sistema operativo) e inet_interfaces = loopback-only, y luego sudo systemctl restart postfix.
Actualizaciones de seguridad: cuántos parches de seguridad esperan instalarse y si se requiere reiniciar tras actualizar el núcleo (−5 en la Puntuación de seguridad si hay parches). Origen: /usr/lib/update-notifier/apt-check, archivo /var/run/reboot-required. La lista detallada está en la página «Actualizaciones de seguridad».
update-notifier-common). Si apt-check no está presente, el monitor calcula los parches mediante apt-get -s upgrade.
Actualizaciones de seguridad automáticas (unattended-upgrades): en la página «Actualizaciones de seguridad» una tarjeta aparte muestra si la instalación automática de parches de seguridad está activada y cuándo se ejecutó por última vez. No se necesita sudo: el estado se lee mediante apt-config dump.
La copia de seguridad es el seguro principal: perder los datos es peor que cualquier intrusión. Se necesitan dos cosas: una copia del servidor/sitios y, aparte, una copia de la BD del panel (ahí están los usuarios, las claves WebAuthn, los ajustes y la licencia).
Opción A — HestiaCP: pestaña Backup del usuario → botón para crear la copia (o de forma programada en los ajustes del servidor). La copia incluye los sitios y sus BD.
Opción B — manual (cron): volcado de la BD + archivo del directorio data/ del panel:
Actualización a una nueva versión. Primero haga una copia de seguridad. Luego vuelva a subir los archivos de código, conservando sus datos:
public/, includes/, assets/, cron/, database/, así como los .htaccess raíz (el controlador frontal: el enrutamiento no debe quedar de la versión anterior), manifest.json, sw.js;config.php (datos de la BD), data/ (informes), logs/, tmp/ (sesiones y caché).SSH_FX_PERMISSION_DENIED — Permission denied. Los archivos del panel pertenecen a www-data (así se establecieron durante la instalación), mientras que el cliente SFTP se conecta con su propio usuario, que no tiene permiso de escritura. Dar a www-data todo el panel «para que funcione» es precisamente lo que provoca este error; a continuación, tres formas, cualquiera resuelve el problema.
data/ (informes), tmp/ (sesiones y caché), logs/; estos permanecen a nombre de www-data. El resto es código, y el servidor web solo lo necesita para lectura, que le concede el grupo www-data con permisos 644. Ventaja adicional: ante una vulnerabilidad en PHP, los archivos del panel ya no se pueden reescribir. En los paneles de hosting (HestiaCP y similares) la variante A no es necesaria: allí los archivos del sitio ya pertenecen a la cuenta con la que se conecta por SFTP, y el servidor web los lee mediante el grupo.
chmod posterior sobre los archivos restablece la máscara de la ACL, y el acceso desaparece silenciosamente. Si tras «poner orden en los permisos» la subida vuelve a toparse con Permission denied, repita ambos comandos setfacl.
2 de la variante C es setgid: los archivos subidos por SFTP permanecen en el grupo www-data; de lo contrario el panel no podrá sobrescribirlos. Tras la variante C, vuelva a conectarse en FileZilla: el nuevo grupo solo se aplica en un inicio de sesión nuevo. Comprobación: id deploy (debe aparecer el grupo www-data) y ls -ld /path/to/monitor (drwxrwsr-x: la letra s significa que setgid está activo).
Migración a otro servidor:
config.php, data/.mysqldump en el antiguo → importar en el nuevo; corrija los datos de la BD en config.php.adm, tareas de cron.Si no puede iniciar sesión, todo se soluciona directamente en la BD desde el servidor. Abra la BD (el nombre está en config.php):
Clave WebAuthn perdida (no supera el segundo factor): desactive el 2FA, inicie sesión con la contraseña y registre una nueva clave:
Olvidó la contraseña: establezca un nuevo hash (genérelo en el servidor e insértelo):
Se bloqueó a sí mismo con el filtro de IP: desactive la restricción:
sudo mysql en el servidor, o bien phpMyAdmin / la sección de BD en el panel del hosting. Tras la recuperación, vuelva a activar WebAuthn y el filtro de IP.
El resumen de tareas está en el cron root del servidor (se añaden mediante sudo crontab -e). Deje solo las líneas de las herramientas que use; ajuste las rutas a su servidor.
sudo crontab -l y que el servicio cron esté activo.
TIMEZONE de config.php. La constante TIMEZONE solo afecta a PHP (cómo muestra el panel las fechas), pero el demonio cron ejecuta las tareas según la hora del sistema del SO. Si la zona del servidor no coincide con la suya, el informe de las «08:00» llegará a otra hora. Ejemplo: el servidor está en otra zona (Europe/London, UTC+1) y usted en Madrid (UTC+2) → el informe de las «08:00» llegará a las 09:00 según su hora. Compruébelo y, si hace falta, ajuste la zona del sistema a la suya:
0 8 * * * se ejecutará a las 08:00 hora local. De lo contrario, habría que desplazar el propio cron, pero al cambiar entre horario de invierno/verano el desplazamiento volvería a descuadrarse, por eso es más correcto configurar la zona del sistema.
crontab.txt) están en la carpeta system/ junto al proyecto, fuera de public_html. Esto no es parte del sitio — no hace falta subirlos a la raíz web; colóquelos en el servidor en las rutas del sistema (como en el crontab de arriba):
lynis-scan.sh → /usr/local/bin/ (chmod +x) — ejecuta lynis audit system, durante el análisis pone la marca /tmp/lynis-running y copia lynis-report.dat a data/lynis/ del panel;logwatch_daily.sh → /usr/local/bin/ (chmod +x) — genera el informe diario de Logwatch (sshd, fail2ban, sudo, postfix) en data/logwatch/;smart-scan.sh → /usr/local/bin/ (chmod +x) — captura el estado de los discos (smartctl) en data/disk/;debsums-scan.sh → /usr/local/bin/ (chmod +x) — comprueba la integridad de los paquetes (debsums) en data/debsums/;clamav-scan.sh → /usr/local/bin/ (chmod +x) — análisis antivirus de ClamAV en rutas peligrosas (web, home, temp); escribe el resumen en /var/log/clamav/scan.log, de donde lo lee la página de ClamAV (línea 01:30 del crontab de arriba);load-ipsum.sh → /usr/local/bin/ (chmod +x) — actualiza el conjunto ipset ipsum (level 1) en el sitio, sin romper las reglas activas del cortafuegos (línea 04:00 del crontab de arriba);daily-report-all.sh → /usr/local/bin/ (chmod +x) — ejecuta el informe cron/daily_report.php del panel (línea 08:00 del crontab de arriba);daily_report.php — ya viene incluido en el panel (cron/daily_report.php), se ejecuta mediante daily-report-all.sh, no hace falta instalarlo aparte;collect-metrics-all.sh → /usr/local/bin/ (chmod +x) — ejecuta cron/collect_metrics.php del panel (página «Rendimiento», línea */5 del crontab de arriba); collect_metrics.php ya viene incluido en el panel, no hace falta instalarlo aparte;crontab.txt (system/cron/) — ejemplo de tareas; añada las líneas necesarias mediante sudo crontab -e./usr/local/bin/. No se puede escribir allí directamente desde FileZilla — el directorio pertenece a root y el cliente SFTP recibirá SSH_FX_PERMISSION_DENIED. El orden es este: primero suba el archivo a /tmp (donde todos pueden escribir), luego muévalo a su sitio con un solo comando:
/tmp en la raíz del servidor — no /var/tmp ni tmp/ dentro del propio panel (este último pertenece a www-data y está cerrado a su usuario). En el árbol de FileZilla, /tmp es una rama de nivel superior, junto a var, no dentro de ella.
/usr/local/bin/, registro — /var/log/arciveo-cron.log) — no hace falta hacer nada manualmente.
/home/*/web/*/public_html y /var/www/*, y colocan los informes en su data/. Si el panel está en otra ruta, añádala a la línea for app in … dentro de los scripts, de lo contrario los informes de Lynis/SMART/debsums/Logwatch no llegarán al panel.
logs/cron.log lo crea primero el cron root — pertenecerá a root, y la pestaña «Registro de cron» del panel no podrá ni leerlo ni vaciarlo. Cree el archivo de antemano con el usuario web (propietario del directorio del sitio; en HestiaCP es la cuenta, p. ej., admin) — así el cron root solo añadirá contenido sin cambiar el propietario:
stat -c %U /path/to/monitor.
sudo crontab -e), de lo contrario se ejecutaría dos veces.
sudo crontab pelado (eso sería una escalada directa a root por parte de cualquiera que obtenga acceso a la sesión del panel), sino un script acotado con dos comandos (list/set) que solo toca su propio bloque entre los comentarios de servicio. Instálelo una sola vez:
www-data — compruebe con qué usuario funciona el pool de PHP-FPM del sitio (ps -o user= -C php-fpm) y sustitúyalo en la línea de sudoers.
public/crontab_monitor.php se subió por FTP/SFTP con un usuario del sistema distinto (por ejemplo, root) al del resto de los archivos del sitio, el servidor web no podrá leerlo. Compare el propietario y los permisos con el archivo vecino y hágalos coincidir:
El monitor detecta la presencia de las herramientas mediante dpkg-query, la base de paquetes de APT. Si la herramienta no se instaló con apt (manualmente, desde snap o desde el código fuente), dpkg no la ve.
Error 500: revise los registros de PHP, nginx y del propio monitor:
www-data, sino bajo la cuenta del usuario (por ejemplo admin, propietario del directorio del sitio). Todas las reglas sudo y la pertenencia a grupos (adm, systemd-journal) deben asignarse a ese usuario; de lo contrario, los módulos mostrarán «Inactivo / 0» aunque los servicios estén funcionando. Para averiguar el usuario real de PHP: ps -o user= -C php-fpm | sort -u o el propietario del directorio del sitio stat -c '%U' /path/to/monitor. A continuación, en todos los comandos de abajo, sustitúyalo por www-data. La instalación automática detecta por sí sola el usuario web y le asigna los sudoers.
Los datos no se muestran: casi siempre son permisos sudo sin definir. Compruebe el comando concreto en nombre del usuario web (sustituya www-data por el suyo). El indicador -n = sin contraseña, igual que PHP; si pide contraseña, es que no hay regla en sudoers:
sudo aa-status en el terminal muestra los perfiles, pero la página «AppArmor» indica «Inactivo»). Motivo: el usuario web no tiene permiso sudo para el comando de ese módulo. Compruébelo en la lista de arriba: si pide contraseña, añada la línea que falta a /etc/sudoers.d/monitor («Configuración de sudo»). Comandos «nuevos» habituales: /usr/sbin/aa-status (MAC), /usr/sbin/psad --Status (PSAD).
apache2ctl, ausearch, aa-status o ss, o el usuario web no esté en los grupos adm/systemd-journal (de ahí se leen los registros de fail2ban/auth/modsec y journalctl: Falco y eventos del kernel).
Síntoma: en el servidor hay datos (visibles por shell), pero la página muestra «no hay datos» o un estado incorrecto — por ejemplo AIDE indica «No inicializada» aunque la base ya está creada.
La causa es open_basedir: muchos paneles y alojamientos limitan el grupo de PHP-FPM al directorio del dominio, por lo que las funciones de PHP file_exists(), file_get_contents(), filemtime() quedan bloqueadas para las rutas del sistema (/var/lib/aide, /var/log, /proc…). El monitor lo evita leyendo esas rutas con comandos del sistema estándar (cat, test, stat).
open_basedir. La solución correcta es leer con comandos del sistema (ya implementado para AIDE y el Monitor de red). No es necesario ampliar open_basedir a /var, /proc y resulta menos seguro.
El monitor verifica los certificados conectándose a los dominios directamente por el puerto 443. Si el dominio no es accesible desde el propio servidor o el puerto está cerrado por el cortafuegos, la comprobación fallará.
/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) y Apache (/etc/apache2/sites-enabled/) más el host actual de HTTP_HOST.
El monitor se conecta a MySQL con un usuario de config.php que solo tiene acceso a su propia base. MySQL muestra en information_schema únicamente las bases con privilegios, por eso las demás no se ven.
Para que el monitor vea todas las BD, conceda a este usuario permiso solo de lectura (una vez como root; sustituya el nombre de usuario de config.php):
sudo mysql: la lista de bases se obtiene mediante su propia conexión PDO.
PostgreSQL requiere acceso a nivel del usuario postgres, del que el usuario web del panel carece. Abrir desde PHP un sudo psql amplio no es seguro; en su lugar, el panel invoca un envoltorio restringido sin parámetros que solo imprime la versión, el número de conexiones y la lista de bases con sus tamaños. Créelo:
monitor-pgstat de sudoers (paso 13 de la instalación manual) y no cree el script: la tarjeta de PostgreSQL simplemente quedará inactiva.
El panel muestra qué ocurre; a continuación, qué hacer en situaciones típicas. Principio general: no entrar en pánico, contrastar con la actividad legítima (sus acciones, actualizaciones, copias de seguridad) y reaccionar según la gravedad.
ignoreip./etc, fuera de /usr/share): posible sustitución. Verifique el paquete: debsums PACKAGE_NAME y, en caso de duda, reinstálelo (apt install --reinstall).127.0.0.1 o cierre el puerto en UFW. Es un agujero real.certbot renew o los ajustes en el panel).sudo apt update && sudo apt upgrade; tras actualizar el kernel, reinicie el servidor.