FAQ

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.

Por dónde empezar

01. Instalación del panel: elija un método

La instalación del panel está en páginas paso a paso independientes. Elija un método:

Si no está seguro, elija la automática. Esta guía sigue siendo la fuente única sobre SSL, herramientas, cron y diagnóstico: las páginas de instalación remiten a sus secciones sin duplicar nada.

Resumen

02. Qué es Arcivéo Monitor

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.

03. Cómo funciona el monitor en el servidor

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.

Para varios servidores, instale el monitor en cada uno por separado, con un dominio único.

04. Cómo se calcula la Puntuación de seguridad

La puntuación parte del máximo y baja por cada problema detectado:

  • UFW inactivo — −30
  • Fail2ban no iniciado (sin jail activas) — −25
  • Sin claves WebAuthn — −15
  • Lynis hardening index < 60 — −20; 60–79 — −10
  • IPset ipsum no cargado — −10
  • Amenazas detectadas por ClamAV — −20
  • Cambios de archivos en AIDE — −15
  • SSL caducado — −30, caduca en <14 días — −15, <30 días — −5
  • CrowdSec instalado, pero no iniciado — −5
  • Suricata instalada, pero no iniciada — −5
  • SGBD/caché (MySQL, PostgreSQL, Redis…) accesibles desde el exterior — −10
  • Acceso root por SSH permitido (PermitRootLogin yes) — −20
  • Actualizaciones de seguridad pendientes de instalar — −5

Resultado: 80+ = Protegido, 60–79 = Atención, <60 = En riesgo.

Las deducciones por ClamAV, AIDE, CrowdSec y Suricata solo se aplican si la herramienta está instalada. Lynis y AIDE sin la base inicializada se muestran como «sin datos» y no reducen la puntuación. El número de ataques de hoy se muestra en el panel, pero no afecta a la Puntuación de seguridad.

Ajustes y licencia

05. WebAuthn — autenticación de doble factor

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.

WebAuthn solo funciona a través de HTTPS. En una conexión HTTP no están disponibles el registro ni el inicio de sesión con clave.

06. Notificaciones: Telegram y Email

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:

  1. En Telegram escriba a @BotFather/newbot → obtendrá un token con el formato 123456:ABC....
  2. Escriba cualquier mensaje a su nuevo bot (para que pueda responderle).
  3. Averigüe su chat id: escriba al bot @userinfobot, o abra https://api.telegram.org/bot<TOKEN>/getUpdates y busque "chat":{"id":...}.
  4. Introduzca el token y el chat id en «Ajustes» → Telegram y pulse «Guardar y enviar prueba».

Email. Dos métodos a elegir en «Ajustes» → Email:

  • SMTP — host, puerto (465/SSL o 587/TLS), usuario y contraseña de su buzón de correo;
  • Resend — API moderna: indique la clave API (re_...) y un dominio de remitente verificado.
El botón «Enviar prueba» comprobará el canal al instante. La programación del informe automático se hace mediante cron (sección «Todas las tareas cron»): este dispara el envío y los canales se toman de los ajustes.

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.

07. Licencia — introducción y activación

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):

  1. Obtenga el código de activación. Área personal my.arciveo.com → sección «Licencias» / «Activación de licencia»: copie el código ARCIVEO-….
  2. Active el código para su dominio. En la misma área personal, abra «Activación de licencia» e introduzca: el código de activación, su correo electrónico y el dominio del panel (la dirección por la que se abre el monitor, p. ej. 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».
  3. Pegue la clave en el panel. Copie todo el texto de la licencia → en el panel abra «Ajustes» → bloque «Licencia», péguela y pulse «Guardar». Los módulos se desbloquean de inmediato.

El panel verifica la clave criptográficamente: la firma, la vinculación al dominio y la vigencia.

El dominio de la activación debe coincidir exactamente con la dirección del panel. Tómelo de la constante 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.
Si la vigencia expira o cambia el dominio, aparecerá una advertencia en la cabecera del panel. La licencia queda vinculada al dominio de forma permanente y no se transfiere a otro dominio: para un nuevo periodo o un nuevo dominio se necesita una clave nueva (se compra en el área personal y se activa una sola vez).

08. Archivo config.php — todos los ajustes del panel

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 --- define('DB_HOST', 'localhost'); // dejar define('DB_NAME', 'db_name'); // lo que definió al crear la BD define('DB_USER', 'user'); // lo que definió al crear la BD define('DB_PASS', 'db_password'); // lo que definió al crear la BD define('DB_CHARSET', 'utf8mb4'); // dejar // --- Aplicación --- define('APP_URL', 'https://monitor.example.com'); // dirección del panel, sin barra al final define('TIMEZONE', 'Europe/Madrid'); // su zona horaria // --- Tiempo de sesión --- define('SESSION_LIFETIME', 28800); // inactividad hasta un nuevo inicio de sesión, seg (28800 = 8 h)

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.

config.php es un archivo secreto. Contiene la contraseña de la BD. Se encuentra en la raíz del panel (junto a 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.
Al migrar o restaurar el acceso, este archivo es la principal fuente de credenciales: el nombre de la BD, el usuario y la contraseña se toman precisamente de aquí (véanse las secciones «Actualización y migración del panel» y «Restauración del acceso»).

Herramientas de seguridad

09. Cortafuegos UFW

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.

sudo apt install ufw # Permitir SSH (¡obligatorio ANTES de activar!) y web sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Cerrar la BD desde el exterior (acceso solo local) sudo ufw deny 3306 # Activar y comprobar sudo ufw enable sudo ufw status verbose
Antes de ufw enable, permita obligatoriamente SSH (ufw allow OpenSSH), de lo contrario perderá el acceso al servidor.
La «Exposición externa» del panel tiene en cuenta UFW: un puerto cerrado por una regla 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.

10. Instalación de Fail2ban

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.

sudo apt install fail2ban sudo systemctl enable --now fail2ban # Comprobar estado: sudo fail2ban-client status
La configuración de trabajo (jail.local con decenas de jails y baneo automático desde ipsum) está en la siguiente sección.

11. Configuración operativa de Fail2ban + ipsum

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:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # Baneo progresivo: cada repetición dura más bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # baneo permanente por fuerza bruta de SSH findtime = 3600 # Reincidentes: quien acumula varios baneos queda baneado para siempre [recidive] enabled = true logpath = /var/log/fail2ban.log banaction = %(banaction_allports)s bantime = -1 findtime = 86400 maxretry = 2 [http-get-dos] enabled = true maxretry = 100 findtime = 300 bantime = 1w # Servicios web (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … y el resto de jails por servicio (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
En 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»):

# 04:00 — actualización del ipset ipsum (level 1, máxima cobertura): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
El conjunto debe llamarse 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:

  • Ataques reales (reactivo) — todo lo que capturó fail2ban: intentos reales de intrusión (jails 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».
  • Bloqueo preventivo (proactivo) — la lista de bloqueo pública de IP maliciosas conocidas 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.

12. Lista de bloqueo IPset (ipsum)

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:

# Crear el conjunto (una sola vez): sudo ipset create ipsum hash:ip # Script de actualización /usr/local/bin/update-ipsum.sh: #!/bin/bash ipset flush ipsum for ip in $(curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/3.txt); do ipset add ipsum "$ip" 2>/dev/null done iptables -C INPUT -m set --match-set ipsum src -j DROP 2>/dev/null \ || iptables -I INPUT -m set --match-set ipsum src -j DROP # Cron (sudo crontab -e, a diario a las 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
La opción avanzada con fail2ban-recidive está en la sección «Configuración de trabajo Fail2ban + ipsum».
ipset vive en memoria y se pierde al reiniciar. Un simple cron diario dejará el conjunto vacío desde el reinicio hasta la siguiente ejecución (el panel mostrará 0). Cargue el conjunto también al arrancar: lleve la carga a un script y engánchelo a @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»):
# /usr/local/bin/load-ipsum.sh #!/bin/bash curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/1.txt \ | grep -v '^#' | sed 's/^/add ipsum /' \ | (echo "create ipsum hash:ip hashsize 131072 maxelem 300000 -exist"; echo "flush ipsum"; cat) \ | ipset restore -exist # sudo crontab -e — a diario a las 04:00 Y en cada arranque: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
Cómo funciona en la instalación automática. El script carga la lista completa level 1 (más de 100 000 IP) en el conjunto 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.

13. Instalación de CrowdSec

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.

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # Bouncer para iptables/nftables: sudo apt install crowdsec-firewall-bouncer-iptables # Comprobar el estado: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
El estado «No iniciado» en el panel = el servicio está instalado, pero no está activo (el monitor lo comprueba mediante 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).
«0 escenarios» o «0 bouncers» en el panel. CrowdSec viene casi vacío de fábrica: sin colecciones no detecta nada, y sin un bouncer registrado los bloqueos no se aplican al cortafuegos. Instale las colecciones básicas y asegúrese de que el bouncer aparece en la lista:
# Colecciones básicas (Linux + SSH + servidor web): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # El bouncer debe aparecer en la lista y con estado de conexión activa: sudo cscli bouncers list
En el registro del bouncer aparece 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:
sudo cscli bouncers add fw-bouncer # generará una nueva api_key # escriba esta clave en api_key: en /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml sudo systemctl restart crowdsec-firewall-bouncer
La instalación automática (perfil «Protección completa») instala las colecciones y registra el firewall-bouncer por sí sola; hacerlo a mano solo es necesario en una instalación manual o tras una intervención manual en CrowdSec.

14. Instalación de AIDE

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).

sudo apt install aide # Inicialización de la base (5–15 minutos): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04: el directorio /var/lib/aide se crea en modo 700 (propietario _aide), # y el panel (www-data) no ve la base → muestra «No inicializada». # Abrir el directorio para el paso (los archivos de la base quedan en 600): sudo chmod 755 /var/lib/aide # Primera comprobación CON ESCRITURA en el registro que lee el monitor. # En Ubuntu/Debian aide requiere un --config explícito (si no, «missing configuration»; # el binario aide.wrapper no se incluye en las versiones nuevas): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Durante 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.
Error de 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:
sudo mv /etc/aide/aide.conf.d/21_aide_spamassassin /etc/aide/21_aide_spamassassin.disabled sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
Estados en el panel. «No inicializada» = el panel no ve el archivo de la base: o bien 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.
Comprobación periódica → registro para el panel. El /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:
# sudo crontab -e — comprobación diaria a las 02:00: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # Ejecutar ahora, sin esperar a la programación: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
La instalación automática ya hace todo esto: chmod 755 /var/lib/aide y el cron de comprobación a las 02:00 — no hace falta nada manual.
Realice la primera inicialización en un servidor limpio — antes de instalar aplicaciones web. Tras cambios legítimos, vuelva a crear la base: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.

15. Instalación de ClamAV

Escáner antivirus para Linux. Especialmente útil para revisar /var/www en busca de shells PHP y código malicioso.

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # Actualizar la base de firmas: sudo freshclam # Escanear una carpeta manualmente: sudo clamscan -r /var/www --infected
¿El demonio clamd muestra «Inactivo» tras enable --now? Tres causas típicas:

1. En la configuración quedó la línea Example: clamd se niega a iniciar mientras esté presente:

sudo sed -i '/^Example/d' /etc/clamav/clamd.conf sudo systemctl restart clamav-daemon

2. No se ha descargado la base de firmas: clamd no arranca sin ella:

sudo systemctl stop clamav-freshclam sudo freshclam sudo systemctl start clamav-freshclam sudo systemctl restart clamav-daemon

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).

Diagnóstico: sudo journalctl -u clamav-daemon -n 30 --no-pager.
¿En el panel aparece «Archivos analizados: 0» / «Último análisis: —»? El demonio 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.

16. Instalación de Linux Malware Detect (maldet)

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.

wget https://www.rfxn.com/downloads/maldetect-current.tar.gz tar -xzf maldetect-current.tar.gz dir=$(ls -d maldetect-*/ | head -1) && cd "$dir" && sudo bash install.sh && cd ~ rm -rf maldetect-* maldetect-current.tar.gz # Actualizar firmas: sudo maldet -u # Escanear /var/www: sudo maldet -a /var/www
LMD y ClamAV funcionan bien juntos. Último informe: maldet --report.
Durante la instalación puede aparecer la línea 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.
¿En la página LMD aparece como «No instalada» aunque está instalado? maldet no se instala mediante 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».

17. Instalación de Suricata

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).

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # Descargar las reglas actualizadas: sudo suricata-update sudo systemctl enable --now suricata
¿Suricata está «Activo» pero el panel no muestra alertas / el número de eventos es 0? Suricata escribe en /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:
sudo chmod o+rx /var/log/suricata
La instalación automática lo hace por usted; no es necesario hacerlo manualmente.

18. Instalación de Falco

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.

curl -fsSL https://falco.org/repo/falcosecurity-packages.asc > /tmp/falco.asc gpg --dearmor < /tmp/falco.asc | sudo tee /usr/share/keyrings/falco-archive-keyring.gpg > /dev/null sudo chmod 644 /usr/share/keyrings/falco-archive-keyring.gpg && rm /tmp/falco.asc echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" \ | sudo tee /etc/apt/sources.list.d/falcosecurity.list sudo apt update && sudo apt install falco sudo systemctl enable --now falco
El monitor lee los eventos de Falco mediante 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.
«0 eventos en 24 horas» es lo normal, no un error. Falco es event-driven: permanece en silencio mientras todo va bien y solo registra un evento ante una anomalía (shell desde un proceso web, lectura de /etc/passwd, escritura en directorios del sistema). Cero eventos críticos en un día en un servidor tranquilo es un estado saludable.
Para el panel es más fiable la salida a archivo. La lectura mediante 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.

19. Instalación de ModSecurity (WAF)

ModSecurity — cortafuegos web (WAF) para Apache o Nginx. Bloquea ataques a nivel de aplicación: inyecciones SQL, XSS, salto de rutas, escáneres.

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # Conjunto de reglas OWASP Core Rule Set: sudo apt install modsecurity-crs # OBLIGATORIO: sin este archivo el motor de reglas está desactivado sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf sudo sed -i 's/^SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf sudo apache2ctl configtest && sudo systemctl reload apache2 # Comprobación: debe devolver 403 curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
Instalar el paquete por sí solo no protege nada. Apache incluye las configuraciones con la línea 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:

sudo tee /usr/local/bin/monitor-modsec >/dev/null <<'EOF' #!/bin/sh echo "ENGINE=$(grep -hE '^SecRuleEngine[[:space:]]+' /etc/modsecurity/*.conf 2>/dev/null | tail -1 | awk '{print $2}')" echo "---LOG---" tail -n 3000 /var/log/apache2/modsec_audit.log 2>/dev/null echo "---RULES---" for f in /etc/modsecurity/crs/rules/*.conf /usr/share/modsecurity-crs/rules/*.conf /etc/modsecurity/custom-rules.conf; do [ -f "$f" ] && { echo "===FILE:$(basename "$f")==="; cat "$f"; } done EOF sudo chown root:root /usr/local/bin/monitor-modsec sudo chmod 755 /usr/local/bin/monitor-modsec # en /etc/sudoers.d/monitor (usuario = aquel con el que se ejecuta PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
El envoltorio toma la última directiva 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.
El usuario en sudoers debe coincidir con el usuario del pool FPM: en un Apache/Debian normal es 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.
Si el sitio está detrás de un proxy Nginx (HestiaCP), Apache ve como cliente al propio proxy; el panel toma la IP real del atacante de la cabecera 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.
El bloque ---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.

20. Instalación de Auditd

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.

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # Comprobar el estado y los eventos: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
El monitor lee los eventos mediante ausearch (/usr/sbin/ausearch) y, si es necesario, desde /var/log/audit/audit.log con el comando tail. Ambos deben estar en sudoers.

21. Instalación de Monit

Supervisa los servicios (nginx, php-fpm, mysql, etc.) y los reinicia cuando fallan. Puede enviar alertas por email.

sudo apt install monit sudo systemctl enable --now monit # Configuraciones: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
El monitor obtiene la lista de servicios mediante 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.
¿En el panel aparece «0 servicios bajo supervisión»? Hay dos causas. (1) La interfaz HTTP está desactivada: en 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:
# /etc/monit/conf.d/00-httpd — interfaz HTTP para localhost: set httpd port 2812 use address localhost allow localhost # ejemplos de estrofas check (qué supervisar): check process sshd with pidfile /run/sshd.pid start program = "/usr/bin/systemctl start ssh" stop program = "/usr/bin/systemctl stop ssh" check filesystem rootfs with path / if space usage > 90% then alert
sudo monit -t # comprobar la sintaxis (Control file syntax OK) sudo systemctl reload monit sudo monit status
La instalación automática coloca un conf.d listo con httpd en 2812 y un conjunto de comprobaciones: en una instalación nueva no hay que configurar nada manualmente.
¿El servicio está en estado «Con errores»? El monitor solo muestra el estado y deliberadamente no reinicia los servicios desde el panel web (sería ejecución remota de comandos root en un panel de seguridad). El diagnóstico y el reinicio se hacen por SSH mediante Monit:
sudo monit status <service> # causa del error sudo monit restart <service> # reinicio mediante Monit # si Monit no levanta el servicio, revise su propia unidad: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

22. Instalación de PSAD (detección de escaneo de puertos)

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.

sudo apt install psad # PSAD lee el registro de iptables — hay que activar el registro (UFW lo hace por sí mismo). # Para iptables puro, añada reglas LOG a las cadenas INPUT/FORWARD. sudo psad --sig-update sudo systemctl enable --now psad
El monitor lee los datos mediante psad --Status (necesario en sudoers). Sin el registro de iptables la página estará vacía — es normal mientras no haya habido escaneos.

23. AppArmor / SELinux (control de acceso)

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).

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # comprobar perfiles
El monitor lee el estado mediante 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.

sudo aa-status | grep -E "profiles are" # desglose por modos sudo aa-enforce /etc/apparmor.d/profile-name # pasar el perfil a enforce
Pasar a enforce los perfiles que Ubuntu dejó deliberadamente en 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.

24. Instalación de debsums (integridad de paquetes)

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.

sudo apt install debsums # Línea de cron (diariamente a las 4:30): 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

El script debsums-scan.sh encuentra por sí mismo la carpeta data/ del panel: no hace falta indicar la ruta.

Los cambios en /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.

25. Configuración de informes de Lynis

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.

# Ejecución única (indique su propia ruta a la raíz del panel): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # Auditoría diaria — línea de cron (envoltorio lynis-scan.sh listo en /usr/local/bin/, ver resumen): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
Tras la primera ejecución, la página «Auditoría de Lynis» mostrará de inmediato el hardening index, las advertencias y las recomendaciones.
Botón «Ejecutar auditoría» en la página de Lynis. Ejecuta 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:
u=$(sudo awk '/NOPASSWD/ && !/lynis-scan/ {print $1; exit}' /etc/sudoers.d/monitor) [ -n "$u" ] && echo "$u ALL=(ALL) NOPASSWD: /usr/local/bin/lynis-scan.sh" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c && sudo chmod 440 /etc/sudoers.d/monitor

26. Configuración de informes de Logwatch

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.

# Diariamente (6:00) — línea de cron (envoltorio listo logwatch_daily.sh en /usr/local/bin/, ver resumen): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

Módulos del panel

27. Monitor de red (integrado)

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:

  • interfaces y tráfico — desde /proc/net/dev;
  • estado de los enlaces (UP/DOWN) e IP — mediante ip;
  • conexiones y puertos de escucha — mediante ss;
  • eventos de red del kernel en las últimas 24 h — mediante 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:

# Comprobación como www-data (con él funciona PHP): sudo -u www-data bash -c 'cat /proc/net/dev' sudo -u www-data bash -c 'ip -o link show' sudo -u www-data bash -c 'ss -s' sudo -u www-data journalctl -k --no-pager -n 5
El bloque «Eventos de red del kernel» muestra eventos de la pila de red del kernel (cambio de enlace up/down, errores de portadora, «network unreachable»). Los registros del cortafuegos 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.

28. Disco y SMART

La página integrada muestra tres cosas:

  • Sistemas de archivos — ocupación de las particiones (df); la barra se pone roja al ≥90%;
  • Unidades de almacenamiento — lista de discos (lsblk), solo los reales (loop/snap se ocultan);
  • Salud (SMART) — estado del disco y atributos (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.

sudo apt install smartmontools # Línea de cron (cada 30 minutos): */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

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.

En discos virtuales (QEMU/KVM y similares) normalmente solo está disponible el estado general «salud: OK», mientras que la temperatura, las horas de funcionamiento y los sectores reasignados pueden aparecer vacíos — es normal. En un servidor físico se muestran todos los atributos.

29. Rendimiento (CPU/RAM/Red/Disco)

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.

# Línea de cron (cada 5 minutos): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

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.

Hasta que el recopilador no se ejecute al menos dos veces (los primeros ~10 minutos tras la instalación), la página muestra «recopilando datos»: los gráficos necesitan como mínimo un par de puntos contiguos para calcular velocidades y porcentajes.

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».

Los umbrales los comprueba el mismo 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.

30. Mapa de ataques (GeoIP)

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:

sudo apt install geoip-bin geoip-database # Comprobación: geoiplookup 8.8.8.8
No se requiere sudo: la base /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.

31. Exposición externa, actualizaciones y actualizaciones automáticas

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.

Si la tarjeta está en rojo, cierre la base de datos al exterior: vincúlela a 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.
«Puerto abierto» ≠ «accesible desde fuera». Un servicio que escucha en 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.
Lynis MAIL-8818 manualmente (si instaló el correo usted mismo): en /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».

# Instalar actualizaciones: sudo apt update && sudo apt upgrade # Comprobar qué escucha hacia fuera: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
La tarjeta de actualizaciones funciona en Ubuntu/Debian (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.

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # activar # Comprobar que está activado: apt-config dump | grep Unattended-Upgrade

Mantenimiento

32. Copias de seguridad

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:

# cron de root (sudo crontab -e) — copia diaria a las 2:30 (ponga sus propios nombres/rutas): 30 2 * * * mysqldump -u root MY_DB | gzip > /var/backups/monitor-db-$(date +\%F).sql.gz 40 2 * * * tar czf /var/backups/monitor-data-$(date +\%F).tar.gz -C /path/to/monitor data # Eliminar archivos con más de 14 días: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
Una copia en el mismo servidor protege de errores, pero no de la pérdida del servidor. Copie los archivos en un almacenamiento externo (otro servidor, S3, rclone a la nube). Compruebe que la restauración funciona de verdad.

33. Actualización y migración 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:

  • sobrescribir (código): 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;
  • no tocar: config.php (datos de la BD), data/ (informes), logs/, tmp/ (sesiones y caché).
# Tras la subida, vaciar la caché de PHP (si opcache está activado): sudo systemctl reload php*-fpm
FileZilla muestra SSH_FX_PERMISSION_DENIEDPermission 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.
# Variante A (recomendada): separar los propietarios: el código es suyo, las carpetas de trabajo del servidor web. # El servidor web no obtiene ningún permiso de escritura sobre el CÓDIGO del panel: sudo chown -R deploy:www-data /path/to/monitor sudo chown -R www-data:www-data /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo find /path/to/monitor -type d -exec chmod 755 {} \; sudo find /path/to/monitor -type f -exec chmod 644 {} \; sudo chmod 750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo chmod 640 /path/to/monitor/config.php # Variante B: ACL sobre los propietarios actuales (no cambiamos nada): sudo apt install -y acl sudo setfacl -R -m u:deploy:rwX /path/to/monitor sudo setfacl -R -d -m u:deploy:rwX /path/to/monitor # Variante C: mediante el grupo www-data. Más sencilla, pero el permiso de escritura # sobre los archivos del panel lo obtiene también el servidor web (código sustituible): sudo usermod -aG www-data deploy sudo find /path/to/monitor -type d -exec chmod 2775 {} \; sudo find /path/to/monitor -type f -exec chmod 664 {} \; sudo chmod 640 /path/to/monitor/config.php sudo chmod 2750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs
Por qué la variante A es segura. El panel escribe solo en tres directorios — 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.
La trampa de la variante B: cualquier 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.
El bit 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:

  1. En el nuevo servidor, ponga en marcha el sitio + HTTPS (consulte la página de instalación manual).
  2. Copie todos los archivos del panel junto con config.php, data/.
  3. Migre la BD: mysqldump en el antiguo → importar en el nuevo; corrija los datos de la BD en config.php.
  4. Repita en el nuevo servidor: sudoers, pertenencia al grupo adm, tareas de cron.
  5. La licencia está vinculada al dominio: si el dominio es el mismo, la clave seguirá funcionando.

34. Recuperación de acceso (clave perdida, contraseña, bloqueo de IP)

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):

sudo mysql MY_DB

Clave WebAuthn perdida (no supera el segundo factor): desactive el 2FA, inicie sesión con la contraseña y registre una nueva clave:

UPDATE users SET webauthn_enabled = 0;

Olvidó la contraseña: establezca un nuevo hash (genérelo en el servidor e insértelo):

# Generar el hash de la nueva contraseña: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # En la BD (inserte el hash obtenido): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

Se bloqueó a sí mismo con el filtro de IP: desactive la restricción:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
Siempre tiene acceso a la BD: 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.

35. Todas las tareas de cron en un solo lugar

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.

# Cron del servidor del monitor (root) — añádalo con: sudo crontab -e # 01:30 — análisis de ClamAV en rutas peligrosas (web, home, temp) → tarjetas «Archivos analizados» y «Último análisis» 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — comprobación de integridad de archivos AIDE (requiere --config explícito) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # al arrancar — restaurar permisos de /var/lib/aide (el archivo tmpfiles del paquete # aide-common.conf los deja en 0700 y el panel deja de ver la base) @reboot chmod 755 /var/lib/aide # 03:00 — auditoría de seguridad de Lynis 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — actualización de la lista de bloqueo ipsum (level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # el conjunto ipsum al arrancar lo levanta el servicio ipsum-load.service (ANTES del cortafuegos, de lo contrario # UFW no verá el conjunto en before.rules) — no es cron. Aquí solo el refresco diario de arriba. # 06:00 — informe de Logwatch 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # cada 30 min — comprobación SMART de discos */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — integridad de paquetes debsums 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — informe programado por Email y Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # cada hora — actualización de las listas de paquetes (para la tarjeta «Actualizaciones de seguridad») 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # cada 5 min — instantánea de recursos (CPU/RAM/red/disco) para la página «Rendimiento» */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Los detalles de cada uno están en las secciones correspondientes. Las tareas de copia de seguridad (sección anterior) se añaden a este mismo cron. Tras los cambios, verifique: sudo crontab -l y que el servicio cron esté activo.
La hora de cron = la zona horaria del servidor, no la 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:
# Comprobar la zona actual del servidor: timedatectl # Establecer su zona (ejemplo) y reiniciar cron: sudo timedatectl set-timezone Europe/Madrid sudo systemctl restart cron
Después de esto, la línea 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.
Scripts envoltorio listos para usar. Sus copias de trabajo y un crontab de ejemplo (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.
La ruta al script en el crontab debe coincidir con el lugar donde lo colocó.
Cómo colocar el script en /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:
# en FileZilla: en el campo «Sitio remoto» escriba /tmp y suba ahí el script, # luego por SSH (install pone de una vez el propietario y los permisos, no hacen falta chown/chmod): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # comprobación: el archivo está en su sitio, permisos rwxr-xr-x, sintaxis intacta bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
No confunda los directorios: hace falta /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.
¿Instaló el servidor con la configuración automática? Estos envoltorios y sus tareas de cron ya están instalados por el script (en /usr/local/bin/, registro — /var/log/arciveo-cron.log) — no hace falta hacer nada manualmente.
Dónde buscan los scripts el panel. Los envoltorios son neutrales respecto al dominio: localizan las instalaciones del panel recorriendo /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.
cron.log y permisos de acceso. El archivo 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:
# crear de antemano con el usuario web (antes de añadir las líneas de cron): sudo -u OWNER touch /path/to/monitor/logs/cron.log # si cron.log ya lo creó el cron root — cederlo al usuario web: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
Para averiguar el propietario del directorio: stat -c %U /path/to/monitor.
Gestión desde el panel. En la sección «Sistema» hay una página «Crontab» — puede ver y añadir tareas sin SSH. El panel edita solo las tareas añadidas a través de él mismo (un bloque aparte en el crontab root, marcado con comentarios de servicio); todo lo que ya está en el crontab (la lista de arriba) se muestra ahí en modo de solo lectura en la lista «Otras tareas del servidor» con el botón «Copiar al editor» — este solo traslada la programación/comando al formulario de adición, sin tocar la línea original. Para «pasar» una tarea existente a la gestión del panel, cópiela al editor, guárdela y luego elimine la línea antigua manualmente (sudo crontab -e), de lo contrario se ejecutaría dos veces.
Configuración única en el servidor. La página necesita un script envoltorio con privilegios — no un 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:
sudo install -m 0755 -o root -g root /dev/stdin /usr/local/sbin/arciveo-cron <<'ARCIVEO_CRON_EOF' #!/bin/bash set -euo pipefail BEGIN='# >>> ARCIVEO-CRON-MANAGED (edited from the panel; do not edit by hand) >>>' END='# <<< ARCIVEO-CRON-MANAGED <<<' cmd=${1:-} case "$cmd" in list) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} list" >&2; exit 2; } crontab -l -u root 2>/dev/null || true ;; set) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} set (body on stdin)" >&2; exit 2; } body=$(cat) if grep -qF "$BEGIN" <<<"$body" || grep -qF "$END" <<<"$body"; then echo "invalid body: markers not allowed" >&2; exit 2 fi current=$(crontab -l -u root 2>/dev/null || true) tmp=$(mktemp); trap 'rm -f "$tmp"' EXIT { if grep -qF "$BEGIN" <<<"$current"; then awk -v b="$BEGIN" '{print} $0==b{exit}' <<<"$current" else [ -n "$current" ] && printf '%s\n' "$current" echo "$BEGIN" fi printf '%s\n' "$body" echo "$END" if grep -qF "$END" <<<"$current"; then awk -v e="$END" 'f{print} $0==e{f=1}' <<<"$current" fi } > "$tmp" crontab -u root "$tmp" ;; *) echo "usage: ${0##*/} <list|set>" >&2; exit 2 ;; esac ARCIVEO_CRON_EOF echo "www-data ALL=(ALL) NOPASSWD: /usr/local/sbin/arciveo-cron" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c
El usuario web puede diferir de 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.
El archivo nuevo se subió con un propietario incorrecto — la página responde «Access denied.». Si el archivo 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:
ls -la public/crontab_monitor.php public/ssl_monitor.php sudo chown OWNER:OWNER public/crontab_monitor.php sudo chmod 644 public/crontab_monitor.php

Diagnóstico

36. La herramienta está instalada, pero muestra «No instalada»

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.

# Comprobar con dpkg: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # Buscar la ruta del binario: which ufw fail2ban-client auditctl # Prueba de sudo como www-data: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

37. Solución de problemas (500, sin datos)

Error 500: revise los registros de PHP, nginx y del propio monitor:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # Registros del monitor: tail -50 logs/monitor_$(date +%Y-%m-%d).log # Permisos de las carpetas: ls -la data/ tmp/ logs/
¿El panel está en un panel de hosting (HestiaCP, ISPmanager, cPanel)? Allí PHP no se ejecuta bajo 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 -u www-data sudo -n fail2ban-client status sudo -u www-data sudo -n ufw status verbose sudo -u www-data sudo -n ipset list -t ipsum sudo -u www-data sudo -n /usr/sbin/ausearch -m USER_LOGIN -ts today sudo -u www-data sudo -n /usr/sbin/aa-status sudo -u www-data sudo -n /usr/sbin/psad --Status sudo -u www-data journalctl -u falco --no-pager -n 5
El módulo muestra «Inactivo» / «0» aunque la herramienta funciona (por ejemplo, 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).
Si una página concreta (Falco, ModSecurity, Auditd, puertos abiertos de UFW) está vacía, contrástela con la lista de la sección sobre sudo: probablemente no esté permitido 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).

38. La página está vacía aunque hay datos en el servidor

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).

# ¿Se ve el archivo por shell (así lo lee el monitor)?: sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # Valor actual de open_basedir para el grupo del dominio: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
Si el shell «ve» el archivo (VISIBLE) pero la página no — se trata de 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.

39. La página SSL no funciona

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á.

# Verificar el certificado manualmente: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # Comprobar la disponibilidad: curl -I https://monitor.example.com
El monitor obtiene los dominios automáticamente de las configuraciones de Nginx (/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) y Apache (/etc/apache2/sites-enabled/) más el host actual de HTTP_HOST.
Detección automática de subdominios. Los subdominios se detectan automáticamente a partir de los registros públicos de Certificate Transparency y se verifican por red, incluso si están alojados en otros servidores. No es necesario añadir nada manualmente.

40. Solo se ve una BD de varias

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 -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* otorga solo permiso de lectura: no permite modificar, eliminar ni crear nada, es seguro para la monitorización.
Sin este GRANT el panel solo ve su propia base; no es un error, sino una limitación de permisos. El panel no usa ningún sudo mysql: la lista de bases se obtiene mediante su propia conexión PDO.

41. PostgreSQL no aparece en la página «Base de datos»

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:

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo Monitor - read-only PostgreSQL version, connections and per-database size sudo -u postgres psql -tAc "SELECT version();" | grep -oE 'PostgreSQL [0-9.]+' echo "---" sudo -u postgres psql -tAc "SELECT count(*) FROM pg_stat_activity;" echo "---" sudo -u postgres psql -tAc "SELECT datname || '|' || pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE datistemplate = false;" EOF sudo chown root:root /usr/local/bin/monitor-pgstat sudo chmod 755 /usr/local/bin/monitor-pgstat # en /etc/sudoers.d/monitor (usuario = aquel con el que se ejecuta PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
Si no utiliza PostgreSQL, elimine la línea monitor-pgstat de sudoers (paso 13 de la instalación manual) y no cree el script: la tarjeta de PostgreSQL simplemente quedará inactiva.

42. Se activó una alerta: qué hacer

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.

  • Mapa de ataques / muchos baneos de fail2ban: es normal en cualquier servidor conectado a internet (los bots prueban SSH/web sin parar). Lo importante es que los baneos se activen. Asegúrese de que el acceso por SSH sea solo con clave (contraseña desactivada) y de que su IP esté en ignoreip.
  • ModSecurity bloqueó peticiones: el WAF repele ataques al sitio, es su función. Si bloquea su tráfico legítimo (falso positivo), busque el rule id en los detalles y añada una excepción a la configuración de CRS.
  • AIDE: archivos modificados: contraste la lista con lo que usted hizo (actualizar paquetes, editar configuraciones es normal). Los cambios en binarios del sistema que usted no tocó son motivo de alerta. Tras cambios legítimos, actualice la base de AIDE.
  • debsums: binarios/bibliotecas modificados (fuera de /etc, fuera de /usr/share): posible sustitución. Verifique el paquete: debsums PACKAGE_NAME y, en caso de duda, reinstálelo (apt install --reinstall).
  • ClamAV / maldet: amenaza detectada: revise el archivo en cuarentena, no lo abra. Si es un web shell en el directorio del sitio, aísle el servidor y busque el punto de entrada (plugin vulnerable, fuga de accesos).
  • Falco: eventos críticos (lanzamiento de shell en un contenedor, acceso a archivos sensibles): analice el evento: de quién es el proceso, qué lo lanzó. A menudo es actividad legítima de administración.
  • Exposición externa: SGBD/caché en rojo: ciérrelos de inmediato: vincule el servicio a 127.0.0.1 o cierre el puerto en UFW. Es un agujero real.
  • SSL caduca / caducado: renueve el certificado (Let's Encrypt se renueva solo; si no, revise certbot renew o los ajustes en el panel).
  • Actualizaciones de seguridad pendientes: instálelas: sudo apt update && sudo apt upgrade; tras actualizar el kernel, reinicie el servidor.
Indicios de una intrusión real (procesos/usuarios desconocidos, binarios modificados, spam saliente, tareas cron desconocidas): desconecte el servidor del acceso externo, haga una copia de seguridad para análisis y, si los datos son críticos, levante un servidor limpio a partir de una copia de seguridad de confianza: eliminar un rootkit de forma fiable es difícil.
Arcivéo - Security Monitor © 2026