Apache2 en Debian 13 para producción (Parte 2): seguridad avanzada, PHP-FPM, Fail2ban, respaldos y monitoreo

En la primera parte de nuestro tutorial aprendimos a instalar Apache2 en Debian 13, configurar VirtualHosts, alojar múltiples páginas web, utilizar dominios locales y reales, habilitar PHP-FPM y proteger las conexiones mediante HTTPS.

Sin embargo, publicar un sitio web es solamente el comienzo.

Cuando administramos un VPS, especialmente si alojamos aplicaciones o páginas de diferentes clientes, también debemos proteger los archivos, aislar procesos, detectar intentos de acceso indebido, mantener respaldos y supervisar el funcionamiento del servidor.

En esta segunda parte aprenderemos a fortalecer nuestra instalación de Apache2 para un entorno de producción.

Trabajaremos con cinco áreas fundamentales:

  1. Aislamiento de aplicaciones mediante usuarios y pools PHP-FPM independientes.
  2. Protección frente a ataques con Fail2ban, limitación de solicitudes y ModSecurity con OWASP CRS.
  3. Respaldos automáticos de archivos, bases de datos y configuraciones.
  4. Monitoreo de Apache, PHP-FPM, almacenamiento, certificados y recursos.
  5. Administración segura del VPS, SSH, actualizaciones y recuperación ante incidentes.

Advertencia: este tutorial está pensado para un VPS de laboratorio o un servidor nuevo. Antes de modificar un servidor en producción debemos realizar respaldos verificables, mantener una sesión SSH abierta y disponer de acceso a la consola de recuperación del proveedor.


1. Preparar el entorno de trabajo

Utilizaremos como referencia:

ComponenteTecnología
Sistema operativoDebian 13
Servidor webApache HTTP Server 2.4
Intérprete PHPPHP 8.4 mediante PHP-FPM
Administración de serviciossystemd
FirewallUFW
Protección de accesosFail2ban
Firewall de aplicacionesModSecurity + OWASP CRS
RespaldosBash, tar, MariaDB y restic
Programacióncron y systemd timers
CertificadosLet’s Encrypt con Certbot

Los procedimientos pueden adaptarse a Ubuntu Server y otras distribuciones basadas en Debian, aunque debemos verificar versiones, nombres de servicios y rutas.

Comprobar el sistema

cat /etc/os-release

Comprobar Apache:

apache2 -v

Comprobar PHP:

php -v

Comprobar los servicios:

systemctl status apache2
systemctl status php8.4-fpm

Antes de comenzar, actualizamos los paquetes:

sudo apt update
sudo apt upgrade

En un servidor con aplicaciones activas debemos revisar los cambios antes de aplicar actualizaciones que puedan afectar dependencias.

Guardar la configuración actual

Creamos un respaldo inicial de Apache:

sudo mkdir -p /root/respaldos-iniciales
sudo tar -czf \
  /root/respaldos-iniciales/apache2-antes-de-cambios.tar.gz \
  -C /etc apache2

También conviene guardar las configuraciones de PHP, SSH, firewall y una copia de las aplicaciones y sus bases de datos.

Este archivo inicial no sustituye un sistema completo de respaldos, pero facilita recuperar la configuración de Apache si cometemos un error.


PARTE I. AISLAMIENTO DE APLICACIONES CON PHP-FPM

2. ¿Por qué necesitamos aislar los sitios web?

Imaginemos un VPS con tres aplicaciones:

/var/www/
├── empresa-a/
├── empresa-b/
└── empresa-c/

Apache puede utilizar un VirtualHost para cada dominio.

Sin embargo, si todos los proyectos PHP se ejecutan como www-data, comparten la misma identidad del sistema.

Esto puede provocar que una aplicación comprometida tenga acceso a archivos de otra aplicación si los permisos lo permiten.

Por ejemplo:

SITIO A ─┐
         │
SITIO B ─┼── PHP-FPM (www-data)
         │
SITIO C ─┘

Los VirtualHosts separan la configuración HTTP, pero no garantizan aislamiento entre procesos.

Una alternativa consiste en crear un usuario y un pool PHP-FPM independiente para cada aplicación.

SITIO A
   └── PHP-FPM A
         └── usuario sitio-a

SITIO B
   └── PHP-FPM B
         └── usuario sitio-b

SITIO C
   └── PHP-FPM C
         └── usuario sitio-c

De esta manera podemos aplicar permisos diferentes y reducir el acceso de una aplicación a los archivos de otra.

Importante: los pools PHP-FPM no equivalen a contenedores o máquinas virtuales. Siguen compartiendo el mismo kernel y otros recursos del servidor.

3. Crear usuarios independientes

Utilizaremos dos aplicaciones de ejemplo:

empresa-a.test
empresa-b.test

Crearemos dos cuentas de sistema sin acceso interactivo.

sudo adduser --system \
  --group \
  --home /var/www/empresa-a \
  --no-create-home \
  sitio-a
sudo adduser --system \
  --group \
  --home /var/www/empresa-b \
  --no-create-home \
  sitio-b

Estas cuentas no están destinadas a iniciar sesión mediante SSH.

Su función es ejecutar procesos de aplicación con permisos separados.

Comprobamos:

id sitio-a
id sitio-b

Crear los directorios

sudo mkdir -p /var/www/empresa-a/public
sudo mkdir -p /var/www/empresa-b/public

Asignamos propietarios:

sudo chown -R sitio-a:sitio-a /var/www/empresa-a
sudo chown -R sitio-b:sitio-b /var/www/empresa-b

Para un laboratorio podemos comenzar con:

sudo find /var/www/empresa-a -type d -exec chmod 750 {} +
sudo find /var/www/empresa-b -type d -exec chmod 750 {} +
sudo find /var/www/empresa-a -type f -exec chmod 640 {} +
sudo find /var/www/empresa-b -type f -exec chmod 640 {} +

Con estos permisos, Apache todavía necesitará acceso de lectura a los archivos públicos. Lo resolveremos mediante ACL, sin hacer que todos los archivos sean accesibles para cualquier usuario.

4. Permitir que Apache lea solamente los archivos públicos

Instalamos las herramientas ACL:

sudo apt install acl

Concedemos al usuario www-data acceso de lectura y recorrido al directorio público del primer sitio:

sudo setfacl -R -m u:www-data:rX \
  /var/www/empresa-a/public

Repetimos para el segundo:

sudo setfacl -R -m u:www-data:rX \
  /var/www/empresa-b/public

También debemos permitir atravesar el directorio padre:

sudo setfacl -m u:www-data:--x /var/www/empresa-a
sudo setfacl -m u:www-data:--x /var/www/empresa-b

¿Qué significa rX?

r concede lectura.

X concede ejecución o recorrido solamente cuando corresponde, como en directorios y archivos que ya tienen algún permiso de ejecución.

Esto evita conceder ejecución indiscriminada a todos los archivos.

¿Por qué utilizamos ACL?

Porque los permisos tradicionales de Linux permiten definir propietario, grupo y otros usuarios, pero las ACL ofrecen permisos adicionales para usuarios concretos.

Así, www-data puede leer los recursos públicos sin recibir acceso general a toda la aplicación.

Para comprobar:

getfacl /var/www/empresa-a/public

En producción debemos incorporar estas ACL al procedimiento de despliegue, ya que los archivos nuevos o reemplazados pueden necesitar permisos adicionales. También podemos utilizar ACL predeterminadas cuidadosamente configuradas.

5. Crear pools PHP-FPM independientes

En Debian 13, los pools de PHP 8.4 suelen configurarse en:

/etc/php/8.4/fpm/pool.d/

Podemos comprobarlo:

ls /etc/php/8.4/fpm/pool.d/

Normalmente encontraremos un archivo llamado:

www.conf

Este archivo define el pool predeterminado.

No necesitamos modificarlo para crear nuestros pools.

Crear el pool de empresa A

sudo nano /etc/php/8.4/fpm/pool.d/empresa-a.conf

Contenido:

[empresa-a]

user = sitio-a
group = sitio-a

listen = /run/php/empresa-a.sock

listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic

pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

pm.max_requests = 500

php_admin_value[expose_php] = Off
php_admin_value[display_errors] = Off
php_admin_flag[log_errors] = on

php_admin_value[error_log] = /var/log/php-empresa-a.log

Explicación

[empresa-a] identifica el pool.

user y group determinan la identidad con la que se ejecutan los procesos PHP de esa aplicación.

listen define el socket Unix que recibirá las solicitudes de Apache.

listen.owner, listen.group y listen.mode controlan quién puede conectarse al socket.

pm = dynamic permite administrar dinámicamente los procesos PHP.

pm.max_children establece un límite de procesos trabajadores para ese pool.

pm.max_requests permite reciclar procesos después de atender cierto número de solicitudes.

Los valores mostrados son ilustrativos. Deben ajustarse según la memoria del VPS y el consumo real de cada aplicación.

El archivo de registro configurado debe tener una ubicación y permisos adecuados. Podemos prepararlo así:

sudo touch /var/log/php-empresa-a.log
sudo chown sitio-a:sitio-a /var/log/php-empresa-a.log
sudo chmod 640 /var/log/php-empresa-a.log

Crear el pool de empresa B

sudo nano /etc/php/8.4/fpm/pool.d/empresa-b.conf

Contenido:

[empresa-b]

user = sitio-b
group = sitio-b

listen = /run/php/empresa-b.sock

listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic

pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

pm.max_requests = 500

php_admin_value[expose_php] = Off
php_admin_value[display_errors] = Off
php_admin_flag[log_errors] = on

php_admin_value[error_log] = /var/log/php-empresa-b.log

Preparamos su registro:

sudo touch /var/log/php-empresa-b.log
sudo chown sitio-b:sitio-b /var/log/php-empresa-b.log
sudo chmod 640 /var/log/php-empresa-b.log

Validar PHP-FPM

sudo php-fpm8.4 -t

Si la configuración es correcta, reiniciamos:

sudo systemctl restart php8.4-fpm

Comprobamos los sockets:

sudo ls -l /run/php/

Deberíamos encontrar:

empresa-a.sock
empresa-b.sock

6. Conectar cada VirtualHost con su pool

Habilitamos los módulos necesarios:

sudo a2enmod proxy proxy_fcgi setenvif

Si anteriormente habilitamos la configuración global php8.4-fpm, debemos deshabilitarla para evitar que los sitios PHP que no tengan una configuración específica sigan utilizando automáticamente el pool compartido:

sudo a2disconf php8.4-fpm

Antes de aplicar este cambio a un servidor existente, debemos revisar todos sus VirtualHosts, porque los sitios que dependían de esa configuración global podrían dejar de ejecutar PHP.

VirtualHost de empresa A

Creamos:

sudo nano /etc/apache2/sites-available/empresa-a.test.conf

Contenido:

<VirtualHost *:80>

    ServerName empresa-a.test

    DocumentRoot /var/www/empresa-a/public

    DirectoryIndex index.php index.html

    <Directory /var/www/empresa-a/public>
        Options -Indexes -FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>

    <FilesMatch "\.php$">
        SetHandler "proxy:unix:/run/php/empresa-a.sock|fcgi://localhost/"
    </FilesMatch>

    ErrorLog ${APACHE_LOG_DIR}/empresa-a-error.log

    CustomLog ${APACHE_LOG_DIR}/empresa-a-access.log combined

</VirtualHost>

¿Qué hace SetHandler?

Esta directiva indica a Apache cómo procesar determinados archivos.

En nuestro ejemplo:

<FilesMatch "\.php$">
    SetHandler "proxy:unix:/run/php/empresa-a.sock|fcgi://localhost/"
</FilesMatch>

Las solicitudes de archivos PHP se envían al socket del pool empresa-a.

Así, los scripts se ejecutan con la identidad sitio-a, no con la identidad del pool de otra aplicación.

VirtualHost de empresa B

<VirtualHost *:80>

    ServerName empresa-b.test

    DocumentRoot /var/www/empresa-b/public

    DirectoryIndex index.php index.html

    <Directory /var/www/empresa-b/public>
        Options -Indexes -FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>

    <FilesMatch "\.php$">
        SetHandler "proxy:unix:/run/php/empresa-b.sock|fcgi://localhost/"
    </FilesMatch>

    ErrorLog ${APACHE_LOG_DIR}/empresa-b-error.log

    CustomLog ${APACHE_LOG_DIR}/empresa-b-access.log combined

</VirtualHost>

Debemos guardar este segundo bloque en:

/etc/apache2/sites-available/empresa-b.test.conf

Habilitamos:

sudo a2ensite empresa-a.test.conf
sudo a2ensite empresa-b.test.conf

Validamos:

sudo apache2ctl configtest

Y recargamos:

sudo systemctl reload apache2

La sintaxis de SetHandler con sockets Unix está documentada oficialmente para Apache y mod_proxy_fcgi.

7. Comprobar que el aislamiento funciona

Crearemos un archivo PHP temporal en cada sitio.

Para empresa A:

sudo nano /var/www/empresa-a/public/identidad.php

Contenido:

<?php

echo "Pool: empresa-a";
echo "\n";

echo "UID del proceso: " . posix_geteuid();

Para empresa B utilizamos el mismo código, cambiando el nombre mostrado.

La extensión POSIX debe estar disponible en PHP.

Podemos probar los sitios mediante /etc/hosts:

127.0.0.1 empresa-a.test empresa-b.test

Y visitar:

http://empresa-a.test/identidad.php
http://empresa-b.test/identidad.php

También podemos comparar los UID obtenidos con:

id -u sitio-a
id -u sitio-b

Deben corresponder a cuentas distintas.

El archivo identidad.php debe eliminarse después de realizar las pruebas.

Una limitación importante

Esta configuración separa la ejecución de PHP, pero Apache sigue atendiendo las solicitudes HTTP mediante su propia identidad.

Además, los archivos del proyecto son propiedad del usuario de aplicación, por lo que un proceso PHP comprometido podría modificar código que pertenece a ese usuario.

Para producción podemos fortalecer el modelo utilizando cuentas separadas de despliegue y ejecución, haciendo que PHP solo pueda escribir en directorios específicos.

También debemos considerar que procesos de mantenimiento, tareas cron, colas y comandos de consola deben ejecutarse con la identidad correspondiente a cada aplicación.


PARTE II. PROTECCIÓN CONTRA ATAQUES

8. Comprender las capas de protección

Un servidor expuesto a Internet recibe constantemente solicitudes automáticas.

Algunas son legítimas, como las realizadas por navegadores y motores de búsqueda.

Otras pueden buscar:

  • Contraseñas débiles.
  • Paneles administrativos.
  • Archivos de configuración.
  • Vulnerabilidades conocidas.
  • Formularios sin protección.
  • Rutas inexistentes.
  • Aplicaciones desactualizadas.

No existe una herramienta que detenga todos los ataques.

Por eso utilizamos varias capas:

INTERNET
   |
   v
FIREWALL
   |
   v
FAIL2BAN
   |
   v
APACHE2 + MODSECURITY
   |
   v
APLICACIÓN WEB
   |
   v
PHP-FPM / BASE DE DATOS

Este diagrama representa las funciones de protección, no una cadena literal por la que pase cada solicitud. Fail2ban, por ejemplo, analiza registros y aplica bloqueos mediante el firewall.

9. Instalar y configurar Fail2ban

Fail2ban analiza registros de servicios y puede bloquear temporalmente direcciones IP cuando detecta determinados patrones.

Es especialmente útil para reducir intentos repetidos de autenticación contra SSH.

Instalamos:

sudo apt install fail2ban

Comprobamos:

systemctl status fail2ban

Crear una configuración propia

No conviene modificar directamente jail.conf, porque las actualizaciones del paquete pueden cambiarlo.

Creamos:

sudo nano /etc/fail2ban/jail.d/oplinux.local

Contenido inicial:

[DEFAULT]

bantime = 1h
findtime = 10m
maxretry = 5

[sshd]

enabled = true backend = systemd port = ssh

Explicación

bantime establece cuánto dura el bloqueo.

findtime define el periodo durante el cual se contabilizan los intentos.

maxretry indica el número máximo de coincidencias antes de aplicar la acción de bloqueo.

backend = systemd permite utilizar registros del journal.

port = ssh identifica el puerto configurado mediante el nombre del servicio. Si SSH utiliza un puerto personalizado, debemos ajustarlo.

Validar la configuración

sudo fail2ban-client -t

Reiniciamos:

sudo systemctl restart fail2ban

Comprobamos:

sudo fail2ban-client status

Y:

sudo fail2ban-client status sshd

¿Qué sucede si nuestra IP queda bloqueada?

Podemos retirar un bloqueo desde una sesión administrativa autorizada:

sudo fail2ban-client set sshd unbanip DIRECCION_IP

Debemos sustituir DIRECCION_IP por la dirección correspondiente.

Por seguridad, es recomendable conservar una sesión SSH abierta y disponer de acceso a la consola del VPS mientras configuramos Fail2ban.

10. ¿Podemos proteger Apache con Fail2ban?

Sí, pero debemos comprender qué queremos detectar.

Fail2ban no bloquea automáticamente todos los ataques web.

Utiliza filtros que buscan patrones concretos en los registros.

Podemos revisar los filtros disponibles:

ls /etc/fail2ban/filter.d/apache*

Dependiendo de la versión y configuración, encontraremos filtros para determinadas solicitudes indebidas.

Por ejemplo, podemos estudiar el filtro apache-badbots:

sudo nano /etc/fail2ban/filter.d/apache-badbots.conf

Antes de habilitar cualquier filtro debemos comprobar que corresponde a nuestros registros y que no genera falsos positivos.

Ejemplo de configuración

En el archivo:

/etc/fail2ban/jail.d/oplinux.local

Podemos agregar:

[apache-badbots]

enabled = true

port = http,https

filter = apache-badbots

logpath = /var/log/apache2/*access.log

maxretry = 2

findtime = 10m

bantime = 1h

Esta configuración es un ejemplo para un servidor cuyos registros siguen los patrones del filtro.

Debemos verificar que logpath coincide con los archivos reales y que el filtro está disponible.

Probar el filtro

sudo fail2ban-regex \
  /var/log/apache2/access.log \
  /etc/fail2ban/filter.d/apache-badbots.conf

La prueba nos ayuda a observar qué líneas coinciden con las expresiones del filtro.

Importante: los filtros basados únicamente en User-Agent son fáciles de evadir y pueden bloquear clientes legítimos. No deben considerarse una protección completa.

Si utilizamos Cloudflare u otro proxy

Debemos configurar correctamente la identificación de la IP real del visitante.

Si Apache solamente registra la IP del proxy, Fail2ban podría bloquear una dirección compartida por numerosos usuarios.

No debemos confiar indiscriminadamente en cabeceras como X-Forwarded-For.

Solamente deben aceptarse desde proxies cuya identidad esté verificada.

11. Limitar solicitudes abusivas en Apache

Una aplicación puede recibir demasiadas solicitudes en poco tiempo.

Esto puede consumir memoria, procesos PHP y conexiones.

Apache dispone de directivas para controlar ciertos aspectos de las conexiones y solicitudes.

Por ejemplo:

Timeout 30
KeepAlive On
KeepAliveTimeout 5

Estas directivas pueden ajustarse en la configuración global del servidor.

Timeout controla distintos tiempos de espera durante operaciones de Apache.

KeepAlive permite reutilizar conexiones HTTP cuando corresponde.

KeepAliveTimeout establece cuánto tiempo espera Apache por otra solicitud en una conexión persistente.

No debemos modificar estos valores sin considerar el tipo de aplicación y sus necesidades.

Limitar el tamaño de las solicitudes

Podemos establecer un límite para el cuerpo de las solicitudes:

LimitRequestBody 10485760

Este ejemplo establece un límite de 10 MiB.

Puede ser útil en determinados sitios, pero también podría impedir subir archivos legítimos.

En una aplicación que permite subir fotografías o documentos necesitaremos adaptar el límite.

¿Apache tiene un limitador de solicitudes por IP?

Apache dispone de módulos y mecanismos que ayudan a controlar conexiones y consumo de recursos, pero no debemos confundirlos con un sistema completo de limitación de solicitudes por IP.

Módulos adicionales como mod_evasive pueden ayudar frente a determinados patrones, aunque requieren pruebas y ajustes.

Para aplicaciones expuestas a ataques volumétricos, un proxy inverso, CDN o servicio especializado de mitigación puede ser más adecuado.

Un firewall local no puede detener por sí solo un ataque que ya ha saturado el enlace de red del proveedor.

12. Instalar ModSecurity

ModSecurity es un firewall de aplicaciones web, conocido como WAF.

A diferencia de un firewall tradicional, puede inspeccionar características de las solicitudes HTTP.

Por ejemplo, puede detectar determinados patrones relacionados con:

  • Inyección SQL.
  • Cross-Site Scripting.
  • Intentos de ejecutar comandos.
  • Solicitudes HTTP anómalas.
  • Acceso a rutas sospechosas.

Para Apache en Debian 13 podemos instalar:

sudo apt install libapache2-mod-security2 modsecurity-crs

Comprobamos el módulo:

apache2ctl -M | grep security

Si es necesario, lo habilitamos:

sudo a2enmod security2

¿Qué es OWASP CRS?

OWASP Core Rule Set es un conjunto de reglas genéricas diseñado para detectar patrones de ataques comunes contra aplicaciones web.

ModSecurity es el motor que inspecciona las solicitudes; CRS proporciona reglas que ayudan a reconocer comportamientos sospechosos.

13. Configurar ModSecurity inicialmente en modo detección

Es recomendable comenzar en modo detección para identificar posibles falsos positivos.

En Debian podemos localizar la configuración principal:

ls /etc/modsecurity/

Si encontramos:

modsecurity.conf-recommended

Y todavía no existe modsecurity.conf, podemos crear una copia:

sudo cp \
  /etc/modsecurity/modsecurity.conf-recommended \
  /etc/modsecurity/modsecurity.conf

Editamos:

sudo nano /etc/modsecurity/modsecurity.conf

Buscamos:

SecRuleEngine DetectionOnly

Si la directiva tiene otro valor, podemos establecer temporalmente DetectionOnly.

¿Qué significa DetectionOnly?

ModSecurity inspecciona las solicitudes y registra coincidencias, pero no aplica las acciones disruptivas habituales de bloqueo.

Esto permite observar cómo se comportan las reglas antes de habilitar la prevención.

Verificar la carga de CRS

Debemos revisar la configuración proporcionada por los paquetes:

sudo cat /etc/apache2/mods-enabled/security2.conf

Y localizar los archivos de reglas:

dpkg -L modsecurity-crs | grep -E 'crs-setup|REQUEST-|RESPONSE-'

La estructura exacta depende de la versión del paquete.

No debemos agregar reglas Include duplicadas sin comprobar primero qué archivos está cargando Apache.

Validar y reiniciar

sudo apache2ctl configtest
sudo systemctl restart apache2

Después revisamos los registros de ModSecurity y Apache.

La ubicación del registro de auditoría depende de la configuración de SecAuditLog.

Podemos localizarla con:

sudo grep -R '^[[:space:]]*SecAuditLog ' /etc/modsecurity/

14. Pasar ModSecurity a modo prevención

Después de revisar el comportamiento de los sitios y corregir falsos positivos, podemos considerar:

SecRuleEngine On

Este cambio permite que las reglas apliquen acciones de bloqueo cuando corresponda.

Sin embargo, debemos probar cuidadosamente:

  • Formularios de contacto.
  • Inicio de sesión.
  • Paneles administrativos.
  • Subida de archivos.
  • API y solicitudes JSON.
  • WooCommerce.
  • WordPress.
  • Aplicaciones Laravel.

Un WAF puede bloquear solicitudes legítimas.

La estrategia correcta es identificar las reglas problemáticas y aplicar exclusiones específicas y justificadas, en lugar de desactivar indiscriminadamente la protección.

ModSecurity complementa la seguridad del código, pero no corrige vulnerabilidades dentro de la aplicación.


PARTE III. RESPALDOS AUTOMÁTICOS

15. ¿Por qué son indispensables los respaldos?

Imaginemos que nuestro VPS aloja tres sitios web y una base de datos.

Un error de configuración, una eliminación accidental, un fallo de almacenamiento o un incidente de seguridad podría afectar los datos.

Por eso debemos conservar copias recuperables.

Una estrategia conocida es la regla 3-2-1:

  • Tres copias de los datos, contando la original.
  • Dos tipos de medios o ubicaciones de almacenamiento.
  • Una copia fuera del servidor principal.

Esta regla es una referencia útil, pero no sustituye un análisis de riesgos.

Para servicios importantes también conviene utilizar respaldos inmutables o protegidos contra eliminación y establecer objetivos de recuperación.

16. ¿Qué debemos respaldar?

En un servidor Apache con PHP podemos necesitar:

/etc/apache2/
/etc/php/
/etc/ssh/
/etc/letsencrypt/
/etc/fail2ban/
/var/www/
Bases de datos
Archivos de aplicaciones
Tareas programadas
Configuraciones de servicios

No todos los directorios deben copiarse de la misma manera.

Por ejemplo, copiar directamente archivos internos de una base de datos mientras está funcionando puede producir una copia inconsistente.

Para MariaDB utilizaremos una exportación lógica.

También debemos identificar archivos que contienen secretos y protegerlos mediante cifrado y controles de acceso.

17. Crear un respaldo de archivos y configuraciones

Crearemos un script Bash.

sudo nano /usr/local/sbin/respaldo-web.sh

Contenido:

#!/bin/bash

set -Eeuo pipefail

UMASK_ANTERIOR=$(umask)
umask 077

DESTINO="/var/backups/oplinux"
FECHA=$(date '+%Y-%m-%d_%H-%M-%S')

mkdir -p "$DESTINO"

ARCHIVO="$DESTINO/web_$FECHA.tar.gz"

tar -czf "$ARCHIVO" \
    -C / \
    etc/apache2 \
    etc/php \
    etc/fail2ban \
    etc/ssh \
    etc/letsencrypt \
    var/www

echo "Respaldo generado: $ARCHIVO"

umask "$UMASK_ANTERIOR"

Explicación

set -Eeuo pipefail ayuda a detectar determinados errores durante la ejecución.

umask 077 establece permisos restrictivos para los archivos nuevos.

DESTINO define dónde almacenaremos las copias.

FECHA permite crear nombres diferentes para cada ejecución.

tar -czf genera un archivo comprimido.

La opción -C / permite utilizar rutas relativas a la raíz del sistema dentro del archivo.

Este ejemplo es apropiado como punto de partida para sitios con archivos que pueden copiarse de manera consistente. Las aplicaciones que modifican archivos durante el respaldo pueden requerir instantáneas o procedimientos específicos.

Proteger el script

sudo chown root:root /usr/local/sbin/respaldo-web.sh
sudo chmod 700 /usr/local/sbin/respaldo-web.sh

Ejecutar una prueba

sudo /usr/local/sbin/respaldo-web.sh

Comprobamos:

sudo ls -lh /var/backups/oplinux/

18. Respaldar MariaDB correctamente

Si nuestras aplicaciones utilizan MariaDB, podemos generar una exportación lógica.

Instalamos el cliente si es necesario:

sudo apt install mariadb-client

Para un servidor MariaDB local configurado con autenticación administrativa mediante socket Unix, podemos utilizar:

sudo mariadb-dump \
  --all-databases \
  --single-transaction \
  --routines \
  --events \
  --triggers \
  > /var/backups/oplinux/bases.sql

¿Qué hace cada opción?

--all-databases solicita exportar todas las bases de datos.

--single-transaction permite obtener una vista consistente para tablas transaccionales compatibles, como InnoDB, bajo determinadas condiciones.

--routines incluye rutinas almacenadas.

--events incluye eventos programados.

--triggers incluye disparadores.

Importante: --single-transaction no garantiza consistencia para todas las clases de tablas ni frente a cambios de esquema simultáneos. Debemos adaptar la estrategia al motor y a las aplicaciones utilizadas.

Evitar dejar contraseñas en comandos

No debemos colocar contraseñas directamente en la línea de comandos ni dentro de scripts legibles por otros usuarios.

Podemos utilizar autenticación por socket, archivos de opciones protegidos o mecanismos específicos de administración de secretos.

19. Automatizar el respaldo de bases de datos

Creamos:

sudo nano /usr/local/sbin/respaldo-bd.sh

Contenido:

#!/bin/bash

set -Eeuo pipefail
umask 077

DESTINO="/var/backups/oplinux"
FECHA=$(date '+%Y-%m-%d_%H-%M-%S')

mkdir -p "$DESTINO"

TEMPORAL=$(mktemp "$DESTINO/.bd_${FECHA}.XXXXXX")

trap 'rm -f "$TEMPORAL"' EXIT

mariadb-dump \
    --all-databases \
    --single-transaction \
    --routines \
    --events \
    --triggers \
    > "$TEMPORAL"

gzip -c "$TEMPORAL" > "$DESTINO/bd_$FECHA.sql.gz"

echo "Respaldo de bases de datos completado."

Este script utiliza un archivo temporal y comprime la exportación únicamente después de que mariadb-dump termina correctamente.

Debemos ejecutarlo con una cuenta que tenga los privilegios necesarios sobre las bases de datos.

Establecemos permisos:

sudo chown root:root /usr/local/sbin/respaldo-bd.sh
sudo chmod 700 /usr/local/sbin/respaldo-bd.sh

Probamos:

sudo /usr/local/sbin/respaldo-bd.sh

Después comprobamos:

sudo ls -lh /var/backups/oplinux/

20. Programar los respaldos con cron

Editamos el crontab de root:

sudo crontab -e

Agregamos:

# Respaldo de archivos y configuraciones a las 02:00
0 2 * * * /usr/bin/flock -n /run/lock/respaldo-web.lock /usr/local/sbin/respaldo-web.sh >> /var/log/respaldo-web.log 2>&1

# Respaldo de bases de datos a las 03:00
0 3 * * * /usr/bin/flock -n /run/lock/respaldo-bd.lock /usr/local/sbin/respaldo-bd.sh >> /var/log/respaldo-bd.log 2>&1

¿Por qué utilizamos flock?

Porque evita que dos ejecuciones del mismo trabajo se superpongan mientras el bloqueo esté ocupado.

Esto resulta útil cuando una copia tarda más de lo esperado.

Debemos comprobar que el directorio de bloqueo existe y que la cuenta que ejecuta la tarea puede utilizarlo.

¿Es suficiente guardar los respaldos en el VPS?

No.

Si el servidor se pierde completamente, las copias almacenadas en el mismo disco también podrían desaparecer.

Por eso debemos mantener una copia externa.

21. Enviar respaldos fuera del VPS con restic

Restic es una herramienta de respaldos que permite almacenar copias cifradas y deduplicadas en diferentes destinos.

Podemos instalarla:

sudo apt install restic

Para un laboratorio utilizaremos un repositorio local, pero en producción debemos configurar un destino externo independiente, como un servidor de respaldos mediante SFTP o un almacenamiento de objetos compatible.

Crear un repositorio de laboratorio

sudo mkdir -p /var/backups/restic-lab

Restic necesita una contraseña para cifrar el repositorio.

Podemos crear un archivo de contraseña protegido:

sudo install -m 600 -o root -g root /dev/null /root/.restic-password

Editamos:

sudo nano /root/.restic-password

Introducimos una contraseña larga y única.

La contraseña debe conservarse también fuera del VPS, mediante un procedimiento seguro. Si perdemos la clave del repositorio, podemos perder el acceso a los respaldos.

Inicializamos:

sudo restic \
  -r /var/backups/restic-lab \
  --password-file /root/.restic-password \
  init

Crear un respaldo

sudo restic \
  -r /var/backups/restic-lab \
  --password-file /root/.restic-password \
  backup /var/backups/oplinux

Consultar las copias

sudo restic \
  -r /var/backups/restic-lab \
  --password-file /root/.restic-password \
  snapshots

¿Cómo utilizarlo en producción?

Debemos sustituir el repositorio local por un destino externo y configurar su autenticación de forma segura.

Por ejemplo, restic admite repositorios mediante SFTP y diferentes servicios de almacenamiento.

También debemos definir una política de retención y proteger las copias frente a borrados accidentales o maliciosos.

No conviene almacenar las únicas credenciales del respaldo exclusivamente dentro del servidor que estamos protegiendo.

22. Verificar y restaurar respaldos

Un respaldo no debe considerarse confiable solamente porque existe un archivo .tar.gz o .sql.gz.

Debemos comprobar que puede utilizarse.

Comprobar un archivo tar

sudo tar -tzf /var/backups/oplinux/ARCHIVO.tar.gz | head

Sustituimos ARCHIVO.tar.gz por el nombre real.

Esto permite revisar el contenido y detectar determinados problemas de integridad.

Comprobar un respaldo SQL comprimido

sudo gzip -t /var/backups/oplinux/ARCHIVO.sql.gz

Comprobar restic

sudo restic \
  -r /var/backups/restic-lab \
  --password-file /root/.restic-password \
  check

Restaurar en un directorio de pruebas

sudo mkdir -p /root/prueba-restauracion
sudo restic \
  -r /var/backups/restic-lab \
  --password-file /root/.restic-password \
  restore latest \
  --target /root/prueba-restauracion

Después inspeccionamos los archivos restaurados.

¿Y las bases de datos?

Lo recomendable es restaurar una copia en una instancia de pruebas, no directamente sobre la base de datos de producción.

Por ejemplo, una exportación SQL puede importarse mediante el cliente de MariaDB después de revisar su contenido y preparar un entorno aislado.

Una exportación con --all-databases puede contener instrucciones que afectan múltiples bases y cuentas. No debemos importarla indiscriminadamente en un servidor activo.

La prueba de restauración debe incluir también la comprobación funcional de la aplicación.


PARTE IV. MONITOREO DEL SERVIDOR

23. ¿Por qué necesitamos monitoreo?

Un servidor puede parecer que funciona correctamente mientras desarrolla problemas.

Por ejemplo:

  • El disco alcanza el 95 %.
  • PHP-FPM agota sus procesos disponibles.
  • Apache deja de responder.
  • Un certificado está próximo a vencer.
  • La memoria disponible disminuye.
  • La base de datos deja de aceptar conexiones.

Si no tenemos monitoreo, podríamos descubrir el problema únicamente cuando un visitante nos avise.

El objetivo es detectar fallas antes de que afecten gravemente el servicio.

24. Comprobar el estado de Apache y PHP-FPM

Podemos revisar manualmente:

systemctl is-active apache2
systemctl is-active php8.4-fpm

Si están funcionando, normalmente obtendremos:

active

Pero debemos comprender una limitación importante.

Que un servicio esté activo no significa necesariamente que una página web esté funcionando correctamente.

Apache puede estar ejecutándose mientras una aplicación devuelve errores 500.

Por eso debemos combinar comprobaciones del servicio con solicitudes HTTP reales.

25. Crear un script de monitoreo básico

Instalamos las herramientas necesarias:

sudo apt install curl mailutils

Crearemos un script que compruebe servicios, disco y una página web.

sudo nano /usr/local/sbin/monitoreo-web.sh

Contenido:

#!/bin/bash

set -Eeuo pipefail

ALERTAS=""

FECHA=$(date '+%Y-%m-%d %H:%M:%S')

if ! systemctl is-active --quiet apache2; then
    ALERTAS+="Apache2 no está activo.\n"
fi

if ! systemctl is-active --quiet php8.4-fpm; then
    ALERTAS+="PHP-FPM no está activo.\n"
fi

USO=$(df -P / | awk 'NR==2 {gsub(/%/, "", $5); print $5}')

if [ "$USO" -ge 90 ]; then
    ALERTAS+="El disco raíz está al ${USO}%.\n"
fi

if ! curl \
    --silent \
    --show-error \
    --fail \
    --max-time 10 \
    --output /dev/null \
    https://ejemplo.com/; then

    ALERTAS+="La página web no respondió correctamente.\n"
fi

if [ -n "$ALERTAS" ]; then

    {
        echo "Fecha: $FECHA"
        echo
        printf "%b" "$ALERTAS"
    } | mail -s "ALERTA: Servidor web" root

fi

Debemos reemplazar https://ejemplo.com/ por una URL real.

¿Qué comprueba?

Primero verifica que Apache esté activo.

Después comprueba PHP-FPM.

Posteriormente consulta el porcentaje de ocupación del sistema de archivos raíz.

Finalmente realiza una solicitud HTTP.

La opción --fail hace que curl devuelva un código de error para determinadas respuestas HTTP fallidas.

La opción --max-time 10 limita el tiempo máximo de la operación.

Limitaciones

Este script puede generar correos repetidos si el problema continúa.

Para producción conviene agregar control de estados, deduplicación, escalamiento de alertas y comprobaciones externas.

También debemos comprobar que Postfix o el sistema de correo utilizado pueda entregar los mensajes.

26. Programar el monitoreo

Concedemos permisos:

sudo chown root:root /usr/local/sbin/monitoreo-web.sh
sudo chmod 700 /usr/local/sbin/monitoreo-web.sh

Probamos manualmente:

sudo /usr/local/sbin/monitoreo-web.sh

Después editamos:

sudo crontab -e

Agregamos:

*/5 * * * * /usr/local/sbin/monitoreo-web.sh >> /var/log/monitoreo-web.log 2>&1

Así se ejecutará cada cinco minutos.

¿Qué pasa si el VPS se apaga?

El script también dejará de funcionar.

Por eso debemos utilizar un sistema de monitoreo externo para detectar caídas completas del servidor.

Un monitor externo puede realizar solicitudes periódicas desde otra infraestructura y avisarnos cuando el sitio no responda.

27. Monitorear certificados HTTPS

Los certificados TLS tienen una fecha de vencimiento.

Podemos consultar la información de un certificado con OpenSSL:

echo | openssl s_client \
  -connect ejemplo.com:443 \
  -servername ejemplo.com \
  2>/dev/null \
  | openssl x509 -noout -dates

Esto mostrará las fechas de validez del certificado presentado.

Comprobar si vence en menos de treinta días

echo | openssl s_client \
  -connect ejemplo.com:443 \
  -servername ejemplo.com \
  2>/dev/null \
  | openssl x509 -checkend 2592000 -noout

2592000 representa treinta días en segundos.

El comando permite comprobar si el certificado seguirá siendo válido durante ese periodo.

Sin embargo, una comprobación de vencimiento no verifica por sí sola toda la cadena de confianza ni que el certificado corresponda correctamente al dominio.

También debemos comprobar la renovación automática de Certbot:

sudo certbot renew --dry-run

Y revisar sus registros cuando corresponda.

28. Monitorear CPU, memoria y procesos

Linux incluye varias herramientas útiles.

Consultar memoria

free -h

Consultar carga del sistema

uptime

Consultar procesos

top

Consultar almacenamiento

df -h

Consultar inodos

df -i

Un servidor puede quedarse sin inodos aunque todavía tenga espacio disponible.

Instalar herramientas adicionales

sudo apt install htop sysstat

htop permite observar procesos y consumo de recursos de forma interactiva.

sysstat proporciona herramientas como sar para consultar estadísticas del sistema cuando su recopilación está habilitada.

¿Qué valores debemos vigilar?

No existe un umbral universal.

Un VPS con 2 GB de RAM tendrá necesidades diferentes a uno con 16 GB.

Debemos observar el comportamiento habitual del servidor y establecer límites adecuados para su carga real.

29. Monitorear PHP-FPM

PHP-FPM puede proporcionar información sobre sus pools mediante una página de estado.

En el archivo del pool:

/etc/php/8.4/fpm/pool.d/empresa-a.conf

Podemos agregar:

pm.status_path = /fpm-status

Después debemos configurar cuidadosamente el acceso desde Apache.

Nunca debemos publicar libremente la página de estado de PHP-FPM en Internet.

Puede revelar información sobre los procesos y el funcionamiento de la aplicación.

Para producción conviene exponerla únicamente a sistemas de monitoreo autorizados, utilizando una configuración que no permita el acceso público.

También podemos revisar los registros de PHP-FPM:

sudo journalctl -u php8.4-fpm --since "1 hour ago"

Y buscar advertencias relacionadas con límites de procesos.

Por ejemplo, cuando un pool alcanza pm.max_children, puede ser necesario investigar el consumo de memoria, las solicitudes lentas y la capacidad del servidor.

Aumentar indiscriminadamente el número de procesos podría agotar la memoria disponible.


PARTE V. ADMINISTRACIÓN SEGURA DEL VPS

30. Crear una cuenta administrativa

En un VPS no conviene realizar todas las operaciones cotidianas iniciando sesión directamente como root.

Podemos crear un usuario administrativo:

sudo adduser administrador

Después lo agregamos al grupo sudo:

sudo usermod -aG sudo administrador

Comprobamos:

id administrador

Este usuario podrá realizar operaciones administrativas mediante sudo, según la política configurada.

¿Por qué no utilizar root para todo?

Porque root tiene acceso completo al sistema.

Un comando equivocado ejecutado como root puede modificar o eliminar archivos críticos.

Utilizar una cuenta administrativa separada ayuda a mantener un mayor control operativo.

Sin embargo, una cuenta con permisos sudo también puede obtener privilegios elevados, por lo que debemos protegerla adecuadamente.

31. Configurar acceso SSH mediante claves

SSH permite administrar el VPS de forma remota.

Una medida importante consiste en utilizar autenticación mediante claves públicas.

En nuestra computadora local podemos generar una clave Ed25519:

ssh-keygen -t ed25519 -C "administracion-vps"

El comando generará una clave privada y una clave pública.

La clave privada debe permanecer protegida en nuestro equipo.

Podemos instalar la clave pública en el servidor mediante:

ssh-copy-id administrador@IP_DEL_SERVIDOR

Después probamos:

ssh administrador@IP_DEL_SERVIDOR

Debemos confirmar que podemos iniciar sesión correctamente antes de modificar los métodos de autenticación.

32. Fortalecer la configuración SSH

En Debian podemos utilizar un archivo independiente dentro de:

/etc/ssh/sshd_config.d/

Primero debemos comprobar qué archivos y directivas están activos.

Creamos:

sudo nano /etc/ssh/sshd_config.d/99-oplinux-hardening.conf

Ejemplo:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes

Explicación

PermitRootLogin no impide iniciar sesión directamente como root mediante SSH.

PasswordAuthentication no deshabilita la autenticación SSH mediante contraseña.

KbdInteractiveAuthentication no deshabilita la autenticación interactiva por teclado.

PubkeyAuthentication yes permite la autenticación mediante claves públicas.

Advertencia: antes de aplicar esta configuración debemos comprobar que la cuenta administrativa puede acceder mediante una clave válida y utilizar sudo.

Validar SSH

sudo sshd -t

Si no aparecen errores, podemos revisar la configuración efectiva:

sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication'

Debemos tener en cuenta que la configuración efectiva puede depender de bloques Match y del contexto de conexión.

Después recargamos el servicio correspondiente:

sudo systemctl reload ssh

Conservamos la sesión actual abierta y probamos una nueva conexión.

No debemos cerrar la sesión original hasta confirmar que el nuevo acceso funciona.

33. Configurar el firewall del VPS

UFW permite administrar reglas de firewall de manera sencilla.

Instalamos:

sudo apt install ufw

Antes de habilitarlo debemos permitir SSH.

Para el puerto habitual:

sudo ufw allow 22/tcp

Permitimos HTTP:

sudo ufw allow 80/tcp

Permitimos HTTPS:

sudo ufw allow 443/tcp

Podemos establecer políticas predeterminadas:

sudo ufw default deny incoming
sudo ufw default allow outgoing

Después comprobamos las reglas:

sudo ufw show added

Finalmente:

sudo ufw enable

Consultamos:

sudo ufw status verbose

¿Qué puertos no deberíamos exponer innecesariamente?

Por ejemplo:

3306 - MySQL/MariaDB
6379 - Redis
11211 - Memcached

Estos servicios normalmente deben permanecer accesibles únicamente desde redes o procesos autorizados.

No debemos abrirlos a todo Internet salvo que exista una necesidad específica y una arquitectura de seguridad adecuada.

También debemos revisar los puertos publicados por Docker, ya que algunas configuraciones de contenedores pueden interactuar con las reglas del firewall de maneras que requieren atención adicional.

34. Configurar actualizaciones automáticas de seguridad

Debian dispone de herramientas para instalar determinadas actualizaciones automáticamente.

Podemos utilizar:

sudo apt install unattended-upgrades

Después ejecutamos:

sudo dpkg-reconfigure unattended-upgrades

La herramienta permite configurar la instalación automática de actualizaciones según los repositorios habilitados.

¿Por qué son importantes?

Porque las vulnerabilidades descubiertas en paquetes pueden corregirse mediante actualizaciones de seguridad.

Sin embargo, debemos considerar que algunas actualizaciones pueden requerir reinicios o afectar servicios.

En producción conviene establecer una política de mantenimiento, supervisar los resultados y comprobar que las aplicaciones siguen funcionando.

Consultar actualizaciones pendientes

apt list --upgradable

Consultar si es necesario reiniciar

En sistemas Debian puede existir el archivo:

/var/run/reboot-required

Podemos comprobarlo:

if [ -f /var/run/reboot-required ]; then
    echo "El sistema requiere reinicio."
fi

La ausencia de este archivo no garantiza que todos los cambios del sistema se hayan aplicado completamente.

35. Proteger credenciales y archivos de configuración

Una aplicación web puede utilizar contraseñas de bases de datos, claves de API y otros secretos.

No debemos colocar estas credenciales dentro de archivos accesibles públicamente.

Por ejemplo, en Laravel encontramos:

.env

Este archivo debe permanecer fuera del DocumentRoot.

También debemos evitar:

  • Subir contraseñas a repositorios públicos.
  • Incluir secretos en registros.
  • Compartir archivos de configuración por canales inseguros.
  • Guardar credenciales dentro de scripts con permisos demasiado amplios.
  • Utilizar la misma contraseña para todos los servicios.

Comprobar permisos

ls -l /ruta/del/proyecto/.env

En una aplicación donde PHP necesita leer el archivo, debemos permitir el acceso solamente a la identidad correspondiente.

Por ejemplo:

sudo chown root:sitio-a /var/www/empresa-a/.env
sudo chmod 640 /var/www/empresa-a/.env

Esto permite que root y los procesos pertenecientes al grupo sitio-a puedan acceder según los permisos establecidos.

Debemos comprobar que el archivo existe, que pertenece al proyecto correcto y que la aplicación puede leerlo.

La configuración concreta depende del modelo de despliegue y del framework.

36. Preparar un procedimiento de recuperación

Un servidor puede fallar incluso cuando hemos aplicado medidas de seguridad.

Por eso debemos saber qué hacer antes de que ocurra un incidente.

Un procedimiento básico debería responder:

¿Cómo recuperamos el acceso si SSH deja de funcionar?

Utilizando la consola de recuperación del proveedor del VPS.

¿Cómo restauramos Apache si una configuración falla?

Conservando copias de los archivos de configuración y validando los cambios mediante apache2ctl configtest.

¿Cómo recuperamos una base de datos?

Restaurando una exportación verificada en un entorno controlado.

¿Qué hacemos si el VPS es comprometido?

Debemos aislar el sistema afectado, conservar la evidencia necesaria, identificar el alcance del incidente y recuperar los servicios desde una base confiable.

No es recomendable asumir que basta con eliminar un archivo sospechoso para considerar limpio un servidor comprometido.

Plan de recuperación recomendado

  1. Identificar los servicios afectados.
  2. Evitar que el incidente continúe.
  3. Conservar registros y evidencia relevante.
  4. Determinar qué datos y credenciales pudieron verse comprometidos.
  5. Preparar un servidor limpio cuando sea necesario.
  6. Restaurar aplicaciones y datos desde copias verificadas.
  7. Rotar las credenciales afectadas.
  8. Comprobar funcionamiento, integridad y seguridad.
  9. Documentar el incidente y las acciones correctivas.

En entornos con datos personales o información de clientes también debemos considerar las obligaciones legales y contractuales aplicables.


PARTE VI. COMPROBACIONES FINALES PARA PRODUCCIÓN

37. Verificar los servicios principales

Apache:

systemctl status apache2

PHP-FPM:

systemctl status php8.4-fpm

SSH:

systemctl status ssh

Fail2ban:

systemctl status fail2ban

Firewall:

sudo ufw status verbose

38. Verificar configuraciones

Validar Apache:

sudo apache2ctl configtest

Consultar VirtualHosts:

sudo apache2ctl -S

Validar PHP-FPM:

sudo php-fpm8.4 -t

Validar SSH:

sudo sshd -t

Comprobar Fail2ban:

sudo fail2ban-client status

39. Verificar que los sitios responden

Podemos realizar solicitudes:

curl -I https://ejemplo.com

Y:

curl -I https://www.ejemplo.com

Debemos comprobar que los códigos HTTP y las redirecciones corresponden al comportamiento esperado.

También debemos revisar los certificados:

sudo certbot certificates

Y probar su renovación:

sudo certbot renew --dry-run

40. Lista final de seguridad

Antes de publicar aplicaciones reales, conviene comprobar lo siguiente:

ComponenteComprobación
ApacheConfiguración válida y módulos necesarios
VirtualHostsDominios y DocumentRoot correctos
PHP-FPMPools independientes cuando corresponda
PermisosAcceso mínimo necesario
SSHClaves públicas y acceso administrativo probado
FirewallSolo puertos necesarios
Fail2banFiltros verificados y bloqueos funcionales
ModSecurityReglas probadas y falsos positivos revisados
HTTPSCertificados válidos y renovación comprobada
RespaldosCopias externas y restauración probada
MonitoreoAlertas internas y supervisión externa
ActualizacionesPolítica de mantenimiento establecida
RecuperaciónProcedimiento documentado y probado

41. ¿Significa esto que nuestro servidor ya es completamente seguro?

No.

La seguridad informática no es un estado permanente que alcanzamos después de ejecutar una lista de comandos.

Es un proceso continuo.

Un servidor puede tener Apache, PHP-FPM, Fail2ban y ModSecurity correctamente configurados y seguir siendo vulnerable debido a una aplicación desactualizada, un plugin inseguro, una contraseña comprometida o un error de permisos.

También debemos considerar que un VPS con múltiples clientes tiene requisitos de aislamiento diferentes a los de un servidor que aloja únicamente proyectos propios.

Los pools PHP-FPM ayudan a separar procesos, pero no constituyen una frontera de seguridad equivalente a una máquina virtual.

Si necesitamos alojar aplicaciones de clientes que no confían entre sí, debemos evaluar arquitecturas de aislamiento más fuertes.

42. Conclusión

En la primera parte de este tutorial aprendimos a instalar Apache2 y publicar páginas web en Debian 13.

En esta segunda parte avanzamos hacia la administración de un servidor de producción.

Aprendimos a separar aplicaciones mediante usuarios y pools PHP-FPM, proteger accesos con Fail2ban, estudiar el uso de ModSecurity con OWASP CRS, crear respaldos automáticos y comprobar que pueden restaurarse.

También desarrollamos herramientas básicas de monitoreo y revisamos medidas fundamentales para proteger SSH, las credenciales y el sistema operativo.

La principal enseñanza es que poner un servidor en producción no consiste únicamente en conseguir que una página sea visible desde Internet.

También debemos garantizar, dentro de las posibilidades de nuestra infraestructura, que el servidor pueda mantenerse, supervisarse, actualizarse y recuperarse cuando algo falle.

Un administrador no solamente debe saber instalar servicios.

También necesita comprender sus riesgos, interpretar sus registros y disponer de procedimientos para responder ante problemas.


OPERSLINUX — Aprende Linux comprendiendo cómo funciona.

La práctica constante, la documentación y la revisión de cada configuración son fundamentales para desarrollar habilidades de administración de servidores Linux.

Deja un comentario