ES | EN

De un VPS Vulnerable a una Fortaleza Automatizada - Parte I

SYSADMIN / HARDENING / CLOUD_SECURITY Lectura Técnica: 15 min
De un VPS Vulnerable a una Fortaleza Automatizada

Muchas veces montamos un VPS como laboratorio, y como sysadmin analizas tu propio servidor y piensas: "esto es un desastre". No porque algo haya explotado todavía, sino porque sabes que es cuestión de tiempo para que eso pase. Este post documenta exactamente eso: un VPS expuesto (un lab para probar muchas cosas, casi un HoneyPot) a proposito, un VPS desordenado y ruidoso, y despues de ver todo el ruido que hay, poco a poco te propones en convertirlo en una infraestructura que trabaja sola mientras duermes.

ESTADO DEL SISTEMA: POST-HARDENING / CLASIFICADO

Arquitecto: 0n3Z3r0 | Roles: SysAdmin Senior & Ethical Hacker.
Objetivo: Pasar de superficie de ataque total a infraestructura bajo Privilegio Mínimo y Monitoreo Continuo.
Serie: Parte I de III — Auditoría, Permisos, Tráfico y Almacenamiento.

1. Lo primero que hice: que el servidor me hable solo

Antes de tocar el firewall, antes de mover usuarios, lo primero que necesitaba era visibilidad. Porque no puedes defender lo que no ves, y revisar /var/log/auth.log a mano cada día no es administrar un servidor, es torturarse.

La solución fue simple: un script que corre solo cada noche a las 23:00 y me deja un resumen en el log. Sin intervención. Sin acordarme. Si algo raro pasó durante el día, lo sé al día siguiente sin haber hecho nada.

# Así quedó el crontab después del hardening
crontab -l

# La línea que hace la magia:
0 23 * * * /usr/local/bin/ssh-summary.sh >> /var/log/ssh-summary.log 2>&1

Lo que hace ese script es centralizar tres cosas: los intentos de acceso SSH fallidos, las IPs que fueron bloqueadas, y los accesos exitosos del día. Nada más. Pero esas tres cosas, juntas, te cuentan la historia completa de lo que pasó mientras no estabas mirando.

Componente Qué hace por ti
ssh-summary.sh Corre cada noche a las 23:00. Agrupa intentos fallidos, IPs bloqueadas y accesos reales del día en un solo sitio.
/var/log/ssh-summary.log Tu historial de inteligencia. Con el tiempo empiezas a ver patrones: rangos de IPs recurrentes, horarios de escaneo, países de origen.
crontab -l Úsalo también para auditar: si alguien comprometió el sistema y plantó persistencia, muchas veces aparece aquí. Revísalo de vez en cuando.

Explicacion no tecnica

Tu servidor recibe cientos de golpes al día de bots escaneando internet. Revisar el log completo es como ver horas de cámara de seguridad buscando algo sospechoso. El ssh-summary.sh es contratar a alguien que lo hace por ti y te deja una nota cada mañana: "847 intentos fallidos, 23 IPs bloqueadas, solo tú entraste a las 14:32". Eso es inteligencia. Lo otro es ruido.

2. Quién puede hacer qué: separar identidades de verdad

Este es el cambio que más cuesta entender hasta que lo ves en acción. Mucha gente opera su VPS entero desde root. Todo. Siempre. Y mientras nada falla, parece cómodo. El problema es que cuando algo falla, y va a fallar, ese atacante tiene las llaves de todo.

La solución no es compleja. Es simplemente decidir qué hace cada quien y no cruzar esa línea. Tres usuarios, tres funciones, sin excepciones:

- root: Solo tocas el kernel, actualizaciones críticas y emergencias. Nada más.
- labadmin: El día a día: despliegues, gestión de laboratorios, administración general.
- services: Corre los contenedores Docker. Sin shell interactiva. Sin sudo. Una jaula.

Y lo mismo aplica al filesystem. Mover las herramientas de recon y exploit a /opt, fuera del PATH estándar, no es solo orden: es que un exploit automatizado que entre al servidor no va a encontrar esas herramientas a mano. Tendría que saber exactamente dónde buscar. Ese fricción extra puede ser la diferencia.

/opt/
 ├── recon/    # Herramientas de reconocimiento (fuera del PATH estándar)
 └── exploit/  # Herramientas ofensivas (permisos restrictivos, aisladas)
Usuario / Directorio Por qué importa
root Acceso directo por SSH deshabilitado (PermitRootLogin no). Si alguien quiere root, primero tiene que comprometer otra cuenta. Un paso más.
labadmin Si el entorno de lab es comprometido, el atacante se queda ahí. No puede tocar producción ni los servicios del sistema.
services Si un contenedor Docker es vulnerado, el atacante cae en este usuario. Sin shell, sin sudo, sin movimiento lateral posible.
/opt/recon y /opt/exploit Fuera del PATH del sistema. Un exploit automatizado no las encuentra. Si alguien quiere usarlas, tiene que saber exactamente dónde están.

Explicacion no tecnica

Piensa en las llaves de un edificio. El conserje tiene la del cuarto de limpieza. El técnico tiene la del sótano. El director tiene la del servidor. Ninguno tiene todas. Si alguien le roba la llave al conserje, puede fregar el suelo, pero no puede llegar al servidor. Eso y nada más es el Principio de Privilegio Mínimo: no es desconfianza, es contención de daños.

3. El firewall no es magia, es política

Cuando haces ss -tulpen en un VPS recién configurado, el resultado da miedo. Puertos por todos lados. Y la reacción instintiva es cerrar todo a lo loco. Pero no es así como funciona: la clave no es cuántos puertos escuchan internamente, sino cuántos puertos deja pasar el firewall desde fuera.

La política es simple y no tiene excepciones: todo denegado por defecto, y solo abres lo que puedes justificar.

# Así quedó UFW después del hardening
ufw status verbose

# La política base, el verdadero Kill Switch:
Default: deny (incoming) → Todo lo que no invitaste: bloqueado
Default: allow (outgoing) → El servidor puede salir sin problemas

# Lo único que pasa desde internet:
22/tcp ALLOW IN → SSH
80/tcp ALLOW IN → HTTP (solo para redirigir a HTTPS)
443/tcp ALLOW IN → HTTPS

Y aquí está la parte que me parece más elegante de toda la configuración: Tailscale. Con tailscaled.service corriendo, Portainer, las bases de datos, cualquier servicio interno, solo son accesibles desde mi red privada virtual. No hace falta abrir ningún puerto extra. Para cualquier bot escaneando internet, esos servicios directamente no existen.

Capa de defensa Qué resuelve
UFW deny (default) Corta de raíz el ruido: bots de escaneo, intentos de fuerza bruta contra puertos aleatorios, tráfico basura en general.
Solo puertos 22, 80, 443 Superficie de ataque mínima. Tres vectores a vigilar, no treinta. Mucho más fácil de auditar.
Tailscale VPN Servicios sensibles invisibles desde internet. No necesitas abrir puertos extra. Si no está en la VPN, no lo ve nadie.

Explicacion no tecnica

Imagina un edificio de oficinas en plena ciudad. UFW es el portero: solo deja entrar por tres puertas contadas (22, 80, 443). Todo lo demás está tapiado. Tailscale es un túnel secreto que va directo desde tu casa hasta tu despacho en el décimo piso, sin pasar por recepción. Para alguien mirando desde la calle, ese despacho no existe. Eso es lo que queremos.

4. El disco no miente: Docker y Snap se comen el espacio

Hay una cosa que nadie te dice cuando empiezas a meter contenedores y paquetes Snap en un servidor: se comen el disco sin que te des cuenta. No de golpe, sino poco a poco. Y un día haces df -h y el disco está al 87% sin saber bien por qué.

El du -sh /* es la autopsia. Te dice exactamente dónde está el peso y ya no puedes ignorarlo:

# La autopsia del disco
du -sh /* 2>/dev/null | sort -rh | head -10

# Lo que encontré:
22G /var → Volúmenes Docker acumulados + logs sin rotar
12G /snap → El precio de los paquetes Snap
4.2G /usr → Sistema base, esto es normal

Los 22GB de /var son principalmente volúmenes Docker huérfanos: imágenes de contenedores que ya no usas, capas de builds anteriores, datos de contenedores borrados que siguen ocupando espacio. Un docker system prune bien dado puede recuperar bastante. Para Snap, el problema es que por defecto guarda las dos últimas versiones de cada paquete. Con unos pocos paquetes instalados eso se acumula. La solución es decirle que guarde solo dos revisiones.

Y para que nada de esto sea un problema de seguridad mientras duermes: unattended-upgrades activo. Los parches críticos se aplican solos. Sin recordatorios, sin ventanas de vulnerabilidad innecesarias.

El problema El correctivo
/var a 22 GB docker system prune -a --volumes para limpiar. Después, automatizar la limpieza periódica vía cron y activar logrotate para los logs.
/snap a 12 GB snap set system refresh.retain=2 para que Snap deje de acumular versiones antiguas sin control.
Parches manuales olvidados unattended-upgrades activo y configurado. Los CVEs críticos no esperan a que te acuerdes de actualizar.

Explicacion no tecnica

Docker y Snap son como dos inquilinos que nunca tiran la basura. Cada semana traen cosas nuevas pero las viejas se quedan ahí. Con el tiempo el piso se colapsa aunque nadie esté haciendo nada malo. docker system prune es el día de limpieza general. snap set refresh.retain=2 es decirles que no guarden más de dos mudas de ropa. Y unattended-upgrades es el técnico de mantenimiento que arregla las goteras solo, sin que tengas que llamar cada vez.

> SYSTEM_READY > NODE_ONLINE

< session_end // node: exit >
> INFOGRATECH_CORE_SHELL X
$