Módulo 17 — Linux como servidor
Introducción
Un servidor Linux es, en esencia, un sistema de propósito general al que se le asigna una tarea concreta —servir páginas web, gestionar bases de datos, resolver nombres de dominio— y se le configura con la mínima superficie de ataque posible para que lo haga de forma fiable y segura durante años. Este módulo recoge todo lo aprendido y lo aplica al despliegue real de servicios.
El enfoque es deliberadamente práctico y orientado a producción. Cada sección parte de un estado limpio —instalación mínima del sistema operativo— y llega a un servicio funcional, seguro y monitorizado. Las interrelaciones son inevitables: la seguridad y hardening del Módulo 14, la red del Módulo 11, el almacenamiento del Módulo 12, los procesos y systemd del Módulo 09 y la monitorización del Módulo 15 son requisitos implícitos en cada servicio que se despliega.
Objetivos de aprendizaje
Al finalizar este módulo, serás capaz de:
- ✅ Preparar un servidor Linux para producción con la checklist de seguridad mínima
- ✅ Desplegar y configurar Nginx y Apache con virtual hosts, compresión y logs estructurados
- ✅ Implementar HTTPS con Let's Encrypt, TLS 1.3 y HSTS con renovación automática
- ✅ Configurar Nginx como proxy inverso con balanceo de carga básico
- ✅ Administrar MariaDB y PostgreSQL a nivel de DBA junior: usuarios, permisos y backups
- ✅ Compartir archivos con NFS (clientes Linux) y Samba (clientes Windows)
- ✅ Desplegar un servidor DNS autoritativo y caching con BIND9 o dnsmasq
- ✅ Configurar un relay de correo con Postfix y entender SPF/DKIM/DMARC
17.1 — Fundamentos: preparar un servidor para producción
Instalación mínima y principio de mínimo privilegio
Un servidor de producción no es un escritorio. La instalación debe ser mínima: sin entorno gráfico, sin paquetes de desarrollo, sin servicios que no van a usarse. Cada paquete instalado es una potencial vulnerabilidad que mantener actualizada.
# Verificar qué servicios están escuchando ANTES de añadir nada
ss -tlnp # TCP, escuchando, numérico, con PID
# En una instalación mínima Ubuntu Server: solo sshd en el 22
# Ver qué paquetes hay instalados
dpkg --get-selections | wc -l # Número de paquetes
apt list --installed 2>/dev/null | grep -v automatic | wc -l
# Eliminar paquetes innecesarios
sudo apt purge snapd # Snap si no lo necesitas
sudo apt autoremove --purge # Dependencias huérfanas
Usuarios de servicio
Cada servicio debe correr con su propio usuario sin privilegios. Un atacante que comprometa nginx no debe poder leer los archivos de postgres.
# Crear un usuario de servicio (sin directorio home, sin shell)
sudo useradd --system --no-create-home --shell /usr/sbin/nologin mi-app
# El usuario existe solo para ejecutar el servicio
# --system: UID bajo (< 1000), excluido de los logins normales
# Ver los usuarios de servicio existentes
getent passwd | awk -F: '$3 < 1000 {print $1, $3, $7}' | sort -n -k2
# Verificar con qué usuario corre cada servicio en ejecución
ps aux | awk '{print $1, $11}' | grep -v "^USER" | sort -u | head -20
# nginx corre como www-data, mysql como mysql, postgres como postgres
Checklist de puesta en producción
SERVIDOR RECIÉN INSTALADO — checklist mínima antes de exponer al mundo:
✓ SISTEMA BASE
[ ] Actualización completa: apt update && apt full-upgrade
[ ] Reinicio si hay nuevo kernel: needrestart -r a
[ ] Zona horaria correcta: timedatectl set-timezone Europe/Madrid
[ ] NTP activo: timedatectl status | grep "NTP synchronized"
[ ] Hostname FQDN correcto: hostnamectl set-hostname srv01.ejemplo.com
✓ ACCESO REMOTO (Módulo 14)
[ ] Solo SSH con clave pública (PasswordAuthentication no)
[ ] Puerto SSH no estándar o fail2ban activo
[ ] Usuario root desactivado para SSH (PermitRootLogin no)
[ ] Usuario de administración en sudo (no trabajar como root)
✓ FIREWALL (Módulo 11)
[ ] Solo los puertos necesarios abiertos
[ ] UFW o nftables activo y persistente
[ ] ufw default deny incoming
✓ ACTUALIZACIONES AUTOMÁTICAS
[ ] unattended-upgrades configurado para parches de seguridad
[ ] Notificaciones por correo de actualizaciones fallidas
✓ MONITORIZACIÓN (Módulo 15)
[ ] node_exporter o similar instalado
[ ] Alertas de disco (>80%), RAM y carga configuradas
✓ BACKUPS (Módulo 18)
[ ] Estrategia 3-2-1 definida antes de poner datos en producción
[ ] Prueba de restauración documentada
# Actualizaciones de seguridad automáticas (Debian/Ubuntu)
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades # Activar con diálogo
# Configuración en /etc/apt/apt.conf.d/50unattended-upgrades
# Ya viene preconfigurado para aplicar solo security updates
# Verificar que funciona:
sudo unattended-upgrade --dry-run --debug 2>&1 | head -20
# Comprobar si hay reinicios pendientes
sudo needrestart -r l # Listar qué necesita reinicio
ls /var/run/reboot-required 2>/dev/null && echo "Reinicio pendiente"
17.2 — Nginx: el servidor web más usado en producción
Nginx fue diseñado desde cero para manejar decenas de miles de conexiones concurrentes con un consumo de memoria predecible. Su modelo asíncrono orientado a eventos —a diferencia del modelo de procesos/hilos de Apache— lo hace especialmente eficiente para servir contenido estático, actuar como proxy inverso y en entornos de alta concurrencia.
Arquitectura y estructura de configuración
Modelo de procesos Nginx:
PID 1: nginx (master) — root, gestiona workers
└── PID 2: nginx (worker) — www-data, maneja conexiones
└── PID 3: nginx (worker) — www-data, maneja conexiones
└── ...
El master lee la configuración, crea/destruye workers, gestiona señales.
Los workers manejan conexiones de forma asíncrona (event loop).
Número de workers = número de CPUs (worker_processes auto).
Estructura de archivos (Debian/Ubuntu):
/etc/nginx/
├── nginx.conf ← configuración principal (global + http block)
├── sites-available/ ← definiciones de sitios (desactivados)
│ ├── default
│ └── mi-sitio.conf
├── sites-enabled/ ← symlinks a sites-available (activos)
│ └── mi-sitio.conf → ../sites-available/mi-sitio.conf
├── conf.d/ ← configuración adicional (incluida automáticamente)
├── snippets/ ← fragmentos reutilizables
│ ├── fastcgi-php.conf
│ └── snakeoil.conf
└── mime.types ← tipos MIME
Instalación y configuración básica
sudo apt install nginx
sudo systemctl enable --now nginx
# Verificar la instalación
nginx -v # Versión
sudo nginx -t # Test de sintaxis de la configuración
curl -I http://localhost # Debe devolver 200 OK
# Habilitar/deshabilitar sitios
sudo ln -s /etc/nginx/sites-available/mi-sitio /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default # Deshabilitar el default
sudo nginx -t && sudo systemctl reload nginx # NUNCA restart si hay tráfico
Virtual host completo para contenido estático
# /etc/nginx/sites-available/ejemplo.com
server {
listen 80;
listen [::]:80; # IPv6 también
server_name ejemplo.com www.ejemplo.com;
# Raíz del sitio web
root /var/www/ejemplo.com/html;
index index.html index.htm;
# Logging con formato personalizado
access_log /var/log/nginx/ejemplo.com.access.log;
error_log /var/log/nginx/ejemplo.com.error.log warn;
# Servir archivos estáticos
location / {
try_files $uri $uri/ =404;
}
# Caché agresiva para assets estáticos (CSS, JS, imágenes)
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off; # No logear peticiones de assets
}
# Denegar acceso a archivos ocultos (.git, .env, etc.)
location ~ /\. {
deny all;
return 404;
}
# Seguridad: no exponer la versión de nginx
server_tokens off;
# Cabeceras de seguridad (refuerza el módulo 14)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
Compresión y rendimiento
# /etc/nginx/conf.d/performance.conf (afecta a todos los sitios)
# Compresión gzip
gzip on;
gzip_vary on;
gzip_min_length 1024; # No comprimir respuestas pequeñas
gzip_comp_level 6; # Nivel 1-9: equilibrio CPU/ratio
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml;
# Buffer y timeouts (ajustar según la aplicación)
client_body_buffer_size 128k;
client_max_body_size 10m; # Tamaño máximo de upload
keepalive_timeout 65;
send_timeout 60;
# Cache de archivos abiertos (reduce syscalls stat())
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
Análisis de logs de acceso
# Formato de log por defecto de nginx:
# 192.168.1.1 - - [01/Jan/2024:10:00:00 +0000] "GET / HTTP/1.1" 200 1234 "-" "curl/7.88"
# IP usr [timestamp] método URL proto code size referer UA
# Las 10 IPs que más peticiones hacen
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# Códigos de error más frecuentes (4xx, 5xx)
awk '$9 >= 400 {print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# URLs más solicitadas
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# Tráfico por hora (para detectar patrones de uso)
awk '{print substr($4, 2, 14)}' /var/log/nginx/access.log | \
cut -d: -f1-2 | sort | uniq -c
# Herramienta dedicada: GoAccess (análisis de logs en tiempo real)
sudo apt install goaccess
goaccess /var/log/nginx/access.log -o /var/www/html/report.html \
--log-format=COMBINED --real-time-html
17.3 — Apache HTTP Server
Apache sigue siendo la primera elección cuando se necesitan módulos dinámicos, archivos .htaccess por directorio, o en stacks de hosting compartido. Su modelo de procesos/hilos es diferente al de nginx, pero sus capacidades para aplicaciones PHP tradicionales y virtualización son muy maduras.
sudo apt install apache2
sudo systemctl enable --now apache2
# Herramientas de gestión
a2ensite mi-sitio.conf # Habilitar sitio (crea symlink)
a2dissite 000-default # Deshabilitar sitio
a2enmod rewrite ssl headers # Habilitar módulos
a2dismod status # Deshabilitar módulo
apache2ctl -t # Test de configuración (como nginx -t)
apache2ctl -M # Listar módulos cargados
Virtual Host con PHP
# /etc/apache2/sites-available/ejemplo.com.conf
<VirtualHost *:80>
ServerName ejemplo.com
ServerAlias www.ejemplo.com
DocumentRoot /var/www/ejemplo.com/html
# Logs por sitio
ErrorLog ${APACHE_LOG_DIR}/ejemplo.com.error.log
CustomLog ${APACHE_LOG_DIR}/ejemplo.com.access.log combined
# Opciones del directorio
<Directory /var/www/ejemplo.com/html>
Options -Indexes +FollowSymLinks # Sin listado de directorios
AllowOverride All # Permitir .htaccess (necesario para WordPress)
Require all granted
</Directory>
# PHP-FPM (mejor rendimiento que mod_php)
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
</FilesMatch>
</VirtualHost>
MPMs y rendimiento
# Apache tiene tres Multi-Processing Modules (MPM):
# prefork: un proceso por conexión — compatible con mod_php, menos eficiente
# worker: hilos + procesos — más eficiente, no compatible con mod_php
# event: como worker pero maneja keep-alive de forma asíncrona — RECOMENDADO con PHP-FPM
# Ver qué MPM está activo
apache2ctl -V | grep MPM
# Cambiar a event MPM (recomendado para PHP-FPM)
sudo a2dismod mpm_prefork mpm_worker 2>/dev/null
sudo a2enmod mpm_event
sudo systemctl restart apache2
# Ajustar el MPM event para producción
# /etc/apache2/mods-enabled/mpm_event.conf:
# <IfModule mpm_event_module>
# StartServers 2
# MinSpareThreads 25
# MaxSpareThreads 75
# ThreadLimit 64
# ThreadsPerChild 25
# MaxRequestWorkers 150
# MaxConnectionsPerChild 0
# </IfModule>
17.4 — HTTPS en producción con Let's Encrypt
HTTPS no es opcional. Desde 2018, los navegadores marcan los sitios HTTP como "No seguro". Let's Encrypt ofrece certificados TLS gratuitos y renovación automática.
Obtener y configurar un certificado
# Instalar certbot (el cliente oficial de Let's Encrypt)
sudo apt install certbot python3-certbot-nginx # Para nginx
# o
sudo apt install certbot python3-certbot-apache # Para Apache
# Obtener un certificado (nginx)
# certbot modifica nginx.conf automáticamente y añade la redirección HTTP→HTTPS
sudo certbot --nginx -d ejemplo.com -d www.ejemplo.com \
--email admin@ejemplo.com --agree-tos --no-eff-email
# Modo standalone (para cuando nginx/apache no está corriendo)
sudo certbot certonly --standalone -d ejemplo.com
# Verificar la instalación
sudo certbot certificates # Listar certificados y fechas de expiración
# Verificar la renovación automática
sudo systemctl status certbot.timer # Timer de systemd (Debian/Ubuntu)
sudo certbot renew --dry-run # Simulacro de renovación
Configuración TLS moderna
# /etc/nginx/sites-available/ejemplo.com (bloque HTTPS)
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name ejemplo.com www.ejemplo.com;
# Certificados de Let's Encrypt
ssl_certificate /etc/letsencrypt/live/ejemplo.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ejemplo.com/privkey.pem;
# Parámetros TLS modernos (guía Mozilla: Intermediate)
# https://ssl-config.mozilla.org/
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off; # TLS 1.3 elige el cifrado el cliente
# Sesiones TLS: evitar el handshake completo en reconexiones
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off; # Deshabilitar por seguridad (forward secrecy)
# OCSP Stapling: el servidor verifica el certificado por el cliente
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/ejemplo.com/chain.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s;
# HSTS: decirle al navegador que SIEMPRE use HTTPS
# (módulo 14: cabecera de seguridad)
# ¡Solo añadir cuando estés SEGURO de que HTTPS funciona!
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
root /var/www/ejemplo.com/html;
# ... resto de la configuración
}
# Redireccion HTTP → HTTPS
server {
listen 80;
listen [::]:80;
server_name ejemplo.com www.ejemplo.com;
return 301 https://$host$request_uri;
}
# Probar la calidad de la configuración TLS
# SSL Labs (requiere que el servidor sea accesible desde Internet):
# https://www.ssllabs.com/ssltest/analyze.html?d=ejemplo.com
# Test local con openssl (módulo 14)
openssl s_client -connect ejemplo.com:443 -tls1_3 </dev/null 2>&1 | head -20
# Herramienta testssl.sh (análisis local completo)
git clone --depth 1 https://github.com/drwetter/testssl.sh.git
cd testssl.sh && ./testssl.sh ejemplo.com
17.5 — Nginx como proxy inverso
El proxy inverso es uno de los patrones más importantes en arquitecturas web modernas: nginx recibe todas las peticiones externas y las reenvía a aplicaciones que corren en puertos locales, añadiendo TLS, compresión, cabeceras y balanceo.
Internet → [Nginx:443] → [App:3000 (Node.js)]
→ [App:8000 (Python/Gunicorn)]
→ [App:8080 (Java/Spring)]
Ventajas del proxy inverso:
• Un solo punto de entrada (443)
• TLS centralizado en nginx (las apps no gestionan certificados)
• Balanceo de carga entre múltiples instancias
• Rate limiting y control de acceso en nginx
• Las apps solo escuchan en localhost (no expuestas directamente)
Configuración de proxy inverso
# /etc/nginx/sites-available/app.ejemplo.com
# Upstream: define el grupo de servidores backend
upstream mi_app {
# Algoritmo de balanceo: por defecto round-robin
server 127.0.0.1:3000; # Instancia 1 local
server 127.0.0.1:3001; # Instancia 2 local
# server 10.0.0.2:3000; # Servidor remoto (si hay cluster)
# Opciones avanzadas:
# server 127.0.0.1:3001 weight=2; # El doble de tráfico
# server 127.0.0.1:3002 backup; # Solo si los otros fallan
# keepalive 32; # Conexiones persistentes al backend
}
server {
listen 443 ssl http2;
server_name app.ejemplo.com;
ssl_certificate /etc/letsencrypt/live/app.ejemplo.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.ejemplo.com/privkey.pem;
# Pasar todas las peticiones al upstream
location / {
proxy_pass http://mi_app;
proxy_http_version 1.1;
# Cabeceras que necesita el backend para saber de dónde viene la petición
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSockets (necesario para apps en tiempo real)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts para el backend
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
# Buffers: si el backend es lento, nginx acumula la respuesta
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
# Health check endpoint (no proxear, responder directamente)
location /nginx-health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
Desplegar una aplicación con systemd
# /etc/systemd/system/mi-app.service
# Aplicación Node.js gestionada por systemd (Módulo 09)
[Unit]
Description=Mi aplicación Node.js
After=network.target
Requires=network.target
[Service]
Type=simple
User=mi-app # Usuario sin privilegios (creado antes)
Group=mi-app
WorkingDirectory=/var/www/mi-app
ExecStart=/usr/bin/node /var/www/mi-app/server.js
Restart=always
RestartSec=5
# Variables de entorno de la app (NO usar para secretos)
Environment=NODE_ENV=production
Environment=PORT=3000
# Cargar secretos desde un archivo protegido
EnvironmentFile=/etc/mi-app/env # chmod 600, propiedad de mi-app
# Hardening de la unidad (Módulo 14)
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/var/www/mi-app/logs /var/www/mi-app/uploads
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now mi-app
sudo systemctl status mi-app
17.6 — Bases de datos para administradores
MariaDB / MySQL
# Instalación
sudo apt install mariadb-server mariadb-client
sudo systemctl enable --now mariadb
# Asistente de seguridad inicial (SIEMPRE ejecutar)
sudo mysql_secure_installation
# Establece clave de root, elimina usuarios anónimos,
# elimina la base de datos test, recarga privilegios
# Conectar como root
sudo mysql -u root # En Debian/Ubuntu: root usa socket auth
# ──── Administración básica ────
# Crear base de datos y usuario (en la consola MySQL)
CREATE DATABASE mi_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'mi_app_user'@'localhost' IDENTIFIED BY 'contraseña_segura';
GRANT ALL PRIVILEGES ON mi_app.* TO 'mi_app_user'@'localhost';
FLUSH PRIVILEGES;
-- Ver usuarios y permisos
SELECT user, host, plugin FROM mysql.user;
SHOW GRANTS FOR 'mi_app_user'@'localhost';
-- Ver el tamaño de las bases de datos
SELECT table_schema, ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'MB'
FROM information_schema.tables GROUP BY table_schema;
# Copias de seguridad con mysqldump
mysqldump -u root mi_app > /backup/mi_app_$(date +%Y%m%d_%H%M).sql
mysqldump -u root --all-databases --single-transaction > /backup/all_dbs.sql
# --single-transaction: backup consistente sin bloquear tablas InnoDB
# Restaurar
mysql -u root mi_app < /backup/mi_app_20240101_0200.sql
# Backup comprimido (más eficiente para bases grandes)
mysqldump -u root mi_app | gzip > /backup/mi_app_$(date +%Y%m%d).sql.gz
gunzip -c /backup/mi_app_20240101.sql.gz | mysql -u root mi_app
# Configuración de rendimiento básica
# /etc/mysql/mariadb.conf.d/50-server.cnf
# [mysqld]
# innodb_buffer_pool_size = 512M # 50-70% de la RAM disponible para MariaDB
# innodb_log_file_size = 128M
# max_connections = 100
# slow_query_log = 1
# slow_query_log_file = /var/log/mysql/slow.log
# long_query_time = 2 # Queries de más de 2 segundos
PostgreSQL
sudo apt install postgresql postgresql-contrib
sudo systemctl enable --now postgresql
# PostgreSQL usa el usuario del SO 'postgres' para administración
sudo -u postgres psql # Entrar a la consola psql
# ──── Administración básica ────
-- Crear rol (usuario) y base de datos
CREATE USER mi_app_user WITH PASSWORD 'contraseña_segura';
CREATE DATABASE mi_app OWNER mi_app_user ENCODING 'UTF8';
-- Ver roles y bases de datos
\du -- Listar roles
\l -- Listar bases de datos
\c mi_app -- Conectar a una BD
\dt -- Listar tablas
\q -- Salir
-- Tamaño de las bases de datos
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database ORDER BY pg_database_size(datname) DESC;
# Control de acceso: pg_hba.conf
# /etc/postgresql/16/main/pg_hba.conf
# Define QUIÉN puede conectar, desde DÓNDE, a QUÉ BD y con qué MÉTODO
# TYPE DATABASE USER ADDRESS METHOD
# local all postgres peer # SO socket, usuario postgres
# local all all md5 # SO socket, otros usuarios con passwd
# host mi_app mi_app_user 127.0.0.1/32 scram-sha-256 # TCP local
# Después de editar pg_hba.conf:
sudo systemctl reload postgresql
# Copias de seguridad
sudo -u postgres pg_dump mi_app > /backup/mi_app_$(date +%Y%m%d).sql
sudo -u postgres pg_dump -Fc mi_app > /backup/mi_app_$(date +%Y%m%d).dump # Formato custom
# Restaurar formato custom (más flexible que SQL plano)
sudo -u postgres pg_restore -d mi_app /backup/mi_app_20240101.dump
# Backup de todas las bases de datos
sudo -u postgres pg_dumpall > /backup/all_$(date +%Y%m%d).sql
# Configuración de rendimiento básica
# /etc/postgresql/16/main/postgresql.conf
# shared_buffers = 256MB # ~25% RAM
# effective_cache_size = 1GB # ~50-75% RAM
# work_mem = 4MB # Por operación de orden/hash
# maintenance_work_mem = 64MB # Para VACUUM, CREATE INDEX
# log_min_duration_statement = 1000 # Log queries > 1s
17.7 — Compartición de archivos: NFS y Samba
NFS: compartir entre sistemas Linux
NFS (Network File System) es el protocolo estándar para compartir archivos entre sistemas Linux/Unix. El cliente monta el directorio remoto como si fuera local.
# ──── SERVIDOR NFS ────
sudo apt install nfs-kernel-server
# Crear el directorio a compartir
sudo mkdir -p /srv/nfs/compartido
sudo chown nobody:nogroup /srv/nfs/compartido
sudo chmod 755 /srv/nfs/compartido
# Definir las exportaciones
# /etc/exports
# /directorio clientes(opciones)
sudo tee /etc/exports << 'EOF'
# Compartir solo con la red 192.168.1.0/24
/srv/nfs/compartido 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
# Opciones importantes:
# rw → lectura-escritura
# ro → solo lectura
# sync → escrituras síncronas (más seguro, más lento)
# async → escrituras asíncronas (más rápido, riesgo de pérdida)
# no_subtree_check → no verificar que el archivo está en el subtree exportado (rendimiento)
# root_squash → mapear root del cliente a nobody (SEGURIDAD)
# no_root_squash → root del cliente conserva sus privilegios (solo para admin)
EOF
# Aplicar cambios y verificar
sudo exportfs -ra # Reexportar todo
sudo exportfs -v # Ver exportaciones activas
sudo systemctl enable --now nfs-kernel-server
# Ver clientes conectados
sudo showmount -a localhost
# ──── CLIENTE NFS ────
sudo apt install nfs-common
# Montar manualmente
sudo mount -t nfs 192.168.1.10:/srv/nfs/compartido /mnt/nfs
# Montar automáticamente con /etc/fstab (Módulo 12)
# 192.168.1.10:/srv/nfs/compartido /mnt/nfs nfs defaults,_netdev 0 0
# _netdev: no montar hasta que la red esté disponible
# Automontaje con autofs (monta al acceder, desmonta cuando no se usa)
sudo apt install autofs
# /etc/auto.master añadir: /mnt/nfs /etc/auto.nfs
# /etc/auto.nfs: compartido -fstype=nfs,rw 192.168.1.10:/srv/nfs/compartido
sudo systemctl restart autofs
ls /mnt/nfs/compartido # Automonta al acceder
Samba: interoperabilidad con Windows
sudo apt install samba samba-common
# Backup de la configuración original
sudo cp /etc/samba/smb.conf /etc/samba/smb.conf.bak
# Configuración básica de Samba
sudo tee /etc/samba/smb.conf << 'EOF'
[global]
workgroup = WORKGROUP
server string = Servidor Linux
# Seguridad: autenticación por usuario
security = user
# Solo permitir SMB2 y SMB3 (no SMBv1, vulnerable)
server min protocol = SMB2
# Cifrado requerido (SMB3)
smb encrypt = required
[compartido]
comment = Carpeta compartida
path = /srv/samba/compartido
browseable = yes
read only = no
valid users = @sambagroup # Solo usuarios del grupo sambagroup
create mask = 0664
directory mask = 0775
force group = sambagroup
[homes]
comment = Carpetas personales
browseable = no
read only = no
valid users = %S # Cada usuario solo ve su carpeta
EOF
# Crear directorio y usuario Samba
sudo mkdir -p /srv/samba/compartido
sudo groupadd sambagroup
sudo useradd -M -s /usr/sbin/nologin sambauser
sudo usermod -aG sambagroup sambauser
# Samba tiene su propia base de datos de contraseñas
sudo smbpasswd -a sambauser # Establece contraseña Samba
sudo smbpasswd -e sambauser # Habilitar usuario
# Verificar configuración
sudo testparm # Parsear y validar smb.conf
sudo systemctl enable --now smbd nmbd
# Ver shares disponibles desde el propio servidor
smbclient -L localhost -U sambauser
17.8 — DNS y DHCP: infraestructura de red propia
dnsmasq: DNS caching + DHCP ligero
dnsmasq es la herramienta perfecta para una red de laboratorio o doméstica: DNS caching con resolución de nombres locales y servidor DHCP en un mismo paquete.
sudo apt install dnsmasq
# /etc/dnsmasq.conf
sudo tee /etc/dnsmasq.conf << 'EOF'
# Interfaz en la que escuchar (no escuchar en loopback solo)
interface=eth0
bind-interfaces
# DNS: reenviar consultas externas a upstream
server=1.1.1.1 # Cloudflare
server=8.8.8.8 # Google
# DNS: cache
cache-size=1000
# DNS: resolver nombres locales desde /etc/hosts
expand-hosts
domain=local.lab
# DHCP: rango y tiempo de arrendamiento
dhcp-range=192.168.1.100,192.168.1.200,24h
# DHCP: asignaciones fijas por MAC (reservas)
dhcp-host=aa:bb:cc:dd:ee:ff,servidor01,192.168.1.10,infinite
dhcp-host=11:22:33:44:55:66,nodo02,192.168.1.11,infinite
# Opciones DHCP para los clientes
dhcp-option=3,192.168.1.1 # Gateway
dhcp-option=6,192.168.1.1 # DNS (nosotros mismos)
dhcp-option=15,local.lab # Dominio
# Bloqueo de publicidad (estilo Pi-hole básico)
# address=/ads.example.com/0.0.0.0
EOF
sudo systemctl enable --now dnsmasq
# Verificar
dig @127.0.0.1 google.com # Debe responder desde la caché
dig @127.0.0.1 servidor01.local.lab # Nombre local
BIND9: servidor DNS autoritativo
Para entornos donde se gestionan zonas DNS propias (dominio público o privado):
sudo apt install bind9 bind9utils
# Estructura de BIND9:
# /etc/bind/named.conf ← archivo principal
# /etc/bind/named.conf.options ← opciones globales
# /etc/bind/named.conf.local ← definición de zonas propias
# /var/cache/bind/ ← archivos de zona
# Configurar BIND como DNS caching recursivo seguro
# /etc/bind/named.conf.options
sudo tee /etc/bind/named.conf.options << 'EOF'
options {
directory "/var/cache/bind";
# Solo responder a clientes de la red local
allow-query { localhost; 192.168.1.0/24; };
allow-recursion { localhost; 192.168.1.0/24; };
# Forwarders upstream
forwarders {
1.1.1.1;
8.8.8.8;
};
dnssec-validation auto;
listen-on { any; };
listen-on-v6 { any; };
};
EOF
# Definir zona interna
# /etc/bind/named.conf.local
cat >> /etc/bind/named.conf.local << 'EOF'
zone "local.lab" {
type master;
file "/var/cache/bind/db.local.lab";
};
zone "1.168.192.in-addr.arpa" { # Zona reversa
type master;
file "/var/cache/bind/db.192.168.1";
};
EOF
# Archivo de zona /var/cache/bind/db.local.lab
sudo tee /var/cache/bind/db.local.lab << 'EOF'
$TTL 3600
@ IN SOA ns1.local.lab. admin.local.lab. (
2024010101 ; Serial (incrementar al cambiar)
3600 ; Refresh
900 ; Retry
604800 ; Expire
3600 ) ; Negative TTL
;
@ IN NS ns1.local.lab.
ns1 IN A 192.168.1.1
servidor01 IN A 192.168.1.10
www IN CNAME servidor01
EOF
# Verificar la configuración
sudo named-checkconf # Verificar named.conf
sudo named-checkzone local.lab /var/cache/bind/db.local.lab # Verificar zona
sudo systemctl enable --now named
dig @127.0.0.1 servidor01.local.lab # Debe resolver a 192.168.1.10
17.9 — Correo electrónico: nociones esenciales
El correo es el servicio más complejo de gestionar correctamente. Este capítulo no pretende convertirte en administrador de correo (un campo especializado), sino en que entiendas la arquitectura y seas capaz de configurar el caso de uso más común: un servidor que envía notificaciones.
La arquitectura del correo
Flujo de un correo de usuario@origen.com a usuario@destino.com:
[Origen] [Destino]
MUA → MTA → MTA → MDA → MUA
(cliente) (SMTP submit) (SMTP entrega) (IMAP)
MUA: Mail User Agent (Thunderbird, Outlook, Gmail web)
MTA: Mail Transfer Agent — Postfix, Exim, Sendmail (envía y reenvía)
MDA: Mail Delivery Agent — Dovecot, Procmail (entrega en el buzón)
El servidor de destino comprueba:
• MX record del dominio destino (DNS: qué servidor recibe el correo)
• SPF: ¿tiene permiso esta IP para enviar por @origen.com?
• DKIM: ¿está firmado el mensaje con la clave privada del dominio?
• DMARC: política de qué hacer si SPF o DKIM fallan
SPF, DKIM y DMARC
SPF (Sender Policy Framework) — registro DNS TXT:
v=spf1 ip4:203.0.113.1 include:_spf.google.com ~all
Dice: "Solo la IP 203.0.113.1 y los servidores de Google pueden enviar
correo con @mi-dominio.com. Tratar el resto con sospecha (~all)."
DKIM (DomainKeys Identified Mail) — registro DNS TXT + firma en cabeceras:
El MTA firma cada mensaje con una clave privada.
El destinatario verifica la firma con la clave pública en DNS.
Garantiza integridad del mensaje y autenticidad del dominio.
DMARC (Domain-based Message Authentication):
Política que dice qué hacer cuando SPF o DKIM fallan:
v=DMARC1; p=reject; rua=mailto:dmarc@mi-dominio.com
p=none: Solo reportar, no rechazar (fase de prueba)
p=quarantine: Poner en spam si falla
p=reject: Rechazar completamente
Postfix como relay de notificaciones
El caso más común en administración de sistemas: el servidor necesita enviar alertas por correo (cron, Nagios, backups fallidos). En lugar de montar un MTA completo, se configura un relay hacia un proveedor externo.
sudo apt install postfix mailutils
# Durante la instalación, seleccionar "Satellite system" o "Internet with smarthost"
# Editar /etc/postfix/main.cf:
sudo postconf -e "relayhost = [smtp.gmail.com]:587"
sudo postconf -e "smtp_use_tls = yes"
sudo postconf -e "smtp_sasl_auth_enable = yes"
sudo postconf -e "smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd"
sudo postconf -e "smtp_sasl_security_options = noanonymous"
sudo postconf -e "smtp_tls_security_level = encrypt"
# Credenciales del relay (usar una app password de Gmail, no la contraseña real)
echo "[smtp.gmail.com]:587 tu_cuenta@gmail.com:tu_app_password" | \
sudo tee /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd # Compilar a formato hash
sudo chmod 600 /etc/postfix/sasl_passwd* # Proteger las credenciales (Módulo 07)
sudo systemctl restart postfix
# Probar el envío
echo "Test desde el servidor" | mail -s "Test relay" destino@ejemplo.com
# Ver la cola de correo
mailq # Cola pendiente
postqueue -f # Intentar reenviar todos en cola
Anexos
A. Puertos de servicios más usados
| Servicio | Puerto | Protocolo |
|---|---|---|
| SSH | 22 | TCP |
| HTTP | 80 | TCP |
| HTTPS | 443 | TCP |
| DNS | 53 | UDP/TCP |
| DHCP | 67/68 | UDP |
| SMTP | 25/587 | TCP |
| IMAP | 143/993 | TCP |
| MySQL/MariaDB | 3306 | TCP |
| PostgreSQL | 5432 | TCP |
| NFS | 2049 | TCP/UDP |
| Samba | 445 | TCP |
B. Interrelaciones con otros módulos
◀ Módulo 07 — Usuarios y permisos
│ Usuarios de servicio (--system, nologin), permisos de archivos de config
◀ Módulo 09 — Procesos y systemd
│ Unidades systemd para cada servicio, hardening de unidades
│ (PrivateTmp, NoNewPrivileges, ProtectSystem)
◀ Módulo 11 — Redes en Linux
│ UFW/nftables para abrir solo los puertos necesarios
│ DNS, DHCP, netstat/ss para diagnóstico de servicios
◀ Módulo 12 — Almacenamiento avanzado
│ /etc/fstab para NFS, LVM para volúmenes de bases de datos
│ Copias de seguridad de BD en volúmenes separados
◀ Módulo 14 — Seguridad y hardening
│ TLS/certbot, cabeceras HTTP de seguridad, SSH hardening
│ Principio de mínimo privilegio en usuarios de servicio
◀ Módulo 15 — Monitorización
│ Logs de acceso de nginx/apache, métricas de postgres, alertas de disco
│ GoAccess para análisis de logs de acceso
◀ Módulo 16 — Virtualización y contenedores
│ Nginx como proxy inverso delante de contenedores Docker
│ Bases de datos en contenedores vs. instaladas en el host
Referencias y Bibliografía
-
Nginx documentation
https://nginx.org/en/docs/ -
Apache HTTP Server documentation
https://httpd.apache.org/docs/2.4/ -
Certbot documentation — ISRG/Let's Encrypt
https://certbot.eff.org/docs/ -
Mozilla SSL Configuration Generator
https://ssl-config.mozilla.org/ -
Let's Encrypt documentation
https://letsencrypt.org/docs/ -
MariaDB Knowledge Base
https://mariadb.com/kb/en/ -
PostgreSQL Documentation 16
https://www.postgresql.org/docs/16/ -
BIND 9 Administrator Reference Manual
https://bind9.readthedocs.io/ -
dnsmasq documentation
http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html -
Postfix documentation
https://www.postfix.org/documentation.html -
NFS-HOWTO — Linux Documentation Project
https://tldp.org/HOWTO/NFS-HOWTO/ -
Samba documentation
https://www.samba.org/samba/docs/ -
High Performance Browser Networking — Ilya Grigorik
https://hpbn.co/ (referencia esencial para HTTP/2, TLS, WebSockets) -
The Linux System Administrator's Guide — Machtelt Garrels
https://tldp.org/LDP/sag/html/ -
SPF Record Syntax — OpenSPF
https://www.openspf.org/SPF_Record_Syntax -
DKIM Overview — dkim.org
http://www.dkim.org/ -
DMARC Overview — dmarc.org
https://dmarc.org/overview/ -
Nginx: The Definitive Guide — Clement Nedelcu
O'Reilly (referencia técnica del proxy inverso y balanceo) -
GoAccess — Real-time web log analyzer
https://goaccess.io/ -
testssl.sh: Testing TLS/SSL encryption
https://testssl.sh/ -
MySQL Performance Blog — Percona
https://www.percona.com/blog/
Preguntas de autoevaluación
- ¿Por qué Nginx es más eficiente que Apache para contenido estático con alta concurrencia? Describe el modelo de procesos de cada uno.
- Explica qué es un virtual host en nginx y cómo nginx decide qué bloque
serverusa para responder una petición. - ¿Qué diferencia hay entre
ssl_protocols TLSv1.2 TLSv1.3y deshabilitar TLS 1.0/1.1? ¿Por qué se evitan versiones antiguas? - ¿Qué es HSTS y por qué no se debe activar hasta que el sitio funcione correctamente en HTTPS?
- En un proxy inverso nginx → Node.js, ¿para qué sirve la cabecera
X-Forwarded-Fory qué problema causa si no se configura? - En MariaDB, ¿cuál es la diferencia entre
GRANT ALL PRIVILEGES ON *.* TOyGRANT ALL PRIVILEGES ON mi_app.* TO? ¿Cuál es más segura y por qué? - ¿Para qué sirve
--single-transactionenmysqldump? ¿Cuándo NO funciona (motores no transaccionales)? - ¿Qué es
pg_hba.confen PostgreSQL? Explica la diferencia entre los métodospeer,md5yscram-sha-256. - ¿Cuándo usarías NFS y cuándo Samba para compartir archivos? ¿Qué protocolo SMB se debe exigir y por qué?
- Explica la diferencia entre los registros DNS de tipo A, CNAME, MX y TXT. ¿Para qué sirve cada uno en el contexto de un servidor web con correo?
- ¿Qué son SPF, DKIM y DMARC? ¿Por qué son los tres necesarios? ¿Qué ocurre si solo tienes SPF pero no DKIM?
- ¿Qué es un "relay de correo" y cuándo es preferible a montar un MTA completo?
Laboratorios prácticos
Lab 17.1 — Nginx básico con virtual host
# 1. Instalar nginx (si no está instalado)
sudo apt install nginx -y
sudo systemctl start nginx
# 2. Crear el directorio del sitio
sudo mkdir -p /var/www/lab-nginx/html
echo "<h1>Lab 17: Nginx funcionando</h1>" | sudo tee /var/www/lab-nginx/html/index.html
# 3. Crear el virtual host
sudo tee /etc/nginx/sites-available/lab-nginx << 'EOF'
server {
listen 8080;
server_name localhost;
root /var/www/lab-nginx/html;
index index.html;
location / { try_files $uri $uri/ =404; }
}
EOF
sudo ln -sf /etc/nginx/sites-available/lab-nginx /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
# 4. Verificar
curl http://localhost:8080
# 5. Ver los logs
sudo tail -5 /var/log/nginx/access.log
# 6. Limpiar
sudo rm /etc/nginx/sites-enabled/lab-nginx
sudo nginx -t && sudo systemctl reload nginx
Lab 17.2 — Self-signed certificate y HTTPS local
# Crear un certificado autofirmado para pruebas locales
# (Para producción usar certbot — Lab 17.4)
sudo mkdir -p /etc/ssl/local
# Generar clave privada y certificado (openssl del módulo 14)
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/local/lab.key \
-out /etc/ssl/local/lab.crt \
-subj "/C=ES/ST=Madrid/L=Madrid/O=Lab/CN=localhost"
# Virtual host con SSL
sudo tee /etc/nginx/sites-available/lab-ssl << 'EOF'
server {
listen 8443 ssl;
server_name localhost;
ssl_certificate /etc/ssl/local/lab.crt;
ssl_certificate_key /etc/ssl/local/lab.key;
root /var/www/lab-nginx/html;
location / { try_files $uri $uri/ =404; }
}
EOF
sudo ln -sf /etc/nginx/sites-available/lab-ssl /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
# Probar (ignorar el error de certificado autofirmado)
curl -k https://localhost:8443
# Limpiar
sudo rm /etc/nginx/sites-enabled/lab-ssl
sudo nginx -t && sudo systemctl reload nginx
Lab 17.3 — MariaDB: crear BD y usuario
# Verificar que MariaDB está instalado
sudo apt install mariadb-server -y 2>/dev/null
sudo systemctl start mariadb
# Crear base de datos y usuario de forma no interactiva
sudo mysql -e "
CREATE DATABASE IF NOT EXISTS lab_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'lab_user'@'localhost' IDENTIFIED BY 'LabPass123!';
GRANT ALL PRIVILEGES ON lab_db.* TO 'lab_user'@'localhost';
FLUSH PRIVILEGES;
SHOW DATABASES;
"
# Verificar la conexión como el nuevo usuario
mysql -u lab_user -pLabPass123! -e "SHOW DATABASES;" 2>/dev/null
# Crear una tabla de prueba
mysql -u lab_user -pLabPass123! lab_db -e "
CREATE TABLE IF NOT EXISTS usuarios (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO usuarios (nombre) VALUES ('Alumno 1'), ('Alumno 2');
SELECT * FROM usuarios;
"
# Hacer un backup
mysqldump -u lab_user -pLabPass123! lab_db > /tmp/lab_db_backup.sql 2>/dev/null
echo "Tamaño del backup: $(wc -c < /tmp/lab_db_backup.sql) bytes"
# Limpiar
sudo mysql -e "DROP DATABASE lab_db; DROP USER 'lab_user'@'localhost';"
rm /tmp/lab_db_backup.sql
Lab 17.4 — NFS: servidor y cliente en el mismo sistema
# Lab simplificado: servidor y cliente en la misma máquina (localhost)
sudo apt install nfs-kernel-server nfs-common -y
# 1. Crear y exportar un directorio
sudo mkdir -p /srv/nfs/lab
echo "Datos en el servidor NFS" | sudo tee /srv/nfs/lab/archivo.txt
echo "/srv/nfs/lab 127.0.0.1(rw,sync,no_subtree_check)" | sudo tee -a /etc/exports
sudo exportfs -ra
sudo exportfs -v | grep lab
# 2. Montar desde localhost
sudo mkdir -p /mnt/nfs-lab
sudo mount -t nfs localhost:/srv/nfs/lab /mnt/nfs-lab
mount | grep nfs-lab
# 3. Verificar que los datos son accesibles
cat /mnt/nfs-lab/archivo.txt
echo "Escrito desde el cliente" | sudo tee /mnt/nfs-lab/cliente.txt
ls /srv/nfs/lab/ # Debe aparecer cliente.txt
# 4. Desmontar y limpiar
sudo umount /mnt/nfs-lab
sudo sed -i '/nfs\/lab/d' /etc/exports
sudo exportfs -ra
Lab 17.5 — DNS local con dnsmasq
# Instalar dnsmasq si no está
sudo apt install dnsmasq -y 2>/dev/null
# Backup de la config original
sudo cp /etc/dnsmasq.conf /etc/dnsmasq.conf.bak 2>/dev/null
# Configuración mínima para lab
sudo tee /etc/dnsmasq.d/lab.conf << 'EOF'
# Solo escuchar en localhost para el lab
listen-address=127.0.0.1
# Resolver nombres de prueba
address=/servidor-lab.local/127.0.0.1
address=/db-lab.local/127.0.0.1
EOF
sudo systemctl restart dnsmasq
# Verificar que resuelve los nombres locales
dig @127.0.0.1 servidor-lab.local +short
dig @127.0.0.1 db-lab.local +short
# Ambos deben devolver 127.0.0.1
# Restaurar
sudo rm /etc/dnsmasq.d/lab.conf
sudo systemctl restart dnsmasq
Lab 17.6 — Nginx como proxy inverso (simulado)
# Simular una aplicación backend con Python (sin dependencias externas)
python3 -m http.server 9000 &
BACKEND_PID=$!
echo "Backend PID: $BACKEND_PID en puerto 9000"
# Configurar nginx como proxy inverso al puerto 9000
sudo tee /etc/nginx/sites-available/lab-proxy << 'EOF'
server {
listen 8090;
server_name localhost;
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
EOF
sudo ln -sf /etc/nginx/sites-available/lab-proxy /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
# Probar: nginx en 8090 proxea a Python en 9000
curl -s http://localhost:8090/ | head -5
echo ""
echo "La misma respuesta directa al backend:"
curl -s http://localhost:9000/ | head -5
# Limpiar
kill $BACKEND_PID 2>/dev/null
sudo rm /etc/nginx/sites-enabled/lab-proxy
sudo nginx -t && sudo systemctl reload nginx
Resumen del módulo
✅ Fundamentos: instalación mínima, usuarios de servicio, checklist de producción, unattended-upgrades
✅ Nginx: modelo asíncrono, virtual hosts, compresión gzip, caché de assets, análisis de logs con awk/GoAccess
✅ Apache: MPM event con PHP-FPM, VirtualHosts, .htaccess, a2ensite/a2enmod
✅ HTTPS: certbot + Let's Encrypt, TLS 1.3, HSTS, OCSP Stapling, SSL Labs, testssl.sh
✅ Proxy inverso: upstream, proxy_pass, cabeceras X-Forwarded-*, WebSockets, balanceo
✅ Bases de datos: MariaDB (usuarios, grants, mysqldump, slow log) y PostgreSQL (pg_hba.conf, pg_dump, tuning)
✅ NFS: exports, opciones (sync/root_squash), montaje, autofs
✅ Samba: SMB2/3, usuarios Samba, shares, smbpasswd, testparm
✅ DNS: dnsmasq (lab/doméstico), BIND9 (autoritativo), registros A/CNAME/MX/TXT, DNSSEC
✅ Correo: arquitectura MTA/MDA/MUA, SPF/DKIM/DMARC, Postfix relay de notificaciones
Próximo paso: Módulo 18 — Automatización y DevOps. Con los servicios desplegados, es hora de automatizar su configuración, versionarla y construir pipelines que los desplieguen sin intervención manual.
Última actualización: 2024-06
Versión: 1.0
Estado: ✅ Listo para enseñanza