Instalación manual

Instalación totalmente manual: desde un VPS recién adquirido hasta un panel operativo, paso a paso. Aquí se detallan la preparación del servidor, la creación del sitio, la base de datos, config.php y SSL. Los comandos de cada herramienta de seguridad y de cron están en el manual de preguntas frecuentes, con enlaces sobre la marcha.

Regla fundamental: al cambiar SSH o el cortafuegos, no cierre la conexión actual hasta comprobar la nueva en una ventana aparte. Si aun así pierde el acceso, casi todos los proveedores ofrecen una consola de emergencia (VNC/Recovery) en su panel de control.

01. Compré un VPS con Ubuntu/Debian: por dónde empezar

Tras la compra, el proveedor envía: la dirección IP, el nombre de usuario (normalmente root) y la contraseña (o una clave SSH). Es suficiente para iniciar sesión. Orden de las acciones (cada paso es una sección más abajo):

  1. Conéctese al servidor por SSH;
  2. Actualice el sistema, defina el nombre de host y la zona horaria;
  3. Cree un usuario normal con permisos sudo (no trabaje como root);
  4. Configure el acceso mediante clave SSH y desactive el acceso por contraseña;
  5. Active el cortafuegos y la autoprotección;
  6. (opcional) instale el panel de control HestiaCP: servidor web, BD y correo «listos para usar».

02. Primera conexión por SSH

SSH es un terminal seguro hacia el servidor. Ponga su propia IP en lugar de 203.0.113.10.

203.0.113.10 es un ejemplo, una dirección inexistente (reservada para documentación). No la introduzca tal cual: sustitúyala por la IP real de su servidor indicada en el correo del proveedor. De lo contrario, no habrá conexión.

Windows 10/11: abra PowerShell o «Terminal» y use el ssh integrado (o los clientes PuTTY / MobaXterm).
macOS / Linux: abra «Terminal».

# Acceso como root (la contraseña la envió el proveedor): ssh root@203.0.113.10 # Si el proveedor le dio un archivo de clave en vez de contraseña: ssh -i ruta/a/la/clave root@203.0.113.10
En la primera conexión, SSH preguntará por la «authenticity of host»: escriba yes. La contraseña no se muestra al escribirla (es normal). Si el proveedor le dio una contraseña temporal, cámbiela con el comando passwd.

03. Actualización del sistema y configuración básica

Lo primero: actualizar todos los paquetes y definir el nombre de host y la zona horaria.

# Actualizar el sistema: apt update && apt upgrade -y # Utilidades básicas: apt install -y curl wget ufw fail2ban unattended-upgrades # Zona horaria (ejemplo) y nombre de host: timedatectl set-timezone Europe/Madrid hostnamectl set-hostname myserver # Actualizaciones de seguridad automáticas: dpkg-reconfigure -plow unattended-upgrades
Lista de zonas horarias: timedatectl list-timezones. Si al final de la actualización aparece una ventana azul «Daemons using outdated libraries», marque todos los servicios (Espacio) y pulse OK; es seguro.

04. Crear un usuario con sudo

Trabajar siempre como root no es seguro. Cree un usuario normal y otórguele permisos de sudo (ejecutar comandos de administrador cuando sea necesario). Sustituya deploy por cualquier nombre.

# Crear el usuario (definirá la contraseña y pedirá datos — puede pulsar Enter): adduser deploy # Añadir al grupo sudo: usermod -aG sudo deploy # Comprobar (como root): su - deploy sudo whoami # debe mostrar: root exit
A partir de ahora inicie sesión en el servidor con este usuario: ssh deploy@203.0.113.10, y ejecute los comandos de administrador con el prefijo sudo.

05. Claves SSH y desactivación del acceso por contraseña

El acceso por clave es más seguro que la contraseña: una contraseña se puede adivinar, una clave prácticamente no. Primero creamos la clave en su propio equipo, la copiamos al servidor, comprobamos el acceso y solo después desactivamos la contraseña.

Paso 1. Crear la clave en su propio equipo (Windows PowerShell / macOS / Linux):

ssh-keygen -t ed25519 -C "my-laptop" # Enter en todas las preguntas (la clave se guardará en ~/.ssh/id_ed25519)

Paso 2. Copiar la clave pública al servidor:

# macOS / Linux: ssh-copy-id deploy@203.0.113.10 # Windows (PowerShell): type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh deploy@203.0.113.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

Paso 3. Comprobar el acceso por clave en una nueva ventana — debe permitir la entrada sin contraseña:

ssh deploy@203.0.113.10
No ejecute la Opción B hasta que el acceso por clave esté comprobado y funcione (Pasos 1–3), y no cierre la sesión de trabajo. Desactiva el acceso por contraseña para todos los usuarios, incluido root. Sin una clave que funcione, perderá por completo el acceso al servidor y solo podrá recuperarlo desde la consola del alojamiento. Si no tiene clave, use la Opción A.

Paso 4. Reforzar el acceso por SSH. Los ajustes van en un archivo separado, no tocamos la configuración principal. Elija la opción según su situación:

Opción A — solo cerrar root, dejar la contraseña. No hace falta clave, no perderá el acceso:

echo 'PermitRootLogin no' | sudo tee /etc/ssh/sshd_config.d/00-hardening.conf >/dev/null sudo systemctl restart ssh

Opción B — refuerzo completo. Desactivar el acceso por contraseña y dejar root solo por clave. Ejecútela solo tras confirmar que el acceso por clave funciona:

sudo tee /etc/ssh/sshd_config.d/00-hardening.conf >/dev/null <<'EOF' PubkeyAuthentication yes PasswordAuthentication no PermitRootLogin prohibit-password KbdInteractiveAuthentication no EOF sudo systemctl restart ssh
En ambas opciones se cierra root por contraseña. PermitRootLogin no prohíbe root por completo, prohibit-password deja el acceso solo por clave (para administrar, entre como deploy y use sudo). Si desea cambiar el puerto SSH, añada la línea Port 2222, pero primero abra el nuevo puerto en el cortafuegos (siguiente sección) y compruebe el acceso, de lo contrario se cerrará el acceso a sí mismo.

06. Cortafuegos básico y autoprotección

Cierre todo lo innecesario con el cortafuegos y active fail2ban (banea los intentos de adivinar contraseñas por SSH). Primero permita SSH, de lo contrario perderá el acceso tras activar UFW.

# Permitir SSH (o su puerto, si lo cambió) y web: sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Activar el cortafuegos: sudo ufw enable sudo ufw status verbose # fail2ban — protección de SSH contra fuerza bruta (el perfil básico se activa de inmediato): sudo systemctl enable --now fail2ban sudo fail2ban-client status sshd
Esto es lo mínimo. Los ajustes operativos de fail2ban, la lista de bloqueo ipsum, el UFW ampliado y las demás herramientas están en el grupo «Herramientas de seguridad» de la guía. El propio panel de Arcivéo Monitor mostrará claramente el estado de todo esto.

07. Instalación del panel HestiaCP (opcional)

HestiaCP es un panel de control de hosting gratuito: instala y configura el servidor web (nginx + apache), PHP, la base de datos (MariaDB), el correo, DNS y certificados SSL, y ofrece una interfaz web para los sitios. Resulta cómodo si no desea configurarlo todo manualmente y tiene previsto alojar sitios (incluido el propio panel Arcivéo Monitor).

Instale HestiaCP en un servidor limpio (una versión reciente y compatible de Ubuntu/Debian, con un mínimo de ~1–2 GB de RAM), antes de instalar otros servidores web y bases de datos; de lo contrario habrá conflictos. La instalación tarda entre 10 y 20 minutos y reinicia el servidor.
# Descargar el instalador y ejecutarlo: wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh sudo bash hst-install.sh

El instalador le pedirá un correo y el nombre de host, y luego instalará toda la pila. Tras el reinicio, el panel estará disponible en https://YOUR_IP:8083 (el instalador mostrará el usuario y la contraseña al final).

HestiaCP gestiona por sí mismo UFW y fail2ban, por lo que no es necesario configurarlos aparte; los detectará automáticamente. Aun así, configure las claves SSH y desactive la contraseña (sección anterior).

08. Requisitos del sistema e ionCube

El panel es una aplicación PHP sobre una pila LAMP/LEMP típica:

  • SO: Linux (se recomiendan Ubuntu/Debian);
  • Servidor web: nginx o Apache con PHP-FPM;
  • PHP 8.0+ con las extensiones: pdo_mysql, openssl, curl, json, mbstring;
  • ionCube Loader — extensión de PHP necesaria para el funcionamiento del panel;
  • BD: MySQL 5.7+ o MariaDB 10.3+;
  • HTTPS — obligatorio (el inicio de sesión y WebAuthn solo funcionan por https);
  • sudo para el usuario del servidor web (conjunto reducido — paso 13).
# Comprobar la versión de PHP y las extensiones: php -v php -m | grep -iE 'pdo_mysql|openssl|curl|mbstring|ioncube'

Instalación de ionCube Loader (si aún no está instalado). En un hosting con panel (HestiaCP, cPanel), ionCube se activa con una casilla en los ajustes de PHP. Manualmente en Ubuntu/Debian:

# Averiguar la versión de PHP y el directorio de extensiones: php -v EXTDIR=$(php -r 'echo ini_get("extension_dir");'); PHPVER=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') # Descargar y descomprimir los loaders (64-bit): cd /tmp wget -q https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz tar xzf ioncube_loaders_lin_x86-64.tar.gz # Copiar el loader de su versión de PHP al directorio de extensiones: sudo cp ioncube/ioncube_loader_lin_${PHPVER}.so "$EXTDIR"/ # Activar (CLI + PHP-FPM) y reiniciar: echo "zend_extension=ioncube_loader_lin_${PHPVER}.so" | sudo tee /etc/php/${PHPVER}/mods-available/ioncube.ini sudo phpenmod ioncube sudo systemctl restart php${PHPVER}-fpm # Comprobación — en la salida aparecerá la línea "with the ionCube PHP Loader": php -v
La versión del loader debe coincidir con la versión de PHP (por ejemplo ioncube_loader_lin_8.1.so para PHP 8.1). Si usa varias versiones de PHP, active el loader para cada una.

09. Dominio y DNS

Para abrir el panel en una dirección como monitor.example.com y obtener SSL gratuito, necesita un dominio que apunte a su servidor. En el panel de control de DNS cree un registro A:

Tipo: A Nombre: monitor (subdominio → monitor.example.com) o @ (raíz del dominio → example.com) Valor: 203.0.113.10 ← IP de su servidor TTL: 3600

Al cabo de unos minutos, compruebe que el dominio apunta al servidor:

dig +short monitor.example.com # debe devolver su IP # o, si no tiene dig: getent hosts monitor.example.com
El certificado SSL de Let's Encrypt solo se emite para un dominio: el DNS debe apuntar al servidor antes de emitir el certificado.

10. Crear el sitio y subir los archivos del panel

Apache: DocumentRoot debe apuntar a la raíz del panel, NO a public/. Los estilos (CSS/JS), sw.js y manifest.json están en assets/ junto a public/ y se solicitan desde la raíz del sitio. El .htaccess raíz es el controlador frontal. Si en Apache pone DocumentRoot en public/, el panel se abrirá sin estilos. Con nginx puro es al revés: como raíz se toma public/, y assets/ se sirve mediante una regla aparte (véase el bloque de nginx más abajo).
Los archivos del panel (el archivo de la distribución) se descargan tras la compra en el área personal de my.arciveo.com«Descargas». Descomprima el archivo antes de subirlo.

1) Cree el directorio del panel y suba a él el contenido de la distribución (de modo que dentro queden public/, assets/, config.php, etc.):

sudo mkdir -p /var/www/monitor # luego suba los archivos de la distribución a /var/www/monitor (FileZilla / WinSCP / scp)

2) Configure el servidor web. Apache: DocumentRoot en la raíz del panel (NO en /public); AllowOverride All es obligatorio. La ruta al socket de PHP-FPM se detecta automáticamente. El bloque se pega en el terminal entero:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # detección automática del socket de PHP-FPM sudo tee /etc/apache2/sites-available/monitor.conf > /dev/null <<'EOF' <VirtualHost *:80> ServerName monitor.example.com DocumentRoot /var/www/monitor <Directory /var/www/monitor> AllowOverride All Require all granted </Directory> <FilesMatch \.php$> SetHandler "proxy:unix:__PHPSOCK__|fcgi://localhost" </FilesMatch> </VirtualHost> EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/apache2/sites-available/monitor.conf sudo a2dissite 000-default.conf sudo a2ensite monitor.conf sudo apache2ctl configtest sudo systemctl reload apache2

nginx: nginx no tiene .htaccess, por eso tomamos como raíz public/, y assets/, sw.js, manifest.json (un nivel más arriba) los servimos con una regla aparte:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # detección automática del socket de PHP-FPM sudo tee /etc/nginx/sites-available/monitor.conf > /dev/null <<'EOF' server { listen 80; server_name monitor.example.com; root /var/www/monitor/public; index index.php; # assets, service worker y manifest están un nivel por encima de public/ location ~ ^/(assets/|sw\.js|manifest\.json) { root /var/www/monitor; } location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:__PHPSOCK__; } } EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/nginx/sites-available/monitor.conf sudo ln -s /etc/nginx/sites-available/monitor.conf /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx

Subida de archivos: SFTP/SCP (FileZilla, WinSCP) o scp:

# Ejemplo con scp desde el equipo local: scp -r ./monitor/* deploy@203.0.113.10:/var/www/monitor/
Establezca los permisos de los archivos: es un paso obligatorio. Si los subió como root o por SFTP, los archivos pertenecen a root y el servidor web (www-data) no podrá leerlos: el panel se abrirá vacío o con un error 403 (en el registro: .htaccess unreadable / directory not executable). El comando de abajo lo soluciona:
# Normalizamos los permisos de todo el webroot: un directorio creado por root es inaccesible # para el servidor web (www-data); sin esto el panel devuelve una página vacía o 403. # Apache se ejecuta como www-data; si tiene otro usuario web, sustitúyalo. cd /var/www/monitor # Creamos las carpetas de trabajo ANTES del chown; de lo contrario los nuevos directorios quedarán root:root # y con chmod 750 el servidor web (www-data) no podrá escribir en ellos. sudo mkdir -p data/lynis data/logwatch tmp logs sudo chown -R www-data:www-data /var/www/monitor sudo find /var/www/monitor -type d -exec chmod 755 {} \; sudo find /var/www/monitor -type f -exec chmod 644 {} \; sudo chmod 640 /var/www/monitor/config.php sudo chmod 750 data tmp logs

3) Habilite para usted mismo la subida por SFTP. Tras el comando anterior todos los archivos pertenecen a www-data, mientras que FileZilla / WinSCP se conectan con su propio usuario: la subida fallará con SSH_FX_PERMISSION_DENIED (Permission denied). Entrar como root para subir archivos no es una opción: el acceso root se desactivó en el paso 05. Elija una de las dos variantes.

Variante A: una ACL solo para su usuario (recomendado). El permiso de escritura lo obtiene únicamente usted; el servidor web sigue sin poder sobrescribir el código del panel:

sudo apt install -y acl # Permiso de escritura para su usuario en todo el directorio del panel: sudo setfacl -R -m u:deploy:rwX /var/www/monitor # La misma regla por defecto: para archivos y carpetas creados más adelante: sudo setfacl -R -d -m u:deploy:rwX /var/www/monitor

Variante B: mediante el grupo www-data. Más sencilla, pero el servidor web también obtiene permiso de escritura sobre los archivos del panel: ante una vulnerabilidad en PHP se podría sustituir el código. El orden de los comandos importa: config.php y las carpetas de trabajo se cierran al final:

sudo usermod -aG www-data deploy # Escritura para el grupo + setgid (el bit 2): los archivos subidos por SFTP # permanecen en el grupo www-data; de lo contrario el panel no podrá sobrescribirlos. sudo find /var/www/monitor -type d -exec chmod 2775 {} \; sudo find /var/www/monitor -type f -exec chmod 664 {} \; sudo chmod 640 /var/www/monitor/config.php sudo chmod 2750 /var/www/monitor/data /var/www/monitor/tmp /var/www/monitor/logs
Tras la variante B, vuelva a conectarse en FileZilla (Servidor → Desconectar y entrar de nuevo): el nuevo grupo solo se aplica en un inicio de sesión nuevo, hasta entonces seguirá sin permisos. Comprobación: id deploy, en la lista de grupos debe aparecer www-data; ls -ld /var/www/monitor, permisos drwxrwsr-x, la letra s en lugar de x significa que setgid está activo.

11. Base de datos

Cree la base de datos y el usuario, luego importe el esquema. El bloque se pega por completo en el terminal. monitor_db y monitor_user son nombres de ejemplo; puede definir los suyos. Recuerde el nombre de la base, el usuario y la contraseña: los introducirá en config.php en el siguiente paso:

# 1. Base de datos. La contraseña se define UNA vez en DBPASS y se sustituye en todas las líneas. # El bloque se pega en el terminal POR COMPLETO; sudo mysql entra como root por socket unix # (no se necesita la contraseña de root). NO use el `sudo mysql -u root -p` interactivo # con copiar y pegar: al pegar, las líneas SQL irán a la petición de contraseña y se perderán. DBPASS='CHOOSE_A_PASSWORD' # ← cambie solo esta línea sudo mysql <<SQL CREATE DATABASE IF NOT EXISTS monitor_db CHARACTER SET utf8mb4; CREATE USER IF NOT EXISTS 'monitor_user'@'localhost' IDENTIFIED BY '$DBPASS'; GRANT ALL ON monitor_db.* TO 'monitor_user'@'localhost'; FLUSH PRIVILEGES; SQL # Comprobación (debe mostrar monitor_db): mysql -u monitor_user -p"$DBPASS" -e "SHOW DATABASES;" # Introduzca esta misma contraseña en config.php → DB_PASS.
No es necesario importar el esquema: el panel crea por sí mismo las tablas y la cuenta admin en el primer acceso desde el navegador (a partir de database/db.sql) si la base de datos está vacía. La importación manual del esquema solo es necesaria si la autoinicialización no funcionó.
Si utilizó el instalador de navegador public/start_db.php, elimínelo justo después de la instalación: permite recrear la base de datos sin autenticación. Mientras el archivo permanezca en la raíz del panel o en public/, el panel mostrará una advertencia en rojo.

12. Configuración de config.php

config.php en la raíz del panel (/var/www/monitor/config.php) es el único archivo que hay que editar a mano. Todos los ajustes del panel se definen en él mediante constantes define(). Ábralo en el editor:

sudo nano /var/www/monitor/config.php

Ponga sus valores en los lugares resaltados; deje el resto tal cual:

// --- Base de datos (del paso 11) --- define('DB_HOST', 'localhost'); // dejar define('DB_NAME', 'db_name'); // la que creó en el paso 11 define('DB_USER', 'user'); // el que creó en el paso 11 define('DB_PASS', 'db_password'); // la que definió en el paso 11 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 // --- Duración de la sesión --- define('SESSION_LIFETIME', 28800); // inactividad hasta un nuevo inicio de sesión, s (28800 = 8 h)

Qué cambiar:

  • DB_NAME, DB_USER, DB_PASS: exactamente el mismo nombre de base, usuario y contraseña que definió al crear la BD en el paso 11 (si dejó los ejemplos: monitor_db / monitor_user). No toque DB_HOST ni DB_CHARSET.
  • APP_URL: la dirección completa del panel con https://, sin barra al final y sin www. Debe coincidir con el dominio en el que activa la licencia (paso 16), de lo contrario la clave será rechazada.
  • TIMEZONE: su zona horaria (lista: timedatectl list-timezones). Solo afecta a cómo el panel muestra las fechas; en la hora de ejecución de las tareas cron no influye (allí se aplica la zona del sistema).
  • SESSION_LIFETIME: tras cuántos segundos de inactividad el panel pedirá volver a iniciar sesión (por defecto 8 horas). Por ej. 3600 = 1 hora, 86400 = un día.
  • El bloque de registro de errores (display_errors, log_errors, error_log): déjelo por defecto.

Guarde el archivo (Ctrl+O, Enter, luego Ctrl+X) y reinicie PHP-FPM; de lo contrario, por OPcache los cambios no se aplicarán:

sudo systemctl restart php*-fpm
config.php es un archivo secreto (contiene la contraseña de la BD). Está en la raíz del panel, que es la raíz web, pero está protegido: permisos 640 (asignados en el paso 10) y una denegación explícita en el .htaccess raíz. No lo publique en repositorios públicos ni lo envíe a soporte con la contraseña real.
El análisis detallado de todos los parámetros está en la FAQ: «Archivo config.php — todos los ajustes del panel».

13. Configuración de sudo para el servidor web

PHP se ejecuta con el usuario del servidor web, que no tiene permisos sobre los comandos del sistema. El acceso se concede de forma restringida: sudo puntual sobre utilidades concretas y lectura de registros mediante grupos (sin sudo). Un ataque a la capa web no da root.

En los ejemplos, www-data es el usuario estándar de Apache. Si el suyo es otro (en algunos paneles PHP corre con un usuario aparte), sustitúyalo en todas partes. Para averiguarlo: ps -o user= -C php-fpm | sort -u.

1. Cree /etc/sudoers.d/monitor con sudo visudo -f /etc/sudoers.d/monitor e inserte (elimine las líneas de los módulos que no use):

# UFW — estado y reglas (página «Cortafuegos») www-data ALL=(ALL) NOPASSWD: /usr/sbin/ufw status, /usr/sbin/ufw status verbose, /usr/sbin/ufw status numbered www-data ALL=(ALL) NOPASSWD: /usr/sbin/ufw allow [0-9]*, /usr/sbin/ufw deny [0-9]*, /usr/sbin/ufw --force delete [0-9]* # Fail2ban — estado, ban y desban (banned devuelve los baneos de todos los jails con un solo comando; # ban/unban los necesitan los botones del panel) www-data ALL=(ALL) NOPASSWD: /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status *, /usr/bin/fail2ban-client banned, /usr/bin/fail2ban-client set * banip *, /usr/bin/fail2ban-client set * unbanip * # Actualizaciones de seguridad (tarjeta «Actualizaciones»). Solo lectura, pero precisamente desde root: # la caché de apt (~70 MB) solo es accesible para root; un usuario no root la reconstruye en cada llamada # (4,2 s de CPU frente a 0,01 s). Sin wildcard: exactamente este único comando, no instala nada. www-data ALL=(ALL) NOPASSWD: /usr/bin/apt list --upgradable # IPset (mapa de ataques, panel) www-data ALL=(ALL) NOPASSWD: /usr/sbin/ipset list -t ipsum # CrowdSec www-data ALL=(ALL) NOPASSWD: /usr/bin/cscli decisions list *, /usr/bin/cscli alerts list *, /usr/bin/cscli bouncers list *, /usr/bin/cscli scenarios list * # Auditd — búsqueda de eventos + lectura de las últimas líneas del registro (ruta exacta) www-data ALL=(ALL) NOPASSWD: /usr/sbin/ausearch -m * www-data ALL=(ALL) NOPASSWD: /usr/bin/tail -n 300 /var/log/audit/audit.log # Monit / ModSecurity / AppArmor / PSAD www-data ALL=(ALL) NOPASSWD: /usr/bin/monit status www-data ALL=(ALL) NOPASSWD: /usr/sbin/apache2ctl -M www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec www-data ALL=(ALL) NOPASSWD: /usr/sbin/aa-status www-data ALL=(ALL) NOPASSWD: /usr/sbin/psad --Status # Puertos abiertos (los registros del núcleo/SSH/Falco se leen SIN sudo — mediante el grupo # systemd-journal, ver p.2; para journalctl NO hace falta dar sudo y es inseguro) www-data ALL=(ALL) NOPASSWD: /usr/bin/ss -tuln, /usr/sbin/ss -tuln, /bin/ss -tuln # PostgreSQL (solo si lo usa) — script fijo de solo lectura, # créelo según la FAQ «PostgreSQL no aparece»; sin él, elimine la línea www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
sudo chmod 440 /etc/sudoers.d/monitor sudo visudo -c # debe indicar "parsed OK"

2. Acceso a los registros y al journal de systemd. Los módulos leen /var/log/fail2ban.log, auth.log, ufw.log, apache2/*, aide directamente (en Debian/Ubuntu estos registros están en el grupo adm). Los eventos del núcleo, SSH y Falco se obtienen de journald con el comando journalctl sin sudo, mediante el grupo systemd-journal. Añada el usuario web a ambos grupos y reinicie PHP-FPM:

sudo usermod -aG adm,systemd-journal www-data sudo systemctl restart php*-fpm # obligatorio, de lo contrario los grupos no se aplican

3. Si ClamAV o Suricata no escriben los registros en el grupo adm (a veces son root:root), conceda acceso mediante ACL:

sudo apt install acl sudo setfacl -R -m u:www-data:rX /var/log/clamav /var/log/suricata 2>/dev/null sudo setfacl -d -m u:www-data:rX /var/log/clamav /var/log/suricata 2>/dev/null

4. Envoltorio de ModSecurity. El registro de auditoría del WAF (/var/log/apache2/modsec_audit.log) pertenece a root con permisos 640, y el usuario web no puede leerlo directamente. La página de ModSecurity obtiene el modo del motor, los eventos y la lista de reglas activas mediante un script fijo de solo lectura, que es el que se autoriza en sudoers en la línea anterior:

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
Sin el archivo /etc/modsecurity/modsecurity.conf, el propio WAF no funciona: el paquete solo instala modsecurity.conf-recommended y el motor de reglas queda desactivado; para activarlo, consulte FAQ → «Instalación de ModSecurity».
El usuario en todas las líneas de sudoers debe coincidir con el usuario del pool de FPM: en un Apache/Debian normal es www-data, en HestiaCP el pool del sitio corre con el propietario del sitio (por ejemplo, admin); compruebe grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.

5. Si delante de Apache hay Nginx (HestiaCP, ISPmanager y otros paneles, donde Nginx hace de proxy de PHP hacia Apache y sirve él mismo el contenido estático). Los directorios internos están protegidos con archivos .htaccess, pero Nginx no los lee: cualquier archivo estático (.json, .txt, .log, .dat) lo servirá directamente, sin pasar por Apache. Al exterior se filtrarán cachés del panel y datos, por ejemplo tmp/modsec_cache.json con eventos del WAF e IP de los atacantes. Añada la denegación a la configuración del sitio en Nginx:

location ^~ /data/ { deny all; } location ^~ /tmp/ { deny all; } location ^~ /logs/ { deny all; } location ^~ /includes/ { deny all; } location ^~ /cron/ { deny all; } location ^~ /database/ { deny all; } location = /config.php { deny all; }
El prefijo ^~ es obligatorio: se selecciona antes que la regla de expresión regular para el contenido estático dentro de location /; de lo contrario, la denegación no surtirá efecto.
En HestiaCP colóquelo en un archivo aparte /home/<user>/conf/web/<domain>/nginx.ssl.conf_deny (y nginx.conf_deny para HTTP): la configuración del sitio incluye nginx.ssl.conf_* y no sobrescribe esos archivos al reconstruirse. Para aplicar: sudo nginx -t && sudo systemctl reload nginx.
Comprobación: curl -s -o /dev/null -w '%{http_code}\n' https://monitor.example.com/tmp/modsec_cache.json — debe ser 403. Si Apache funciona sin Nginx (escucha él mismo en 80/443), no hace falta añadir nada: basta con .htaccess.
Verifique las rutas a los binarios con which (por ejemplo which ufw cscli ausearch ss). Edite sudoers únicamente con visudo. La lista de todas las bases de datos MySQL se habilita con un GRANT aparte (FAQ → «Solo se ve una BD»).

14. Restricción de acceso por IP

Restrinja el acceso al monitor por dirección IP: aunque se conozca la URL, la página de inicio de sesión no se abrirá. Puede hacerse a nivel del servidor web (ejemplo para nginx más abajo) o en el propio panel («Ajustes» → «Restricción de acceso por IP»). Si utiliza Apache, use la restricción del panel.

Si el sitio nginx ya está configurado según el paso 10, no añada un segundo location /: escriba las líneas allow/deny en el bloque ya existente. Dos location / iguales en un mismo server { } es un error de configuración y nginx no se reiniciará.
# En la configuración de nginx (dentro de server { }): # La ruta ACME de Let's Encrypt se mantiene abierta sin la restricción por IP, # para que la emisión y renovación automática de SSL (paso 15) no dependan del filtro de IP. location ^~ /.well-known/acme-challenge/ { allow all; } location / { allow 203.0.113.10; # ← escriba su propia IP allow 10.0.0.0/8; # red local (si es necesario) deny all; try_files $uri $uri/ /index.php?$query_string; } # Recargar nginx: sudo nginx -t && sudo systemctl reload nginx

15. Emitir SSL (HTTPS)

El panel solo funciona por HTTPS. La sesión de inicio usa una cookie segura, y WebAuthn (2FA) solo funciona con HTTPS. No es posible iniciar sesión por http://.

El certificado es gratuito (Let's Encrypt). El DNS del dominio ya debe apuntar al servidor. El comando depende del servidor web:

# Apache: sudo certbot --apache -d monitor.example.com # nginx — SOLO si usa precisamente nginx. En Apache NO lo ejecute: # apt arrastrará nginx y ocupará el puerto 80, conflicto con Apache. # sudo apt install python3-certbot-nginx # sudo certbot --nginx -d monitor.example.com # certbot añadirá HTTPS a la configuración y activará la renovación automática
Qué preguntará certbot: e-mail → aceptación de los Terms (Y) → envío del e-mail a la EFF (a su criterio). Luego emitirá el certificado, añadirá <VirtualHost *:443>, configurará la redirección http→https y la renovación automática.
El DNS debe apuntar al servidor ANTES de ejecutar certbot (verificación de propiedad por el puerto 80). Comprobación: dig +short monitor.example.com → IP del servidor. Puertos 80/443 abiertos: sudo ufw allow 80,443/tcp.

Tras la emisión: https://monitor.example.com se abre con candado, http:// redirige a https:// (APP_URL en config.php ya se definió en el paso 12).

16. Acceso y configuración inicial

Abra https://monitor.example.com, inicie sesión con admin / useradmin y complete la lista de comprobación:

  1. Cambiar la contraseña de admin — sección «Usuarios» en el menú.
  2. Activar WebAuthn (2FA) — «Claves WebAuthn» → registrar clave/passkey (requiere HTTPS). Registre dos de una vez: si pierde la única clave, no podrá iniciar sesión con ella. Más información.
  3. Restringir el acceso por IP — «Ajustes» → «Restricción de acceso por IP» (indique su propia IP antes de activarla, o se bloqueará el acceso a sí mismo).
  4. Introducir la licencia — active el código ARCIVEO-… de su área personal para su dominio e inserte la clave en «Ajustes» → «Licencia». Más información.
  5. Configurar las notificaciones — Telegram o correo electrónico en «Ajustes». Más información.
  6. Eliminar el instalador public/start_db.php, si sigue presente (paso 11).

17. Herramientas de seguridad (opcional)

El panel ya está funcionando. Las herramientas se instalan según necesite: instale lo que precise y el panel mostrará el estado al instante. Los comandos de instalación de cada una están en la guía (secciones específicas por herramienta):

18. Cron y mantenimiento

Se configura una sola vez, también es opcional, pero se recomienda. Los comandos detallados están en la referencia:

  1. Tareas de cron (informes, actualización de listas, comprobaciones);
  2. Copias de seguridad;
  3. Actualización y migración del panel;
  4. Recuperación de acceso: por si pierde la clave o la contraseña.
¿Algo no funciona o muestra «sin datos»? Consulte el grupo «Diagnóstico» de la referencia.
Arcivéo - Security Monitor © 2026