Qué vas a conseguir
Un Ubuntu Server (22.04 o 24.04 LTS) endurecido lo justo para producción: nadie entra por contraseña, el firewall solo abre lo que das de alta, los parches de seguridad se instalan solos y el sistema tiene nombre y hora correctos. Treinta minutos que te ahorran un incidente.
Antes de empezar
- Acceso a la consola (hipervisor o KVM) o por SSH al usuario creado en la instalación.
- Tu clave pública SSH (
~/.ssh/id_ed25519.puben tu equipo; si no la tienes,ssh-keygen -t ed25519). - Decidido con el equipo: nombre del servidor, qué servicios dará y qué puertos abrir.
1. Nombre, hora y actualización inicial
sudo hostnamectl set-hostname srv-app-02sudo timedatectl set-timezone Europe/Madridtimedatectl | grep -E "Time zone|synchronized" Time zone: Europe/Madrid (CEST, +0200)System clock synchronized: yessudo apt update && sudo apt full-upgrade -y[ -f /var/run/reboot-required ] && echo "Reinicio necesario"Reinicio necesariosudo reboottimedatectl tiene que decir synchronized: yes (Ubuntu usa systemd-timesyncd). Un servidor con la hora mal falla en certificados, en Kerberos y en los logs correlacionados con el resto.
2. Usuario de administración y sudo
Un usuario por persona, nunca una cuenta compartida. root sin contraseña y sin acceso por SSH.
sudo adduser laurasudo usermod -aG sudo laurasudo mkdir -p /home/laura/.ssh && sudo chmod 700 /home/laura/.sshecho "ssh-ed25519 AAAAC3Nza... laura@laptop" | sudo tee /home/laura/.ssh/authorized_keyssudo chmod 600 /home/laura/.ssh/authorized_keys && sudo chown -R laura:laura /home/laura/.ssh# Probar desde tu equipo, en OTRA ventana, antes de seguir:# ssh laura@srv-app-02 → debe entrar sin pedir contraseña3. SSH solo con claves
sudo nano /etc/ssh/sshd_config.d/10-hardening.confPasswordAuthentication noPermitRootLogin noKbdInteractiveAuthentication noMaxAuthTries 3sudo sshd -t && sudo systemctl reload sshsudo sshd -T | grep -E "^(passwordauthentication|permitrootlogin)"passwordauthentication nopermitrootlogin nosshd -t comprueba la sintaxis antes de recargar: un error aquí con la sesión cerrada es un viaje a la consola. Y la regla de oro: no cierres la sesión actual hasta haber entrado con otra usando solo la clave.
Los ficheros en /etc/ssh/sshd_config.d/ se cargan antes que sshd_config y la primera aparición de cada opción gana. Si Ubuntu ya trae ahí un 50-cloud-init.conf con PasswordAuthentication yes, tu fichero 10-… lo adelanta por orden alfabético.
4. Firewall con ufw
sudo ufw default deny incomingsudo ufw default allow outgoingsudo ufw allow from 10.20.0.0/16 to any port 22 proto tcp comment "SSH desde la red de IT"sudo ufw allow 443/tcp comment "HTTPS app"sudo ufw enableFirewall is active and enabled on system startupsudo ufw status numbered To Action From -- ------ ----[ 1] 22/tcp ALLOW IN 10.20.0.0/16 # SSH desde la red de IT[ 2] 443/tcp ALLOW IN Anywhere # HTTPS appufw es una capa sobre nftables; lo que no está en la lista no entra. Limitar SSH a la red de administración o a la VPN es la medida que más ataques automáticos quita de los logs.
5. Actualizaciones de seguridad automáticas
sudo apt install -y unattended-upgradessudo dpkg-reconfigure -plow unattended-upgrades # responde Sísudo nano /etc/apt/apt.conf.d/50unattended-upgradesUnattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}-security";};Unattended-Upgrade::Automatic-Reboot "false";Unattended-Upgrade::Mail "it@contoso.com";sudo unattended-upgrade --dry-run --debug 2>&1 | tail -n 3Solo el origen -security: los parches de seguridad entran solos cada noche, el resto (y los reinicios) se hacen en la ventana de cambios con apt full-upgrade. /var/log/unattended-upgrades/ guarda lo que instaló cada noche.
6. Comprobación final
hostnamectl --static; timedatectl show -p NTPSynchronized --valuesrv-app-02yessudo ss -tlnp | awk 'NR==1 || /LISTEN/' | awk '{print $4, $7}'Local Address:Port Process0.0.0.0:22 users:(("sshd",pid=812,fd=3))0.0.0.0:443 users:(("nginx",pid=1204,fd=6))sudo ufw status | head -n 1; systemctl is-active unattended-upgradesStatus: activeactivesudo journalctl -p err -b --no-pager | tail -n 5ss -tlnp es la lista de lo que escucha: si hay algo que no esperas (una base de datos en 0.0.0.0, un panel de administración), o se cierra o se limita a 127.0.0.1.
Siguientes pasos
- Instala el script de comprobación de la guía de Bash en cron, y fail2ban si SSH tiene que estar expuesto.
- Para más de un servidor, la secuencia entera de esta guía es un playbook de Ansible de 40 líneas (Nivel 3).
- Ubuntu Landscape (Nivel 3) hace el inventario, el parcheo y los informes de toda la flota desde una consola.
¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.
What you'll get
An Ubuntu Server (22.04 or 24.04 LTS) hardened just enough for production: nobody gets in with a password, the firewall opens only what you register, security patches install themselves, and the system has the right name and time. Thirty minutes that save you an incident.
Before you start
- Console access (hypervisor or KVM) or SSH to the user created at install time.
- Your SSH public key (
~/.ssh/id_ed25519.pubon your device; if you don't have one,ssh-keygen -t ed25519). - Agreed with the team: server name, which services it will provide and which ports to open.
1. Name, time and first update
sudo hostnamectl set-hostname srv-app-02sudo timedatectl set-timezone Europe/Madridtimedatectl | grep -E "Time zone|synchronized" Time zone: Europe/Madrid (CEST, +0200)System clock synchronized: yessudo apt update && sudo apt full-upgrade -y[ -f /var/run/reboot-required ] && echo "Reboot required"Reboot requiredsudo reboottimedatectl has to say synchronized: yes (Ubuntu uses systemd-timesyncd). A server with the wrong time fails on certificates, on Kerberos, and in logs correlated with everything else.
2. Admin user and sudo
One user per person, never a shared account. root with no password and no SSH access.
sudo adduser laurasudo usermod -aG sudo laurasudo mkdir -p /home/laura/.ssh && sudo chmod 700 /home/laura/.sshecho "ssh-ed25519 AAAAC3Nza... laura@laptop" | sudo tee /home/laura/.ssh/authorized_keyssudo chmod 600 /home/laura/.ssh/authorized_keys && sudo chown -R laura:laura /home/laura/.ssh# Test from your device, in ANOTHER window, before going on:# ssh laura@srv-app-02 → must log in without asking for a password3. SSH with keys only
sudo nano /etc/ssh/sshd_config.d/10-hardening.confPasswordAuthentication noPermitRootLogin noKbdInteractiveAuthentication noMaxAuthTries 3sudo sshd -t && sudo systemctl reload sshsudo sshd -T | grep -E "^(passwordauthentication|permitrootlogin)"passwordauthentication nopermitrootlogin nosshd -t checks the syntax before reloading: a mistake here with the session closed means a trip to the console. And the golden rule: don't close the current session until you have logged in with another one using only the key.
Files in /etc/ssh/sshd_config.d/ load before sshd_config and the first occurrence of each option wins. If Ubuntu already ships a 50-cloud-init.conf there with PasswordAuthentication yes, your 10-… file overrides it by alphabetical order.
4. Firewall with ufw
sudo ufw default deny incomingsudo ufw default allow outgoingsudo ufw allow from 10.20.0.0/16 to any port 22 proto tcp comment "SSH from the IT network"sudo ufw allow 443/tcp comment "HTTPS app"sudo ufw enableFirewall is active and enabled on system startupsudo ufw status numbered To Action From -- ------ ----[ 1] 22/tcp ALLOW IN 10.20.0.0/16 # SSH from the IT network[ 2] 443/tcp ALLOW IN Anywhere # HTTPS appufw is a layer over nftables; what is not on the list does not get in. Limiting SSH to the admin network or the VPN is the measure that removes the most automated attacks from the logs.
5. Automatic security updates
sudo apt install -y unattended-upgradessudo dpkg-reconfigure -plow unattended-upgrades # answer Yessudo nano /etc/apt/apt.conf.d/50unattended-upgradesUnattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}-security";};Unattended-Upgrade::Automatic-Reboot "false";Unattended-Upgrade::Mail "it@contoso.com";sudo unattended-upgrade --dry-run --debug 2>&1 | tail -n 3Only the -security origin: security patches install themselves every night, the rest (and reboots) happen in the change window with apt full-upgrade. /var/log/unattended-upgrades/ keeps what was installed each night.
6. Final check
hostnamectl --static; timedatectl show -p NTPSynchronized --valuesrv-app-02yessudo ss -tlnp | awk 'NR==1 || /LISTEN/' | awk '{print $4, $7}'Local Address:Port Process0.0.0.0:22 users:(("sshd",pid=812,fd=3))0.0.0.0:443 users:(("nginx",pid=1204,fd=6))sudo ufw status | head -n 1; systemctl is-active unattended-upgradesStatus: activeactivesudo journalctl -p err -b --no-pager | tail -n 5ss -tlnp is the list of what is listening: if there is something you don't expect (a database on 0.0.0.0, an admin panel), either close it or bind it to 127.0.0.1.
Next steps
- Install the check script from the Bash guide in cron, and fail2ban if SSH has to be exposed.
- For more than one server, the whole sequence in this guide is a 40-line Ansible playbook (Level 3).
- Ubuntu Landscape (Level 3) does inventory, patching and reports for the whole fleet from one console.
Is something missing, or different in your environment? Get in touch and I'll fix it.
Tech Career Lab · newsletter
¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.Found this useful? Every two weeks I send one practical idea for IT professionals: LinkedIn, projects, networking and hiring processes.
Apúntate y llévate las 33 ideasSign up and get the 33 ideas →