Ilustración de hardening para un VPS Linux expuesto a Internet, mostrando control de puertos, firewall, acceso SSH, monitoreo, logs y reducción de servicios innecesarios.

Hardening básico y efectivo para un VPS Linux expuesto a Internet

Medidas concretas para reducir superficie de ataque sin inflar innecesariamente la complejidad del servidor.

Tiempo de lectura: 8 min

Exponer un servidor Linux a Internet sin configuración de seguridad activa no es una cuestión de si será atacado, sino de cuándo. Los bots de escaneo masivo encuentran puertos abiertos en menos de 10 minutos tras el aprovisionamiento. Este artículo no vende miedo: entrega controles concretos, aplicables en menos de una hora, que eliminan la mayoría de los vectores de ataque comunes sin añadir complejidad operativa innecesaria.

1. SSH: el primer vector a endurecer

SSH mal configurado es la puerta de entrada más explotada en VPS expuestos. Cambios mínimos en /etc/ssh/sshd_config eliminan la mayoría de los ataques automatizados:

bash# /etc/ssh/sshd_config
Port 2222                        # Cambia el puerto por defecto (reduce ruido de logs)
PermitRootLogin no               # Nunca login directo como root
PasswordAuthentication no        # Solo autenticación por llave pública
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
MaxAuthTries 3                   # Limita intentos antes de cortar
ClientAliveInterval 300          # Cierra sesiones inactivas
ClientAliveCountMax 2
AllowUsers tu_usuario            # Whitelist explícita de usuarios

Después de editar, recarga sin cortar tu sesión activa:

bashsshd -t          # Valida la sintaxis antes de aplicar
systemctl reload sshd
Trade-off importante: cambiar el puerto no es seguridad real, es reducción de ruido en logs. Un atacante con nmap lo encontrará igual. El valor real está en deshabilitar contraseñas y root.

2. Firewall: principio de mínimo privilegio

Usa ufw si quieres simplicidad o nftables si necesitas control fino. Para la mayoría de VPS, ufw es suficiente:

bashapt install ufw -y

ufw default deny incoming
ufw default allow outgoing

ufw allow 2222/tcp
ufw allow 80/tcp
ufw allow 443/tcp

ufw enable
ufw status verbose

¿Qué no abrir? Nunca expongas directamente: MySQL/MariaDB (3306), PostgreSQL (5432), Redis (6379), Elasticsearch (9200). Si una aplicación necesita acceso a base de datos desde afuera, usa un túnel SSH o VPN, no un puerto abierto.

3. Fail2ban: bloqueo automático de fuerza bruta

fail2ban monitorea logs y bloquea IPs que superan umbrales de fallos:

bashapt install fail2ban -y
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edita /etc/fail2ban/jail.local:

ini[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled  = true
port     = 2222
logpath  = /var/log/auth.log
maxretry = 3
bashsystemctl enable fail2ban
systemctl start fail2ban
fail2ban-client status sshd

4. Actualizaciones: automatiza lo que no requiere criterio

bashapt install unattended-upgrades -y
dpkg-reconfigure --priority=low unattended-upgrades

En /etc/apt/apt.conf.d/50unattended-upgrades verifica:

Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Criterio: automatiza security updates solamente, no upgrades completos. Los upgrades de versión mayor pueden romper dependencias y requieren revisión manual.

5. Auditoría rápida de superficie expuesta

bashss -tlnp
systemctl list-units --type=service --state=running
ss -tnp state established

Servicios que frecuentemente corren por defecto y no se necesitan:

  • avahi-daemon — descubrimiento de red local, inútil en servidor
  • cups — impresión
  • bluetooth

Para desactivar:

bashsystemctl disable --now avahi-daemon

6. Usuario no-root con sudo controlado

bashadduser tu_usuario
usermod -aG sudo tu_usuario

mkdir -p /home/tu_usuario/.ssh
cp ~/.ssh/authorized_keys /home/tu_usuario/.ssh/
chown -R tu_usuario:tu_usuario /home/tu_usuario/.ssh
chmod 700 /home/tu_usuario/.ssh
chmod 600 /home/tu_usuario/.ssh/authorized_keys

Una vez confirmado el acceso con el nuevo usuario, aplica PermitRootLogin no en SSH.

7. Lo que este artículo no cubre (y por qué)

Este hardening es la base. No cubre:

  • IDS/IPS (Snort, Suricata): agrega valor en entornos con tráfico heterogéneo, pero introduce complejidad operativa real.
  • SELinux / AppArmor: valioso, pero requiere tuning para no romper aplicaciones en producción.
  • Auditoría de integridad (AIDE, Tripwire): útil, pero con overhead de mantenimiento.
  • 2FA para SSH: recomendable si el servidor es crítico (Google Authenticator PAM funciona bien).

La decisión de agregar capas depende del perfil de riesgo del servidor, no de una checklist genérica.

Checklist de verificación final

  • SSH no acepta contraseñas (grep PasswordAuthentication /etc/ssh/sshd_config)
  • Root no puede conectarse por SSH
  • ufw activo con política deny por defecto (ufw status)
  • Solo puertos necesarios abiertos (ss -tlnp)
  • fail2ban activo y monitoreando SSH (fail2ban-client status)
  • Actualizaciones de seguridad automatizadas
  • Usuario no-root funcional con acceso por llave
  • Sin servicios innecesarios corriendo

¿Tienes un VPS con configuración más compleja — múltiples aplicaciones, Docker expuesto, o acceso a base de datos remota? Esos escenarios tienen vectores adicionales que vale revisar por separado.