Práctica 8 — Proyecto Final: Servidor Web
Contexto — ¿Qué es la pila LEMP?
Sección titulada “Contexto — ¿Qué es la pila LEMP?"LEMP es el acrónimo de:
- Linux — el sistema operativo (ya lo tienes)
- Engine-X (nginx) — el servidor web que recibe las peticiones HTTP
- MariaDB — la base de datos relacional
- PHP — el lenguaje que genera las páginas dinámicas
Es una de las combinaciones más usadas en servidores web de producción. WordPress, Drupal y muchas aplicaciones corren sobre LEMP.
Paso 1 — Preparar el servidor
Sección titulada “Paso 1 — Preparar el servidor"# Asegúrate de que el sistema está actualizadosudo apt update && sudo apt upgrade -y
# Crea los directorios de trabajosudo mkdir -p /var/www/misitesudo mkdir -p /backup/db
# Verifica el estado del firewall (debe estar activo desde la práctica 7)sudo ufw statusPaso 2 — Instalar nginx
Sección titulada “Paso 2 — Instalar nginx"sudo apt install -y nginx
# Inicia y habilita el serviciosudo systemctl start nginxsudo systemctl enable nginx
# Verifica que respondecurl -s -o /dev/null -w "nginx responde: HTTP %{http_code}\n" http://localhost# Debe devolver: HTTP 200Si el firewall está activo, permite el tráfico web:
sudo ufw allow 'Nginx Full'# Esto abre los puertos 80 (HTTP) y 443 (HTTPS)Paso 3 — Instalar MariaDB
Sección titulada “Paso 3 — Instalar MariaDB"sudo apt install -y mariadb-server
# Inicia y habilitasudo systemctl start mariadbsudo systemctl enable mariadb
# Asegurar la instalación: establece contraseña de root, elimina usuarios anónimossudo mysql_secure_installationDurante mysql_secure_installation:
- Enter current password for root: presiona Enter (no hay contraseña aún)
- Switch to unix_socket authentication: n
- Change the root password: y → elige una contraseña segura y anótala
- Remove anonymous users: y
- Disallow root login remotely: y
- Remove test database: y
- Reload privilege tables: y
Verifica que funciona:
sudo mysql -u root -p# Introduce la contraseña que acabas de crear# Debe aparecer el prompt: MariaDB [(none)]>exitPaso 4 — Crear la base de datos y el usuario
Sección titulada “Paso 4 — Crear la base de datos y el usuario"sudo mysql -u root -pEn el prompt de MariaDB, ejecuta estos comandos uno a uno:
-- Crea la base de datosCREATE DATABASE cursodb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Crea el usuario (pon tu propia contraseña)CREATE USER 'cursouser'@'localhost' IDENTIFIED BY 'CursoLinux2024!';
-- Concede todos los permisos sobre la base de datos al usuarioGRANT ALL PRIVILEGES ON cursodb.* TO 'cursouser'@'localhost';
-- Aplica los cambiosFLUSH PRIVILEGES;
-- Verifica que la BD existeSHOW DATABASES;
-- Verifica que el usuario existeSELECT user, host FROM mysql.user WHERE user = 'cursouser';
exitPrueba que el usuario puede conectarse:
mysql -u cursouser -p cursodb# Introduce la contraseña del usuario cursouser# Debe aparecer el prompt: MariaDB [cursodb]>exitPaso 5 — Instalar PHP-FPM
Sección titulada “Paso 5 — Instalar PHP-FPM"sudo apt install -y php-fpm php-mysql php-cli
# Verificar la versión instalada y guardarla (la necesitarás después)php --versionPHP_VERSION=$(php -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;")echo "Versión PHP: $PHP_VERSION"
# El servicio se llama php8.X-fpm (X varía según la versión)sudo systemctl start php${PHP_VERSION}-fpmsudo systemctl enable php${PHP_VERSION}-fpm
systemctl is-active php${PHP_VERSION}-fpm# Debe decir: activePaso 6 — Crear la página de prueba PHP
Sección titulada “Paso 6 — Crear la página de prueba PHP"sudo nano /var/www/misite/index.phpEscribe:
<?php// Información del sistema PHP y la conexión a BD
echo "<h1>Servidor Linux — Práctica Final</h1>";
// Información PHPphpinfo();?>Asigna el propietario correcto:
sudo chown -R www-data:www-data /var/www/misitesudo chmod -R 755 /var/www/misitePaso 7 — Configurar el virtualhost de nginx
Sección titulada “Paso 7 — Configurar el virtualhost de nginx"sudo nano /etc/nginx/sites-available/misiteEscribe (reemplaza 8.X por tu versión de PHP real, por ejemplo 8.3):
server { listen 80; listen [::]:80;
server_name misite.local; root /var/www/misite;
index index.php index.html;
location / { try_files $uri $uri/ =404; }
location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.3-fpm.sock; }
location ~ /\.ht { deny all; }}Activa el virtualhost y desactiva el de por defecto:
# Crea el enlace simbólico para activar el sitiosudo ln -s /etc/nginx/sites-available/misite /etc/nginx/sites-enabled/
# Desactiva el sitio por defecto (opcional pero recomendado)sudo rm /etc/nginx/sites-enabled/default
# Verifica que la configuración de nginx es correcta (sin errores de sintaxis)sudo nginx -t# Debe decir: syntax is ok / test is successful
# Recarga nginx para aplicar la nueva configuraciónsudo systemctl reload nginxPaso 8 — Configurar el dominio local
Sección titulada “Paso 8 — Configurar el dominio local"Para que misite.local resuelva a tu servidor sin necesidad de DNS, añádelo al archivo /etc/hosts:
echo "127.0.0.1 misite.local" | sudo tee -a /etc/hosts
# Verifica que se puede resolverping -c 1 misite.localPrueba que la web responde con PHP:
curl -s http://misite.local | head -20# Debe mostrar HTML, incluyendo información de PHPPaso 9 — Verificar la suma completa
Sección titulada “Paso 9 — Verificar la suma completa"# Los tres servicios deben estar activosfor svc in nginx mariadb php8.3-fpm; do echo "$svc: $(systemctl is-active $svc)"done
# La web responde con HTTP 200curl -s -o /dev/null -w "HTTP: %{http_code}\n" http://misite.local
# La BD y el usuario existensudo mysql -e "SHOW DATABASES; SELECT user FROM mysql.user WHERE user='cursouser';"Paso 10 — Backup automático de la base de datos
Sección titulada “Paso 10 — Backup automático de la base de datos"# Prueba manual primerosudo mysqldump -u root cursodb > /backup/db/cursodb_$(date '+%F').sql
# Verifica que el archivo se creóls -lh /backup/db/Automatiza con cron:
# Edita el crontab del usuario rootsudo crontab -eAñade esta línea al final (ejecuta a las 2:00 AM todos los días):
0 2 * * * mysqldump -u root cursodb > /backup/db/cursodb_$(date '+\%F').sqlVerifica que el cron está guardado:
sudo crontab -lPaso 11 — Securizar el servidor
Sección titulada “Paso 11 — Securizar el servidor"Aplica las medidas de seguridad aprendidas:
# 1. Verifica UFW (de la práctica 7)sudo ufw status verbose
# 2. Verifica fail2bansudo fail2ban-client status sshd
# 3. Verifica que root no puede conectarse por SSHgrep "PermitRootLogin" /etc/ssh/sshd_config
# 4. Establece permisos correctos en el directorio websudo find /var/www/misite -type f -exec chmod 644 {} \;sudo find /var/www/misite -type d -exec chmod 755 {} \;sudo chown -R www-data:www-data /var/www/misite📤 Bloque de entrega (script de corrección)
Sección titulada “📤 Bloque de entrega (script de corrección)"Ejecuta este script completo. El profesor lo revisará contigo:
#!/bin/bashecho "=========================================="echo " ENTREGA PRÁCTICA 8 — PROYECTO FINAL"echo "=========================================="echo ""
# Serviciosecho "--- SERVICIOS ---"for svc in nginx mariadb fail2ban; do echo "$svc: $(systemctl is-active $svc)"donePHP_VER=$(php -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;" 2>/dev/null)echo "php${PHP_VER}-fpm: $(systemctl is-active php${PHP_VER}-fpm 2>/dev/null)"
echo ""echo "--- WEB ---"curl -s -o /dev/null -w "HTTP misite.local: %{http_code}\n" http://misite.local
echo ""echo "--- BASE DE DATOS ---"sudo mysql -e "SHOW DATABASES;" 2>/dev/null | grep cursodb && echo "BD cursodb: OK" || echo "BD cursodb: FALTA"sudo mysql -e "SELECT user FROM mysql.user WHERE user='cursouser';" 2>/dev/null | grep -q cursouser && echo "Usuario cursouser: OK" || echo "Usuario cursouser: FALTA"
echo ""echo "--- VIRTUALHOST ---"test -f /etc/nginx/sites-enabled/misite && echo "Virtualhost activo: OK" || echo "Virtualhost activo: FALTA"test -f /var/www/misite/index.php && echo "index.php: OK" || echo "index.php: FALTA"
echo ""echo "--- SEGURIDAD ---"sudo ufw status | grep "Status:"grep "PermitRootLogin" /etc/ssh/sshd_config
echo ""echo "--- BACKUP ---"sudo crontab -l 2>/dev/null | grep mysqldump && echo "Cron backup: OK" || echo "Cron backup: FALTA"ls -lh /backup/db/ 2>/dev/null | head -5
echo ""echo "=========================================="Ejecuta con:
bash <(cat << 'SCRIPT'# (pega aquí el script de arriba)SCRIPT)O simplemente ejecuta los comandos uno a uno y copia toda la salida.