VPS servidor Linux debian, primeros pasos
cadirPro
VPS con Debian 13 + Nginx, webs estáticas, seguro, fácil de migrar y mantener
Trabajamos en dos ordenadores distintos, el nuestro que llamamos local y el servidor en la nube que llamamos VPS.
-
PASO 1 usuario + SSH seguro
-
PASO 2 Instala nftables (config básica)
-
PASO 3 Instala fail2ban
-
PASO 4 Instala nginx Comprueba:
ss -tulpn # Debe salir: 22 80 Nada más.
Arquitectura recomendada para tu caso
Con 4 vCPU y 4GB RAM en Debian 13:
🔹 En el HOST (fuera de Docker)
nginx
certbot
nftables
fail2ban
🔹 En Docker
jitsi (stack completo)
django
postgres
etherpad
redis (si lo necesitas)
Orden correcto ahora
Vamos a hacer esto bien:
- Terminar seguridad base
- Instalar nginx
- Configurar subdominios
- Configurar HTTPS
- Instalar Docker
- Levantar Django
- Levantar Etherpad
- Levantar Jitsi (lo último) # Jitsi siempre el último.
FASE 1 — Primer acceso y usuario
Terminal LOCAL
ssh root@IP_DEL_SERVIDOR # Conéctate como root
Terminal VPS
apt update && apt upgrade -y # Actualiza el sistema
adduser nuevoAdministrador # crea usuario “nuevoAdministrador”
usermod -aG sudo nuevoAdministrador # añadir al grupo sudo
adduser web # crea usuario “web”.
# Nunca trabajarás como root.
# no rellene datos.
usermod -aG sudo web # se puede añadir al grupo sudo y luego quitar
su - web # comprueba
sudo whoami # debe decir root
sudo userdel -r web # si necesitas eliminar el usuario
FASE 3 — Firewall del proveedor
Activar este firewall, abrir puertos 22, 80 y 443 cerrar todos los demás puertos.
También se puede cambiar el puerto 22 por otro puerto p.ej. 22222: Permite TCP puerto 22222 (SSH personalizado) con estado NEW, ESTABLISHED.
FASE 4 — Crear claves de acceso SSH, permisos ssh y abrir terminales
Las claves SSH son archivos:
ls ~/.ssh/ # ver si ya tienes alguna
id_rsa id_rsa.pub # p.ej. un par clave privada/pública
id_ed25519 id_ed25519.pub # otro par clave privada/pública
id_ecdsa id_ecdsa.pub # otro par clave privada/pública
ssh-keygen -t ed25519 -C "comentario opcional" # generar una nueva?
# Te pregunta dónde guardarla y si quieres contraseña.
# Si ya tienes una y no quieres perderla, pon un nombre distinto
# como /home/miUsuarioLocal/.ssh/id_ed25519_servidor2.
# Varias claves tiene sentido si trabajas con distintos servidores
# o clientes y quieres poder revocar el acceso a uno sin afectar a los demás,
# o por seguridad si una clave se compromete.
Terminal LOCAL
ssh-copy-id web@IP # Copia tu clave SSH, desde tu PC:
# si hay varias claves
ssh-copy-id -i /home/miUsuarioLocal/.ssh/id_ed25519_servidor2.pub web@IP
ssh web@IP # Luego prueba
¿Qué necesita un usuario NO root para entrar por clave? Para el usuario user1 por ejemplo, debe existir:
/home/user1/.ssh/authorized_keys
Con permisos correctos y la clave pública correspondiente. Pasos para permitir acceso SSH por clave a usuario no root. Supongamos que el usuario es user1.
adduser user1 # Crear usuario si no existe
mkdir -p /home/user1/.ssh # Crear carpeta .ssh
chmod 700 /home/user1/.ssh # Dar permisos
Copiar clave pública:
a. Desde tu máquina local:
ssh-copy-id -i ~/.ssh/tu_clave.pub user1@IP
b. O manualmente:
nano /home/user1/.ssh/authorized_keys # Pegas la clave pública.
Luego:
chmod 600 /home/user1/.ssh/authorized_keys
chown -R user1:user1 /home/user1/.ssh
ls -la ~/.ssh
drwx------ 2 user1 user1 4096 ... .ssh
-rw------- 1 user1 user1 571 ... authorized_keys
Permisos correctos
drwx------ → 700
d = directorio
rwx para el propietario
--- para grupo
--- para otros
-rw------- → 600
= archivo
rw para propietario
--- grupo
--- otros
También comprueba esto (muy importante)
ls -ld ~ # Tu carpeta home NO debe ser escribible por otros.
# Lo normal es:
drwxr-xr-x # Eso es 755 → correcto.
drwxrwxrwx # Si fuera algo como SSH fallaría.
# Eso es EXACTAMENTE lo que SSH exige.
¿Es mejor usar usuario normal en vez de root? -> Sí. Mucho mejor.
Configuración profesional:
PermitRootLogin no
PasswordAuthentication no
Y trabajar así:
ssh user1@IP
sudo -i
Eso reduce muchísimo el riesgo. Diferencia práctica:
Acceso root directo -> recomendado solo en emergencia
Acceso usuario + sudo -> recomendado Producción profesional
Paso 1. Comprueba que tu clave funciona
LOCAL. Desde tu máquina local:
ls ~/.ssh/
ssh -i ~/.ssh/tu_clave root@IP_DEL_SERVIDOR
Si entra sin pedir contraseña → seguimos.
Paso 2. Abre una segunda sesión SSH
Mantén abiertas:
Terminal A (la actual)
Terminal B (nueva conexión con clave)
Ahora tienes red de seguridad.
Paso 3. Copia de seguridad sshd_config del servidos
SERVIDOR:
cat ~/.ssh/authorized_keys # Comprueba que tienes clave configurada
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak # copia de seguridad
Si algo falla:
sudo cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config
sudo systemctl restart ssh
FASE 5 — Endurecimiento SSH sin riesgo. Vamos a desactivar login por contraseña en el servidor
En terminal del SERVIDOR:
sudo nano /etc/ssh/sshd_config # Edita.
Asegúrate de que tienes:
PubkeyAuthentication yes # Autenticación por clave (debe estar activa)
PasswordAuthentication no # Desactivar contraseña
PermitRootLogin no # Root no puede entrar por SSH nunca.
PermitRootLogin prohibit-password # Significa que
# Root puede entrar SOLO con clave, nunca con contraseña
ChallengeResponseAuthentication no
UsePAM yes
MaxAuthTries 3
LoginGraceTime 30
AllowUsers root u007
Guarda: Ctrl+O → Enter → Ctrl+X
No reinicies aún.
Validación antes de reiniciar (CLAVE). Haz esto:
sudo sshd -t # Si no devuelve nada → configuración válida.
# Si devuelve error → NO reinicies y corrige.
Reiniciar servicio (Tenemos dos terminales abiertos)
sudo systemctl restart ssh # Recarga SSH (sin cortar sesión)
PRUEBA CRÍTICA. En una TERCERA terminal nueva desde tu máquina local:
ssh user1@IP_DEL_SERVIDOR # Si estamos usando user1
ssh root@IP_DEL_SERVIDOR # Si entra → éxito total. (Si usamos root)
# Solo cuando lo confirmes,
# puedes cerrar las otras sesiones.
PLAN DE EMERGENCIA. Si algo falla, entra al panel de nuestro proveedor de Servicios y abre consola web y restaura backup del sshd_config
sudo cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config # copia configur inicial
sudo systemctl restart ssh
Extra (nivel profesional reduce ataques automáticos) finalmente puedes dejarlo así:
sudo nano /etc/ssh/sshd_config # Edita.
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
MaxAuthTries 3
LoginGraceTime 30
MaxSessions 2
UseDNS no
Guarda: Ctrl+O → Enter → Ctrl+X
Probar
sudo sshd -t # Si no devuelve nada → configuración válida.
sudo systemctl restart ssh # Recarga SSH (sin cortar sesión)
ssh user1@IP_DEL_SERVIDOR # Si estamos usando user1
ssh root@IP_DEL_SERVIDOR # Si entra → éxito total. (Si usamos root)
# Solo cuando lo confirmes,
# puedes cerrar las otras sesiones.
Dejamos copia de la nueva configuración.
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.no_passwd
También se puede limitar SSH por firewall (nftables solo desde tu IP)
pero hoy día la mayor parte de servicios de conexión a internet, novistar, vodafone, orange, ... se ofrecen sin IP fija, por ello no vamos a utilizar esta opción.
FASE 6. facilitar acceso al servidor.
LOCAL terminal Copia de seguridad de la configuración ssh y actualizar.
sudo cp /home/user1/.ssh/config /home/user1/.ssh/config.$(date +%F).orig.bak
# hacer copia seguridad
sudo nano ~/.ssh/config # Edita
Pegar:
Host vps-web
HostName IP
User web
Port 22
IdentityFile ~/.ssh/vps_debian
IdentitiesOnly yes
ServerAliveInterval 60
ServerAliveCountMax 3
Guarda: Ctrl+O → Enter → Ctrl+X
Y podemos usar el explorador de archivos NAUTILUS de Ubuntu para acceder al VPS
# + Otras ubicaciones
# Escriba la dirección del servidor:
fstp://vps-web
LOCAL Conectar, abrir dos sesiones LOCAL y hacer copia de seguridad:
ssh vps-web # Si entra sin pedir password → OK
sudo cp /home/userLocal/.ssh/config /home/userLocal/.ssh/config.$(date +%F)-ok.bak
CHECKLIST SSH
LOCAL
ssh usuario@IP_DEL_SERVIDOR ¿Tu clave realmente funciona?
Debe:
- NO pedir contraseña
- Entrar directamente
- Mostrar tu usuario correcto con whoami
- Si pide password → algo no está bien.
Verifica que realmente está usando clave
Una prueba más técnica:
ssh -v usuario@IP_DEL_SERVIDOR
Busca algo como:
- Offering public key
- Authentication succeeded (publickey)
Eso confirma que no está usando password.
Verifica que root NO puede entrar con contraseña
En el servidor, revisa:
sudo grep -E 'PasswordAuthentication|PermitRootLogin' /etc/ssh/sshd_config
Lo ideal sería ver algo como:
PasswordAuthentication no
PermitRootLogin prohibit-password
Validación de configuración (crítica), en el servidor:
sudo sshd -t
# Si no devuelve nada → configuración válida.
# Si devuelve error → no reiniciar.
Verifica puertos abiertos
En el servidor:
sudo ss -tulpn | grep ssh
Debe mostrar algo como:
LISTEN 0 128 0.0.0.0:22
Simulación de ataque (mini test). Desde tu máquina local intenta forzar password:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no usuario@IP
# Debe fallar inmediatamente. Si entra → PasswordAuthentication sigue activo.
Vamos a comprobar la configuración REAL cargada. En el servidor ejecuta:
sudo sshd -T | grep passwordauthentication
Esto muestra la configuración efectiva. Debe devolver:
passwordauthentication no
Si devuelve:
passwordauthentication yes
Entonces está activado en algún archivo adicional.
ls /etc/ssh/sshd_config.d/
50-cloud-init.conf
sudo cat /etc/ssh/sshd_config.d/50-cloud-init.conf
PasswordAuthentication yes
sudo nano /etc/ssh/sshd_config.d/50-cloud-init.conf
PasswordAuthentication no
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no usuario@IP
# Debe fallar inmediatamente. Si entra → PasswordAuthentication sigue activo.
FASE 7 — Firewall con nftables "permisivo"
nftables (firewall)
VPS terminal
sudo apt update
sudo apt install nftables -y # instalar
sudo systemctl enable --now nftables # activar
sudo systemctl start nftables
sudo systemctl status nftables # verificar
sudo cp /etc/nftables.conf /etc/nftables.conf.ori.bak
sudo cp /etc/nftables.conf /etc/nftables.conf.$(date +%F).bak
ls -l /etc/nftables.conf* # Comprobar que se creó bien
# Deberías ver:
# nftables.conf
# nftables.conf.bak
Restaurar si algo falla
sudo cp /etc/nftables.conf.bak /etc/nftables.conf # Restaurar si algo falla
sudo systemctl reload nftables #vuelves al estado anterior.
Antes de aplicar cambios grandes:
sudo nft -c -f /etc/nftables.conf # valida la config sin activarla.
Si hay errores → no se aplica → no te quedas sin SSH.
Crear configuración limpia
sudo nano /etc/nftables.conf # Configuración base
VPS Contenido mínimo seguro:
#!/usr/sbin/nft -f
# Borra todas las reglas cargadas antes.
flush ruleset
# Crea una tabla llamada filter en familia inet.
# inet = IPv4 + IPv6 (ambos a la vez)
table inet filter {
# Cadena INPUT (lo que entra al servidor)
chain input {
# Define la “puerta de entrada”.
# hook input → tráfico que entra al servidor
# policy drop → por defecto, bloquear todo
# Todo prohibido
# Solo lo que yo permita
type filter hook input priority 0;
policy drop;
# Permite tráfico local (localhost).
iif lo accept
# Permite conexiones ya iniciadas. Ejemplo:
# Tú → servidor → responde → vuelve a ti
ct state established,related accept
# Abre el puerto 22 (SSH) a todo el mundo.
tcp dport {22,22222} accept
# Limita a 3 intentos de conexión SSH cada 60 segundos por IP
#tcp dport 22 ct state new limit rate 3/minute accept
# Web
tcp dport {80,443} accept
# Ping Permitir ping
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
}
# Bloquear reenvío. Impide que tu VPS actúe como router.
chain forward {
type filter hook forward priority 0;
policy drop;
}
# Permitir salida. Permite que el servidor salga a Internet.
# Ejemplo: Actualizaciones, Certbot, DNS, Git, Backups
chain output {
type filter hook output priority 0;
policy accept;
}
}
Guardar y salir (nano ctrl+o y ctrl+x)
Servidor:
sudo nft -c -f /etc/nftables.conf # Si no devuelve error → ok.
sudo nft -f /etc/nftables.conf # Aplicar
sudo nft list ruleset # Verificar
sudo cp /etc/nftables.conf /etc/nftables.conf.hardening.bak
Solo SSH + Web, todo lo demás bloqueado
VPS
ss -tulpn
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 127.nn.nn.nn:nn 0.nn.nn.nn:*
FASE 8 — ORDEN PROFESIONAL DE MONTAJE VPS
1 Base del sistema (higiene)
sudo apt update
sudo apt upgrade -y
sudo apt autoremove -y
sudo apt install curl -y
2 Instalar fail2ban. Comprobar que está activo
sudo apt update
sudo apt install fail2ban -y
sudo systemctl status fail2ban # Debe decir active (running).
sudo systemctl start fail2ban
sudo fail2ban-client status sshd
Ahora mismo Fail2ban está casi “en blanco”. No está bloqueando casi nada todavía. Necesita configuración. Crear config local (nunca editar defaults) Fail2ban funciona así:
/etc/fail2ban/jail.conf → original (NO tocar)
/etc/fail2ban/jail.local → tu config
Recomendado endurecer un poco más. Ahora mismo usas defaults. Podemos mejorarlos. Ajustar:
-
Bloqueo más largo
-
Menos intentos
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime = 12h
findtime = 10m
maxretry = 3
backend = systemd
banaction = nftables-multiport
[sshd]
enabled = true
Guardar y salir (nano ctrl+o y ctrl+x)
sudo systemctl restart fail2ban
Ahora Tiempo ban Ahora 12h antes 1h, intentos, ahora 3 antes 5-6 Bots = fuera todo el día. Ver ataques en tiempo real (opcional, muy educativo)
sudo tail -f /var/log/fail2ban.log
Verificar que está protegiendo SSH
sudo fail2ban-client status # Debe mostrar algo como:
Jail list: sshd
Luego:
sudo fail2ban-client status sshd # Debe mostrar:
Currently banned: 0
Total banned: 0
Instalar y configurar utilidades y seguridad
VPS terminal
nano
which nano # nano está instalado
/usr/bin/nano
mc y fastfech
sudo apt install -y \
fail2ban \
curl \
nftables \
screen \
mc
fastfetch
unattended-upgrades
cron
rsync
tmux
unattended-upgrades
sudo apt install unattended-upgrades
# unattended-upgrades is already the newest version (n.nn).
sudo systemctl status unattended-upgrades
No necesitas tocar nada. Los parches de seguridad se aplicarán automáticamente. No utilizan cron, utilizan:
systemctl status apt-daily.timer
systemctl status apt-daily-upgrade.timer
No necesitas cron para esto. Tu sistema se actualiza solo automáticamente.
Idiomas
echo $LANG echo $LC_ALL locale
Tienes C.UTF-8 que es el locale neutro por defecto. Para un VPS web es perfectamente válido y tiene ventajas.
- Los errores salen en inglés, más fácil buscarlos
- Nginx, fail2ban y los logs todos en inglés
- Sin problemas de caracteres raros en scripts Recomendación -> déjalo como está.
- No hay ninguna razón técnica para cambiarlo en un VPS que solo va a servir una web.
- El español solo tendría sentido si ejecutas aplicaciones que muestran texto al usuario final directamente desde el servidor, que no es tu caso.
FASE 15 — Backups
VPS
sudo mkdir -p /opt/backups
Backup solo configuración + usuarios:
-C / significa cambiar de directorio raíz antes de comprimir.
Sin -C /, tar intentaría comprimir rutas absolutas como /etc/, /home/, etc, lo que causa advertencias.
con -C /:
- Archivo más portable - puedes restaurar en cualquier sistema
- Sin warnings - no dice "Removing leading /"
- Más seguro - evita sobrescribir archivos del sistema accidentalmente
Hacer un Backup base minimalista limpio. Esto te sirve como punto de partida antes de montar Docker u otras cosas
sudo tar czf /opt/backups/system_config_$(date +%F).tar.gz \
-C / etc home root srv var/spool/cron/crontabs
# -C / cambiar a raíz comprimir estos directorios
Crear versión base permanente
sudo cp /opt/backups/system_config_YYYY-MM-DD.tar.gz /opt/backups/system_config_clean.tar.gz
Verificar archivo
ls -lh /opt/backups/system_*.tar.gz
tar tzf /opt/backups/system_*.tar.gz | head
- `tar` - programa de archivos
- `c` - crear archivo
- `t` - **list** (listar contenido)
- `z` - **gzip** (leer archivos .gz comprimidos)
- `f` - **file** (archivo siguiente)
- `~/backups/*_system_*.tar.gz` - archivos que coincidan con el patrón
- `| head` - mostrar solo las primeras 10 líneas
# Ver todo el contenido
tar tzf /opt/backups/full_system_*.tar.gz
# Ver cuántos archivos hay
tar tzf /opt/backups/full_system_*.tar.gz | wc -l
# Ver solo archivos de configuración
tar tzf /opt/backups/full_system_*.tar.gz | grep etc/
# Buscar un archivo específico
tar tzf /opt/backups/full_system_*.tar.gz | grep "nginx.conf"
Recuperar backup
sudo apt update
sudo apt upgrade -y
sudo apt autoremove -y
Crear usuarios
sudo useradd -m -s /bin/bash -G sudo -c "usuario1" usuario1
sudo useradd -m -s /bin/bash -G sudo -c "" usuario2
sudo passwd usuario1
sudo passwd usuario2
Instalar paquetes
sudo apt install -y \
fail2ban \
curl \
nftables \
screen \
mc
fastfetch
unattended-upgrades
cron
rsync
tmux
Restaurar home del backup
sudo mkdir -p /opt/backups && cd /opt/backups
#copiar el archivo
Restaurar configuracion del backup
sudo tar xzf config_users_2026-02-28.tar.gz -C / etc home root srv
Fijar permisos
sudo chown -R tu_usuario:tu_usuario /home/tu_usuario/"
sudo chown -R root:root /root/"
sudo chown -R root:root /srv/"
FASE 16 - Nginx y dominios
sudo apt update
sudo apt install nginx -y # Instalar nginx
sudo systemctl enable nginx
sudo systemctl start nginx
systemctl status nginx # Comprobar que funciona
nano /var/www/html/index.html
Pegar
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Servicio en mantenimiento | web1.com -I</title>
</head>
<body>
<h1>🔧 Servicio en mantenimiento web.com</h1>
<p>Estamos realizando tareas de mantenimiento para mejorar tu experiencia.</p>
<p>El servicio volverá a estar disponible en breve.</p>
Trabajando en ello...
</body>
</html>
Guardar y salir (nano ctrl+o y ctrl+x)
Redirigir dominios a la IP. En el panel donde compraste el dominio, busca DNS / Zona DNS / Registros. Añade:
Registro A
Tipo Nombre Valor
A @ TU_IP
A www TU_IP
Abre en el navegador http://midominio.com → deberías ver la página de nginx por defecto o si ya la has creado a /var/www/html/index.html debería ver lo que has puesto en tu index.html
FASE 17 - Certbot letsencrypt
Instalar certbot
sudo apt update
sudo apt install -y certbot python3-certbot-nginx
certbot --version
Lo que debes comprobar antes de pedir los certificados
Haremos copia de las configuraciones originales.
sudo mkdir /etc/nginx/backups
sudo cp /etc/nginx/sites-available /etc/nginx/backups/sites-available
sudo cp /etc/nginx/sites-enabled /etc/nginx/backups/sites-enabled
sudo cp /etc/nginx/nginx.conf /etc/nginx/backups/nginx.conf
1. Comprobar
Que exista:
/var/www/html/index.html
Verifica que el site activo (probablemente default)
sudo nano /etc/nginx/sites-availables/default
y tenga:
root /var/www/html;
index index.html index.htm;
Añadir
server {
listen 80;
listen [::]:80;
server_name dominio.com www.dominio1.com subdominio1.dominio1.com;
root /var/www/html;
index index.html;
}
Guardar y salir (nano ctrl+o y ctrl+x)
2. Probar configuración. Siempre antes:
sudo nginx -t
Si dice:
syntax is ok
test is successful
Entonces:
sudo systemctl reload nginx
Antes de ejecutar certbot, verifica SOLO esto:
sudo mkdir -p /var/www/html/.well-known/acme-challenge
sudo chown -R www-data:www-data /var/www/html/.well-known
3. Cómo emitir los certificados (varios certificados separados)
Certificado 1
sudo certbot certonly --webroot \
-w /var/www/html \
-d dominio1.com \
-d wwww.dominio1.com
Comando recomendado (modo profesional)
sudo certbot certonly --webroot \
-w /var/www/html \
-d dominio1.com \
-d www.dominio1.com \
--email tuemail@dominio.com \
--agree-tos \
--no-eff-email \
--non-interactive
Qué hace cada parámetro
--email → obligatorio en modo no interactivo
--agree-tos → acepta términos automáticamente
--no-eff-email → no te suscribe a mailing
--non-interactive → evita preguntas
Resultado correcto:
...
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/dominio1.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/dominio1.com/privkey.pem
This certificate expires on 2026-05-30.
....
Comprueba que certbot tiene renovación automática activa:
sudo systemctl list-timers | grep certbot
respuesta ->
Sun 2026-03-01 15:52:36 UTC 4h 12min Sun 2026-03-01 05:35:25 UTC 6h ago certbot.timer certbot.service
Veamos que funciona la renovación
sudo certbot renew --dry-run
respuesta ->
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/....conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Account registered.
Simulating renewal of an existing certificate for .... and .....
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/..../fullchain.pem (success)
importante
Si es la primera vez que usas certbot en el servidor, ejecuta primero:
sudo certbot register \
--email tuemail@dominio.com \
--agree-tos \
--no-eff-email
Esto registra la cuenta de Let's Encrypt una sola vez.
FASE 18 Configuramos nginx para cambiar de http a https
Creamos archivo nuevo:
sudo nano /etc/nginx/sites-available/dominio1.com
Y pegas esto:
# ===============================
# dominio1.com
# ===============================
# HTTP → redirección a HTTPS
server {
listen 80;
listen [::]:80;
server_name dominio1.com www.dominio1.com;
return 301 https://$host$request_uri;
}
# HTTPS
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name dominio1.com www.dominio1.com;
root /var/www/html;
index index.html;
ssl_certificate /etc/letsencrypt/live/dominio1.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dominio1.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
location / {
try_files $uri $uri/ =404;
}
# Logs para depuración (Útiles para ver si Docker responde)
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}
Guardar y salir (nano ctrl+o y ctrl+x)
Activarlo
sudo ln -s /etc/nginx/sites-available/dominio1.com /etc/nginx/sites-enabled/
Desactivar el default, muy importante para evitar conflictos:
sudo rm /etc/nginx/sites-enabled/default
Comprobar configuración
sudo nginx -t
Debe salir limpio sin warnings. Y luego:
sudo systemctl reload nginx
Ahora prueba en navegador
http://dominio1.com
https://dominio1.com
https://www.dominio1.com
También puedes Probar
curl http://dominio1.com
curl -i http://dominio1.com
curl -L http://dominio1.com # comprobar que resuelve
Debe:
Redirigir desde HTTP automáticamente
Mostrar la página de mantenimiento
Tener candado válido
Comprueba que nftables está funcionando
sudo nft list ruleset # Debes tener -> tcp dport 80 accept y tcp dport 443 accept
sudo tar czf /opt/backups/system_https_$(date +%F).tar.gz \
-C / etc home root srv var/spool/cron/crontabs
# -C / cambiar a raíz comprimir estos directorios
Crear versión base permanente
sudo cp /opt/backups/system_https_YYYY-MM-DD.tar.gz /opt/backups/system_https_clean.tar.gz
FASE 19 - Crear esctructura.
sudo mkdir -p /srv/docker/{django,etherpad,jitsi}
sudo chown -R $USER:$USER /srv/docker
FASE 20 - Instalar docker
No uses apt install docker.io. Instalamos Docker oficial (docker-ce).
🐳 Paso 1 — Limpiar posibles restos
sudo apt remove docker docker-engine docker.io containerd runc
Respuesta:
Package 'docker' is not installed, so not removed
Error: Unable to locate package docker-engine
🐳 Paso 2 — Instalar dependencias
sudo apt update
sudo apt install ca-certificates curl gnupg
🐳 Paso 3 — Añadir clave oficial Docker
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
🐳 Paso 4 — Añadir repositorio oficial
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
🐳 Paso 5 — Instalar Docker CE
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
🐳 Paso 6 — Activar y comprobar
sudo systemctl enable docker
sudo systemctl start docker
sudo systemctl status docker
🐳 Paso 7 — Permitir usar docker sin sudo
sudo usermod -aG docker $USER
Después:
newgrp docker
(O cerrar sesión y volver a entrar)
🧪 Probar que funciona
docker run hello-world
Debe descargar imagen y mostrar mensaje de éxito.
FASE 21 — tmux monitorización y gestion
Como estás gestionando una infraestructura, te recomiendo mucho usar paneles (splits) en tmux. Es muy útil tener varios paneles en un solo terminal.
Comandos rápidos en tmux: Ctrl+b y d nluego en el terminal tmux kill-server Ctrl+b y luego %: Divide la pantalla en vertical. Ctrl+b y luego ": Divide la pantalla en horizontal. Ctrl+b y luego o: Salta entre paneles
Lo ideal es configurar tmux para que, con un solo comando, te abra un "centro de mando" donde veas todo tu ecosistema Nginx, Redes, logs, htop de un vistazo.
Aquí tienes una guía rápida y un script para automatizarlo.
-
Guía Rápida de Supervivencia (Atajos Pro)
En tmux, todo empieza con el prefijo Ctrl + b. Acción Atajos Dividir Vertical Ctrl + b seguido de % Dividir Horizontal Ctrl + b seguido de " Navegar entre paneles Ctrl + b seguido de Flechas o o Cerrar panel Escribe exit o Ctrl + d Zoom en un panel Ctrl + b seguido de z (repite para volver) Desacoplar (Salir sin cerrar) Ctrl + b seguido de d Volver a entrar tmux attach tmux attach → tmux a (o tmux att) tmux new-session → tmux new tmux list-sessions → tmux ls tmux detach → tmux det
Script de Configuración: "El Centro de Mando"
Crea un archivo llamado ~/bin/mimonitor.sh en tu carpeta personal del VPS. Este script abrirá una o mas sesiones de tmux dividida en paneles estratégicos.
nano ~/bin/mimonitor.sh
#!/bin/bash
SESSION="infra"
CONTAINER="django-infra"
# 1. Comprobar si el contenedor está corriendo
STATUS=$(docker inspect -f '{{.State.Running}}' $CONTAINER 2>/dev/null)
if [ "$STATUS" != "true" ]; then
echo -e "\e[31m[!] Alerta: El contenedor $CONTAINER no parece estar activo.\e[0m"
echo "Iniciando tmux de todos modos para revisión..."
sleep 1
fi
# 2. Comprobar si la sesión ya existe
tmux has-session -t $SESSION 2>/dev/null
if [ $? -ne 0 ]; then
# Crear sesión y nombrar la primera ventana como "Monitor"
tmux new-session -d -s $SESSION -n "Monitor"
# --- VENTANA 1: MONITORIZACIÓN ---
# Panel superior izquierda: Fastfetch
tmux send-keys -t $SESSION:Monitor "fastfetch" C-m
# Dividir horizontal para Logs (abajo)
tmux split-window -v -t $SESSION:Monitor
tmux send-keys -t $SESSION:Monitor "docker logs -f $CONTAINER" C-m
# Dividir vertical el panel superior para Nginx (derecha)
tmux split-window -h -t $SESSION:Monitor.0
tmux send-keys -t $SESSION:Monitor "sudo tail -f /var/log/nginx/django_access.log" C-m
# --- VENTANA 2: DESARROLLO ---
# Crear nueva pestaña llamada "Codear" en la carpeta del proyecto
# -c especifica el directorio de inicio
tmux new-window -t $SESSION -n "Codear" -c "/srv/mi-infra/app"
tmux send-keys -t $SESSION:Codear "ls -F" C-m
fi
# 3. Seleccionar la ventana principal y entrar
tmux select-window -t $SESSION:Monitor
tmux a -t $SESSION
Guardar y salir (nano ctrl+o y ctrl+x)
chmod +x ~/bin/mimonitor.sh
añade export PATH="$HOME/bin:$PATH" al final de tu archivo
nano ~/.bashrc
export PATH="$HOME/bin:$PATH"
Luego haz source ~/.bashrc y ya podrás lanzarlo simplemente escribiendo abre.esm desde cualquier carpeta.
Configuración Visual (.tmux.conf)
Para que tu tmux en Debian 13 se vea profesional y sea más fácil de usar con el ratón, crea el archivo nano ~/.tmux.conf y pega esto:
# Habilitar el ratón para redimensionar y seleccionar paneles
set -g mouse on
# Mejorar los colores
set -g default-terminal "screen-256color"
# Barra de estado personalizada (Estilo "Chalet")
set -g status-bg black
set -g status-fg cyan
set -g status-left-length 20
set -g status-left "#[fg=green]VPS: #[fg=white]#H "
set -g status-right "#[fg=yellow]Red: 172.20.10.200 #[fg=white]| %H:%M"
# Cambiar el color del panel activo para no perderse
set -g pane-active-border-style fg=brightblue
Guía de navegación para tu nueva interfaz:
- Ctrl + b y luego n: Siguiente ventana (next).
- Ctrl + b y luego p: Ventana anterior (previous).
- Ctrl + b y luego 0 o 1: Ir directamente al número de ventana.
- Cambiar de pestaña (Monitor <-> Codear): Ctrl + b y luego n (Next) o p (Previous).
- Moverte entre los 3 paneles del Monitor: Ctrl + b y luego las flechas del teclado.
- Hacer "Zoom" en los logs: Si ves algo raro en los logs y quieres pantalla completa, ve al panel de logs y pulsa Ctrl + b y luego z. Repite para volver al mosaico.
- Salir (Seguro): Ctrl + b y luego d. Todo seguirá funcionando en el VPS.
- Salida "Modo Cierre Total" (Cerrar sesión)
Si quieres que la sesión de tmux desaparezca y que los comandos que tenías abiertos (como el tail de los logs) se detengan.
Opción A (Panel a panel): Escribe exit en cada panel hasta que se cierren todos. Cuando cierres el último, tmux se apagará solo.
Opción B (Comando rápido): Si estás fuera o en un panel y quieres matar toda la sesión de golpe:
tmux kill-session -t infra
Opción C (Desde dentro): Ctrl + b y luego escribe :kill-session (con los dos puntos delante) y pulsa Enter.
Si quieres limpiar todas las sesiones de golpe sin escribir el nombre (asumiendo que solo tienes una o quieres cerrarlas todas), el comando más rápido es:
tmux kill-server
Tabla de atajos resumidos para gestión de sesiones
Si te gusta ahorrar pulsaciones de teclas, aquí tienes los equivalentes:
Comando Completo Versión Resumida Qué hace
tmux attach-session -t tmux a -t Entrar en una sesión
tmux list-sessions tmux ls Ver qué sesiones hay vivas
tmux new-session -s tmux new -s Crear sesión con nombre
tmux kill-session -t tmux kill-ses -t Cerrar una sesión concreta
tmux rename-session -t tmux rename -t Cambiar el nombre a la sesión
FASE 22 — monitorización básica
VPS
sudo apt install htop iotop iftop vnstat logwatch
-
CPU / RAM / disco
-
Servicios caídos
-
Logs claros
-
Alertas simples
-
Todo local (sin SaaS raro)
htop systemctl status nginx systemctl status ssh systemctl status fail2ban sudo journalctl -f sudo apt install vnstat sudo systemctl enable vnstat sudo systemctl start vnstat sudo systemctl status vnstat
Crear carpeta scripts
mkdir -p ~/bin # crear carpeta de scripts
nano ~/bin/monitor.sh # crar script
Pegar
#!/bin/bash
INFORME_DIR="$HOME/logs"
INFORME="$INFORME_DIR/accesos-vps.log"
mkdir -p "$INFORME_DIR"
{
echo "════════════════════════════════════════"
echo "📅 ENTRADA: $(date '+%Y-%m-%d %H:%M:%S')"
echo "════════════════════════════════════════"
echo ""
echo "💾 MEMORIA:"
free -h | awk 'NR==2{printf " %s / %s (Libre %s)\n", $3, $2, $4}'
echo ""
echo "💿 DISCO:"
df -h / | awk 'NR==2{printf " %s / %s (Libre %s)\n", $3, $2, $4}'
echo ""
echo "⚡ CPU:"
uptime | awk -F'load average:' '{print " "$2}'
echo ""
echo "🌍 IP de este VPS:"
hostname -I | awk '{print " "$1}'
echo ""
echo "🌐 CONEXIONES SSH ACTIVAS:"
ss -tn state established | grep ":22"
echo " Total: $(ss -tn state established | grep -c ":22") sesiones"
echo ""
echo "👤 SESIONES ABIERTAS:"
who | awk '{print " "$0}'
echo ""
echo "🔑 TU SESIÓN ACTUAL:"
if [ -n "$SSH_CLIENT" ]; then
echo " $SSH_CLIENT"
else
echo " (no es sesión SSH)"
fi
echo ""
echo "🛡️ Fail2ban:"
sudo fail2ban-client status sshd 2>/dev/null | grep -E "Currently|Total|Banned" || echo " No data"
echo ""
echo "💾 BACKUPS:"
BACKUP_DIR="$HOME/web-backup/snapshots"
LOG="$HOME/web-backup/logs/backup.log"
# Último backup
LAST=$(ls -1dt "$BACKUP_DIR"/20* 2>/dev/null | head -1)
if [ -z "$LAST" ]; then
echo " ❌ Sin backups"
else
DATE=$(basename "$LAST")
SIZE=$(du -sh "$LAST" | cut -f1)
AGE=$(( ( $(date +%s) - $(stat -c %Y "$LAST") ) / 3600 ))
echo " 📅 Último: $DATE"
echo " 📦 Tamaño: $SIZE"
echo " ⏱️ Edad: ${AGE}h"
if [ "$AGE" -gt 48 ]; then
echo " ⚠️ ALERTA: Backup antiguo"
else
echo " ✅ OK"
fi
fi
# Último error
if grep -q "rsync error" "$LOG" 2>/dev/null; then
echo " ❌ Errores detectados"
tail -3 "$LOG" | sed 's/^/ /'
else
echo " 🟢 Sin errores recientes"
fi
echo ""
echo "📦 Updates:"
apt list --upgradable 2>/dev/null | grep -c "/" | awk '{print " "$1}'
echo ""
echo "📊 TRÁFICO RED:"
vnstat
echo ""
echo "🔐 Últimos OK:"
{
sudo journalctl -u ssh --no-pager 2>/dev/null | grep Accepted
grep "Accepted" /var/log/auth.log 2>/dev/null
} | tail -3 || echo " (sin datos)"
echo ""
echo "🚨 Últimos fallos:"
{
sudo journalctl -u ssh --no-pager 2>/dev/null | grep Failed
grep "Failed" /var/log/auth.log 2>/dev/null
} | tail -3 || echo " (sin datos)"
} >> "$INFORME"
tail -160 "$INFORME"
# Mantener solo las últimas 500 líneas
tail -1000 ~/logs/accesos-vps.log > ~/logs/accesos-vps.log.tmp
mv ~/logs/accesos-vps.log.tmp ~/logs/accesos-vps.log
echo ""
echo "📋 COMANDOS ÚTILES:"
echo " sudo fail2ban-client status sshd → IPs bloqueadas"
echo " ss -tn state established → conexiones activas"
echo " ss -tn state established | grep ':22' → conexiones activas"
echo " curl -s ipinfo.io/ip → mi IP pública"
echo " curl -s ipinfo.io/IP → info de una IP"
echo " sudo journalctl -u ssh | grep Accepted → accesos OK"
echo " sudo journalctl -u ssh | grep Failed → intentos fallidos"
echo ""
fastfetch
Guardar y salir (nano ctrl+o y ctrl+x)
chmod +x ~/bin/monitor.sh # Dar permisos
nano ~/.bashrc
Pegar
if [[ -n "$SSH_CONNECTION" ]]; then
~/bin/monitor.sh
fi
source ~/.bashrc # Recarga
Guardar y salir (nano ctrl+o y ctrl+x)
sudo visudo
Añadir
web ALL=(ALL) NOPASSWD: /usr/bin/fail2ban-client
web ALL=(ALL) NOPASSWD: /usr/bin/journalctl
web ALL=(root) NOPASSWD: /usr/bin/fail2ban-client status sshd
Guardar y salir (nano ctrl+o y ctrl+x)
which fail2ban-client
/usr/bin/fail2ban-client
Alertas automáticas (sin estar conectado)
nano ~/bin/check-alerts.sh
#!/bin/bash
ALERT=0
MSG=""
# Disco
DISK=$(df / | awk 'NR==2{print $5}' | tr -d '%')
if [ "$DISK" -gt 80 ]; then
ALERT=1
MSG+="Disco >80% ($DISK%)\n"
fi
# Memoria
MEM=$(free | awk '/Mem:/ {printf("%.0f"), $3/$2*100}')
if [ "$MEM" -gt 80 ]; then
ALERT=1
MSG+="RAM >80% ($MEM%)\n"
fi
# Fail2ban
BANS=$(fail2ban-client status sshd 2>/dev/null | grep "Currently banned" | awk '{print $4}')
if [ "${BANS:-0}" -gt 20 ]; then
ALERT=1
MSG+="Muchos bans: $BANS\n"
fi
# SSH brute force
FAILS=$(journalctl -u ssh -S -10min | grep Failed | wc -l)
if [ "$FAILS" -gt 30 ]; then
ALERT=1
MSG+="Muchos fallos SSH: $FAILS\n"
fi
if [ "$ALERT" -eq 1 ]; then
echo -e "$(date)\n$MSG" >> "$HOME/logs/alerts.log"
fi
chmod +x ~/bin/check-alerts.sh # Permisos
crontab -e Automatizar con cron
Añadir:
*/10 * * * * /home/web/bin/check-alerts.sh
Alertas automáticas (recomendado). Si quieres que te avise si algo peta Monit puede:
-
Reiniciar nginx si cae
-
Avisar si disco se llena
-
Avisar si RAM se dispara
sudo apt install monit # Instala monit sudo systemctl enable --now monit # Actívalo
FASE 23 — Backups
VPS
sudo apt install rsync
# rsync -aAXHv --delete --exclude 'nombre_del_directorio' /origen/ /destino/
mkdir -p ~/web-backup/{snapshots,logs}
nano ~/bin/backup.sh
Pegar
#!/bin/bash
set -euo pipefail
DEST="$HOME/web-backup"
SNAP="$DEST/snapshots"
LOG="$DEST/logs/backup.log"
DATE=$(date +"%Y-%m-%d_%H-%M")
CURRENT="$SNAP/$DATE"
HOME_SNAP="$CURRENT/home"
ETC_SNAP="$CURRENT/etc"
VAR_SNAP="$CURRENT/var"
SYS_SNAP="$CURRENT/system"
LATEST="$SNAP/latest"
mkdir -p \
"$HOME_SNAP" \
"$ETC_SNAP" \
"$VAR_SNAP" \
"$SYS_SNAP" \
"$DEST/logs"
exec >> "$LOG" 2>&1
echo "==============================="
echo "🕒 Backup: $DATE"
echo "==============================="
echo ""
echo "📊 Tamaño previo:"
du -sh /home/web/web1.com 2>/dev/null || true
du -sh /home/web/miweb2.com 2>/dev/null || true
du -sh /home/web/miweb5.es 2>/dev/null || true
du -sh /home/web/miweb3.es 2>/dev/null || true
du -sh /home/web/* 2>/dev/null || true
du -sh "$HOME/logs" 2>/dev/null || true
du -sh "$HOME/bin" 2>/dev/null || true
du -sh "$HOME/config" 2>/dev/null || true
du -sh /etc/nginx 2>/dev/null || true
du -sh /var/spool/cron 2>/dev/null || true
echo ""
echo "📦 Guardando paquetes..."
dpkg --get-selections > "$SYS_SNAP/packages.list"
apt-mark showmanual > "$SYS_SNAP/packages-manual.list"
cp /var/log/apt/history.log "$SYS_SNAP/apt-history.log"
echo ""
echo "🚀 Iniciando rsync..."
LINK=""
# if [ -d "$LATEST" ]; then
# LINK="--link-dest=$LATEST"
# echo "Usando incremental desde: latest"
# fi
if [ -L "$LATEST" ]; then
LINK="--link-dest=$(readlink -f "$LATEST")"
echo "🔗 Incremental desde latest"
fi
# ----------------------------
# HOME / WEBS
# ----------------------------
echo "🌐 Backup HOME"
rsync -aAXHv --delete \
$LINK \
--exclude "$DEST" \
"$HOME/web1.com" \
"$HOME/miweb2.pm" \
"$HOME/miweb5.es" \
"$HOME/miweb3.es" \
"$HOME/logs" \
"$HOME/bin" \
"$HOME/config" \
"$HOME/.ssh" \
"$HOME/.bashrc" \
"$HOME/.profile" \
"$HOME/.config" \
"$HOME/.bash_history" \
"$HOME_SNAP/"
# ----------------------------
# ETC
# ----------------------------
echo "⚙️ Backup /etc"
rsync -aAXHv --delete \
$LINK \
/etc/nginx \
/etc/ssh \
/etc/fail2ban \
/etc/nftables.conf \
/etc/nftables.d \
/etc/systemd/system \
/etc/cron* \
"$ETC_SNAP/"
# ----------------------------
# VAR
# ----------------------------
echo "📂 Backup /var"
rsync -aAXHv --delete \
$LINK \
/var/spool/cron \
"$VAR_SNAP/"
# ----------------------------
# ACTUALIZAR LATEST
# ----------------------------
rm -f "$LATEST"
ln -s "$CURRENT" "$LATEST"
# ----------------------------
# ROTACIÓN
# ----------------------------
echo "🧹 Aplicando rotación..."
cd "$SNAP"
ls -1dt 20* | tail -n +15 | while read -r old; do
echo "🗑️ Eliminando backup antiguo: $old"
#rm -rf "$old"
done
# ls -1dt 20* # Lista backups por fecha (más nuevos primero)
# tail -n +15 # Desde el 15 en adelante → viejos
# rm -rf # Borra
# ----------------------------
# FINAL
# ----------------------------
echo "✅ Backup terminado"
echo ""
echo "📦 Tamaño snapshot:"
du -sh "$CURRENT"
chmod +x ~/bin/backup.sh # Dar permisos
./backup.sh # Ejecutar
Recuperar los apt install:
dpkg --set-selections < packages.list apt-get dselect-upgrade
Nota. Para máxima seguridad con rutas absolutas:
BASE="/home/directorio/copias"
ls -1dt "$BASE"/20* 2>/dev/null | tail -n +15 | while read -r old; do
# Validar que la ruta empieza por BASE y contiene formato de fecha
if [[ "$old" =~ ^$BASE/20[0-9]{6} ]]; then
echo "🗑️ Eliminando: $old"
rm -rf "$old"
else
echo "⚠️ Ruta sospechosa bloqueada: $old"
fi
done
Ventaja brutal de esta versión
# Restaurar solo nginx
rsync -a /home/web/web-backup/snapshots/latest/etc/nginx/ /etc/nginx/
# Restaurar solo webs
rsync -a snapshots/latest/home/web/ /home/web/
# Restaurar cron
rsync -a snapshots/latest/var/spool/cron/ /var/spool/cron/
# Reinstalar sistema entero
dpkg --set-selections < packages.list
apt-get dselect-upgrade
Backup automático con cron
sudo apt update # actualizar listas
sudo apt install cron # instalar
sudo systemctl enable cron # habilitar
sudo systemctl start cron # iniciar
systemctl status cron # verificar que está corriendo deberías ver active (running).
crontab -e # Editar cron con nano
0 3 * * * /home/web/bin/backup.sh # Añadir: Cada día a las 03:00
~/bin/backup.sh # Probar ahora
~/bin/monitor.sh # Luego y mirar la sección BACKUPS
FASE 24 — Cambiar puerto 22 a puerto 22222
firewall del PROVEEDOR DEL VPS
Abrir puerto 22222
Qué archivos tengo que modificar para cambiar del puerto 22 al puerton 22222 para ssh? Solo tres archivos en el VPS, uno en el LOCAL y el puerto en el firewall del PROVEEDOR DEL VPS:
VPS - SSH
sudo nano /etc/ssh/sshd_config
Port 22
Port 22222
VPS - Firewall
sudo nano /etc/nftables.conf
tcp dport { 22, 22222, 80, 443 } accept
VPS - fail2ban -
sudo nano /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 22,22222
Después reinicia los servicios:
sudo systemctl restart sshd
sudo systemctl restart ssh
sudo systemctl restart nftables
sudo systemctl restart fail2ban
sudo systemctl status sshd
q o crtl+c
sudo systemctl status ssh
sudo systemctl status nftables
sudo systemctl status fail2ban
sudo nft -c -f /etc/nftables.conf # Aplicar
sudo nft list ruleset # Verificar
LOCAL
sudo nano ~/.ssh/config
Host vps-web
HostName IP
User web
Port 22222
IdentityFile ~/.ssh/vps_debian
IdentitiesOnly yes
ServerAliveInterval 60
ServerAliveCountMax 3
Host vps-web22
HostName IP
User web
Port 22
IdentityFile ~/.ssh/vps_debian
IdentitiesOnly yes
ServerAliveInterval 60
ServerAliveCountMax 3
IMPORTANTE: Antes de cerrar tu sesión actual, abre una segunda terminal y prueba:
sh -p 22222 web@217.76.133.194
Si entra bien, ya puedes cerrar la sesión del puerto 22. Si no entra, tienes la sesión original para arreglar el problema. Cerrar el 22 (solo cuando confirmes que 22222 funciona).
Quita Port 22 de:
sshd_config,
firewall
fail2ban
y reinicia los tres servicios de nuevo (igual que el reinicio anterior)
FASE 25 — Reconfigurar Nginx
Headers de seguridad, dentro del server {}
# Seguridad
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=()" always;
# HSTS (solo si SSL funciona bien)
#add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Bloquear archivos peligrosos, añade en cada server:
# Bloquear archivos ocultos
location ~ /\.(?!well-known) {
deny all;
}
# Bloquear backups
location ~* \.(bak|old|swp|tmp)$ {
deny all;
}
Forzar HTTPS en cada dominio, así nadie entra por HTTP.:
server {
listen 80;
server_name web1.com www.web1.com;
return 301 https://$host$request_uri;
}
Rate limiting (anti bots), frena crawlers agresivos, dentro http {} en cada server:
limit_req zone=one burst=20 nodelay
Contenido provisional http
sudo cat /etc/nginx/sites-available/web1.com
server {
listen 80;
listen [::]:80;
server_name web1.com www.web1.com;
root /home/web/web1.com/public;
index index.html;
access_log /var/log/nginx/web1.access.log;
error_log /var/log/nginx/web1.error.log;
# Headers de seguridad
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;
add_header Permissions-Policy "geolocation=(), microphone=()" always;
client_max_body_size 5M;
location / {
try_files $uri $uri/ =404;
}
# Bloquear archivos ocultos
location ~ /\.(?!well-known) {
deny all;
}
# Bloquear backups
location ~* \.(bak|old|swp|tmp)$ {
deny all;
}
}
Contenido provisional http
sudo cat /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
worker_cpu_affinity auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
events {
#worker_connections 768;
worker_connections 2048;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
keepalive_timeout 30;
types_hash_max_size 2048;
server_tokens off; # Recommended practice is to turn this off
client_max_body_size 5M;
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1.2 TLSv1.3; # Dropping SSLv3 (POODLE), TLS 1.0, 1.1
ssl_prefer_server_ciphers off; # Don't force server cipher order.
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
##
# Gzip Settings
##
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1024;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/json
application/xml
application/rss+xml
image/svg+xml;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
sudo chown -R web:www-data /home/web
sudo chmod 755 /home
sudo chmod 750 /home/web
sudo chmod -R 750 /home/web/web1.com
namei -l /home/web/web1.com/public/index.html
Reinicia
sudo nginx -t
sudo systemctl reload nginx