Asegura un servidor Ubuntu nuevo antes de poner aplicaciones en él
Sigue esto en tu propio servidor. Empieza gratis, sin tarjeta
Un servidor Ubuntu nuevo, de cualquier proveedor, viene con una sola cuenta (root), una sola puerta de entrada (una contraseña o una clave generada por el proveedor) y ninguna regla de firewall. Eso está bien durante los primeros cinco minutos mientras te orientas, pero todavía no es un servidor en el que debas correr nada. Cualquiera que escanee internet buscando puertos SSH abiertos lo va a encontrar en cuestión de horas, y una cuenta root accesible por contraseña es exactamente lo que espera encontrar. Al final de esta guía vas a tener un usuario sin privilegios de root con su propia clave SSH, el inicio de sesión de root y la autenticación por contraseña desactivados, un firewall que solo deja pasar los puertos que realmente usas, fail2ban vigilando intentos de fuerza bruta, actualizaciones de seguridad que se aplican solas, y una lista corta de verificaciones para confirmar que todo eso realmente quedó en efecto.
Lo que necesitas
- Un servidor Ubuntu 24.04 LTS nuevo, de cualquier proveedor, con sus credenciales SSH root iniciales (una contraseña o una clave generada por el proveedor).
- Tu propio par de claves SSH en tu máquina (
ssh-keygen -t ed25519si todavía no tienes una). - Un nombre de usuario sin privilegios de root que quieras crear en el servidor (esta guía usa
deploy). - Unos veinte minutos, la mayoría leyendo resultados en lugar de escribiendo.
Paso 1: crear un usuario sudo sin privilegios de root
Hacer todo como root significa que un solo comando mal escrito, un script comprometido o una clave filtrada entrega toda la máquina. Una cuenta separada con sudo te da la misma capacidad de administrar el servidor, pero solo cuando lo pides deliberadamente, y es la cuenta que Railyard (o cualquier herramienta de despliegue) va a usar más adelante.
Como root:
adduser deploy
usermod -aG sudo deploy
adduser te guía para definir una contraseña y algunos datos opcionales; usermod -aG sudo agrega la nueva cuenta al grupo sudo, que en Ubuntu ya está configurado para otorgar privilegios administrativos completos.
Verifica que funcionó: ejecuta id deploy y confirma que sudo aparece en la lista de grupos. Luego, su - deploy seguido de sudo whoami debería pedir la contraseña del usuario deploy y mostrar root.
Paso 2: copia tu clave SSH y prueba el inicio de sesión
Antes de tocar nada en sshd_config, haz que el inicio de sesión por clave funcione para el nuevo usuario y confírmalo desde una terminal completamente nueva, mientras tu sesión root original queda abierta como red de seguridad.
La forma fácil, desde tu propia máquina:
ssh-copy-id deploy@YOUR_SERVER_IP
Si ssh-copy-id no está disponible, hazlo a mano como el usuario deploy en el servidor:
mkdir -p ~/.ssh
echo "ssh-ed25519 AAAA... your-public-key-here" >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Ahora, en una segunda terminal, sin cerrar tu sesión root existente, intenta iniciar sesión con el nuevo usuario:
ssh deploy@YOUR_SERVER_IP
Deberías entrar directamente a una shell como deploy (puede pedir la frase de contraseña de tu propia clave si le pusiste una, pero no una contraseña para la cuenta). Si esto falla por cualquier razón, resuélvelo ahora, todavía desde tu sesión root original. No avances al siguiente paso hasta que esto funcione.
Paso 3: desactiva el inicio de sesión de root y la autenticación por contraseña
Con el inicio de sesión por clave confirmado, el acceso root y la autenticación por contraseña ya no son puertas que necesites abiertas, y dejarlas abiertas es justo lo que la mayoría de los escáneres automáticos de SSH están buscando.
Edita /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Antes de recargar nada, verifica que el archivo sea sintácticamente válido:
sudo sshd -t
Si eso sale limpio, recarga el servicio (en Ubuntu la unidad de systemd se llama ssh, no sshd):
sudo systemctl reload ssh
Verifica que funcionó desde una tercera terminal, nueva (de nuevo, mantén abierta tu sesión de trabajo): ssh root@YOUR_SERVER_IP debería ser rechazado de inmediato, y ssh deploy@YOUR_SERVER_IP con la contraseña equivocada debería fallar al instante sin siquiera pedir una contraseña, ya que la autenticación por contraseña está completamente desactivada. Tu inicio de sesión por clave ya establecido como deploy debería seguir funcionando exactamente igual que antes.
Paso 4: configura ufw
Un firewall que niega todo por defecto, y luego permite explícitamente solo los puertos que usas, hace que un puerto que olvidaste cerrar (o un servicio que instalaste y olvidaste) no sea accesible desde internet solo porque está escuchando.
sudo apt update
sudo apt install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
22/tcp mantiene SSH accesible (puedes usar sudo ufw allow OpenSSH en su lugar si prefieres el perfil de aplicación con nombre), 80/tcp y 443/tcp cubren HTTP y HTTPS planos para lo que eventualmente despliegues. Si sabes que no vas a servir nada por HTTP, puedes quitar la regla 80/tcp, aunque la mayoría de las configuraciones TLS igual usan el puerto 80 brevemente para la emisión de certificados.
Verifica que funcionó: sudo ufw status verbose debería mostrar Status: active, Default: deny (incoming), allow (outgoing), y las tres reglas que acabas de agregar listadas como ALLOW.
Paso 5: instala y configura fail2ban
Incluso con la autenticación por contraseña desactivada, tu puerto SSH sigue aceptando intentos de conexión, y un flujo constante de intentos de inicio de sesión automatizados llena tus registros y gasta algo de CPU sin ningún motivo. fail2ban vigila el registro de autenticación y banea temporalmente las direcciones IP que fallan demasiadas veces en autenticarse.
sudo apt install fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
La convención propia de fail2ban es dejar jail.conf sin tocar (se sobrescribe en cada actualización del paquete) y poner tus cambios en jail.local, que se lee después y tiene prioridad. Abre /etc/fail2ban/jail.local y asegúrate de que la sección sshd quede así:
[sshd]
enabled = true
port = ssh
maxretry = 5
bantime = 1h
findtime = 10m
Eso banea una IP durante una hora después de cinco intentos fallidos dentro de una ventana de diez minutos. Luego activa e inicia el servicio:
sudo systemctl enable --now fail2ban
Verifica que funcionó: sudo fail2ban-client status sshd debería mostrar el jail corriendo, con contadores de direcciones actualmente fallidas y actualmente baneadas.
Paso 6: activa las actualizaciones de seguridad automáticas
La mayoría de los servidores comprometidos no caen por fallas de día cero, sino por vulnerabilidades conocidas en paquetes que nadie se tomó el tiempo de actualizar. unattended-upgrades aplica los parches de seguridad automáticamente, según un horario, sin que tengas que acordarte de conectarte por SSH a correr apt upgrade.
sudo apt install unattended-upgrades
Ubuntu entrega esto con las actualizaciones de seguridad ya activadas para la versión actual una vez instalado. Confirma (y ajusta si hace falta) los dos archivos de configuración:
/etc/apt/apt.conf.d/20auto-upgrades debería contener:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
/etc/apt/apt.conf.d/50unattended-upgrades lista qué orígenes de paquetes tienen permiso de actualizarse automáticamente; el repositorio de seguridad de tu versión viene incluido por defecto.
Verifica que funcionó: sudo unattended-upgrade --dry-run --debug imprime lo que haría en su próxima ejecución sin cambiar nada realmente. Después de que el servicio haya corrido de verdad (revisa una vez al día), /var/log/unattended-upgrades/unattended-upgrades.log muestra qué instaló.
Paso 7: define la zona horaria y el nombre de host del servidor
Este paso no tiene que ver con atacantes, sino con tu propia cordura más adelante: marcas de tiempo en los registros que coincidan con tu propio reloj, y un nombre de host que te diga qué servidor estás mirando cuando tienes más de uno abierto en distintas pestañas de terminal.
sudo timedatectl set-timezone America/Montreal
sudo hostnamectl set-hostname myapp-server-01
Cambia por la zona horaria IANA y el nombre de host que te convengan; timedatectl list-timezones lista los valores válidos si no estás seguro del nombre exacto.
Verifica que funcionó: timedatectl muestra la zona horaria que definiste bajo “Time zone”, y hostnamectl muestra el nuevo nombre de host bajo “Static hostname”.
Paso 8: pasada final de verificación
Antes de considerar el servidor listo, cierra sesión por completo y confirma que toda la configuración se sostiene desde cero, no solo desde la sesión que has mantenido abierta todo el tiempo:
- Abre una terminal completamente nueva y ejecuta
ssh deploy@YOUR_SERVER_IP. Deberías entrar a una shell usando solo tu clave, sin que pida contraseña. ssh root@YOUR_SERVER_IPdesde esa misma terminal nueva debería ser rechazado.sudo ufw status verbosesigue mostrandoactivecon exactamente los puertos que quieres abiertos.sudo fail2ban-client status sshdmuestra el jail corriendo.sudo whoamicomo el usuariodeploysigue devolviendorootdespués de pedir una contraseña.
Si todas estas verificaciones pasan, el servidor está en un estado razonable para poner algo en él.
El atajo: hazlo con Railyard
Nada de lo anterior lo hace Railyard por ti. No toca sshd_config, no configura ufw ni fail2ban, y no va a definir tu zona horaria. Lo que necesita de un servidor que ya aseguraste de la forma descrita arriba es acotado: el usuario SSH tiene sudo sin contraseña, los puertos 22, 80 y 443 son accesibles desde internet (exactamente las reglas que acabas de agregar), y el servidor tiene al menos 1 GB de RAM (2 GB o más si también vas a construir imágenes de aplicaciones en él).
Un detalle para resolver antes de conectar un servidor: la combinación por defecto de adduser más usermod -aG sudo del paso 1 todavía le pide a deploy su contraseña cada vez que corre sudo, y Railyard necesita que eso pase sin pedir nada. Agrega un archivo de sudoers para eso:
echo "deploy ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/deploy
sudo chmod 440 /etc/sudoers.d/deploy
A partir de ahí: Servidores → Agregar un servidor → Conectar tu propio servidor, apunta a la IP de este servidor con la clave privada que corresponde a la clave pública que copiaste en el paso 2. Esa clave privada se usa una sola vez para la instalación y no se guarda. El servidor aparece en tu panel en cuestión de minutos, listo para recibir una aplicación.
Si prefieres no aprovisionar un servidor a mano para nada, Railyard también puede crear un servidor directamente en tu propia cuenta de nube (DigitalOcean, Vultr, Hetzner, AWS u OVH) y configurarlo automáticamente, apareciendo en tu panel de la misma manera. En cualquiera de los dos caminos, el endurecimiento descrito arriba sigue siendo tu responsabilidad mantenerlo en pie: Railyard automatiza el despliegue de aplicaciones, las puestas en producción sin interrupciones, y la gestión de bases de datos y respaldos sobre un servidor que ya está asegurado, es un atajo que se salta las herramientas de despliegue, no el endurecimiento en sí.
Solución de problemas
Te quedaste bloqueado afuera después de desactivar la autenticación por contraseña. Esto pasa cuando PasswordAuthentication no se recarga antes de confirmar que el inicio de sesión por clave realmente funcionaba. Si tu proveedor ofrece una consola web o un modo de recuperación, úsalo para entrar localmente y vuelve a activar temporalmente PasswordAuthentication yes, o corrige la clave en ~/.ssh/authorized_keys, y luego vuelve a probar desde una terminal nueva antes de desactivarla de nuevo.
ufw está bloqueando un puerto que tu aplicación necesita. Si algo que despliegas más adelante necesita un puerto más allá de 22, 80 y 443 (una base de datos que expones a propósito, un puerto de aplicación distinto), agrégalo explícitamente: sudo ufw allow 5432/tcp, y luego revisa con sudo ufw status verbose. Por lo general es más seguro mantener las bases de datos completamente fuera de internet y acceder a ellas por una red privada, en vez de abrir otro puerto.
fail2ban baneó tu propia IP. Si escribes mal una contraseña varias veces mientras pruebas (o pruebas desde un script que reintenta rápido), fail2ban puede banear la IP desde la que estás trabajando. Desbanéala desde una sesión que siga conectada, o desde la consola del servidor: sudo fail2ban-client set sshd unbanip YOUR_IP.
Puedes entrar por SSH desde la consola del servidor pero no desde internet. La mayoría de los proveedores de nube agregan su propio firewall a nivel de red (un “grupo de seguridad” o “firewall de nube”), separado de ufw. Si un puerto está abierto en ufw pero sigue sin ser accesible, revisa también la configuración de firewall del proveedor; ambos deben permitir los mismos puertos para que el tráfico pase.
Preguntas frecuentes
¿Necesito hacer esto si mi proveedor ya tiene su propio firewall? Sí. El firewall de red de un proveedor es una primera capa útil, pero no te da SSH solo con clave, no banea intentos de fuerza bruta, y no va a parchar un paquete vulnerable por ti. Trata ufw como una segunda capa, no como un reemplazo.
¿A qué puerto debería mover SSH? No tienes que moverlo. Una vez que el inicio de sesión de root y la autenticación por contraseña están desactivados y fail2ban está vigilando, el puerto 22 deja de ser un punto débil relevante; un escáner que encuentra el puerto 22 abierto no puede hacer gran cosa sin una clave válida. Mover SSH a un puerto no estándar sobre todo reduce el ruido en los registros causado por escaneos automatizados; si lo haces, acuérdate de actualizar tanto la directiva Port de sshd_config como la regla correspondiente de ufw, y prueba el nuevo puerto antes de cerrar el 22.
¿Con qué frecuencia debería actualizar el servidor? unattended-upgrades aplica los parches de seguridad todos los días por sí solo, lo que cubre las actualizaciones más importantes. Igual vale la pena correr sudo apt update && sudo apt upgrade a mano de vez en cuando para actualizaciones de paquetes que no son de seguridad, y reiniciar después de una actualización del kernel para que el nuevo realmente se cargue.
¿Todavía necesito fail2ban si la autenticación por contraseña ya está desactivada? Sigue valiendo la pena mantenerlo activo. Reduce el ruido en los registros y el (pequeño) costo en recursos de los intentos de conexión constantemente fallidos, y aplica la misma protección a cualquier otro servicio al que luego le agregues un jail, no solo a SSH.
¿Esto aplica también a versiones LTS de Ubuntu más recientes? El enfoque general (un usuario sudo sin privilegios de root, SSH solo con clave, ufw, fail2ban, actualizaciones automáticas) se traslada a otras versiones LTS recientes de Ubuntu; los comandos y rutas de archivos de esta guía son los vigentes para 24.04. Si estás en otra versión, vale la pena revisar rápidamente la documentación actual de Ubuntu por si algo cambió, antes de dar por buena una medida de endurecimiento.
Ninguno de estos pasos toma mucho tiempo por sí solo, y una vez hechos no tienes que volver a pensar en ellos hasta el próximo servidor. Lo que ganas con esto es una máquina donde un escáner perdido encuentra una puerta cerrada en lugar de una abierta, y esa es la base desde la que debería partir cualquier servidor en el que corras algo real.
Por qué eligen Railyard para esto
- Tu propia cuenta en la nube. Despliega en DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, o cualquier servidor Linux por SSH, facturado directamente por tu proveedor, sin márgenes adicionales.
- Un precio fijo por equipo. El plan Free es gratis para siempre: un servidor más un servidor de base de datos, tres apps, sin tarjeta. Pro cuesta $19 al mes por hasta cinco servidores y apps ilimitadas. Team cuesta $49 al mes con servidores ilimitados. Sin cargos por app, por despliegue, por asiento ni por gigabyte.
- Plantillas de un clic. Despliega apps de código abierto, como bases de datos, analítica y herramientas de desarrollo, en tu propio servidor con un clic.
- Bases de datos administradas. Postgres, MySQL y Redis, con copias de seguridad programadas y verificadas por restauración, y recuperación a un punto en el tiempo.
- Despliegue con git push. Despliega desde GitHub, GitLab o la CLI, con HTTPS automático y lanzamientos sin tiempo de inactividad.