Una auditoría de seguridad manual consiste en recorrer, de forma ordenada, los puntos por los que un servidor suele quedar expuesto: paquetes sin actualizar, cuentas sobrantes, SSH permisivo, puertos abiertos sin motivo, permisos incorrectos o registros que nadie revisa. En esta guía tienes una lista de verificación para Ubuntu 24.04 en la que cada punto va acompañado del comando que lo comprueba, del resultado esperado y de cómo corregirlo. Al final guardarás las evidencias en un directorio fechado para poder comparar auditorías futuras.
Todos los comandos son de solo lectura salvo los que aparecen explícitamente como corrección.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. Casi todo sirve en Debian 12; las diferencias para Rocky Linux 9 se indican donde importan.
- Un usuario no root con privilegios
sudo. - Una segunda sesión SSH abierta antes de modificar SSH o el firewall, para no quedarte fuera.
Paso 1: Preparar el directorio de evidencias
Guardar la salida de cada comprobación te permite demostrar qué revisaste y comparar con la siguiente auditoría. Crea un directorio protegido con la fecha del día:
AUDIT_DIR="$HOME/auditoria-$(date +%F)"
mkdir -p "$AUDIT_DIR"
chmod 700 "$AUDIT_DIR"
A lo largo de la guía puedes añadir | tee "$AUDIT_DIR/nombre.txt" a cualquier comando para guardar su salida. Empieza por la información básica del sistema:
hostnamectl | tee "$AUDIT_DIR/sistema.txt"
uname -r | tee -a "$AUDIT_DIR/sistema.txt"
Static hostname: web01
Operating System: Ubuntu 24.04.3 LTS
Kernel: Linux 6.8.0-84-generic
Architecture: x86-64
Paso 2: Actualizaciones y parches
Los paquetes con vulnerabilidades conocidas son la vía de entrada más común. Comprueba qué hay pendiente:
sudo apt update
apt list --upgradable
Esperado: lista vacía o solo paquetes publicados en los últimos días.
Comprueba si hay un reinicio pendiente (por ejemplo, tras actualizar el kernel):
cat /var/run/reboot-required 2>/dev/null || echo "No hace falta reiniciar"
Verifica que las actualizaciones de seguridad automáticas están activas:
cat /etc/apt/apt.conf.d/20auto-upgrades
systemctl is-enabled unattended-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
enabled
Corrección: si falta el archivo o los valores son 0, instala y activa el paquete:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
En Rocky Linux 9 el equivalente es sudo dnf check-update y el paquete dnf-automatic.
Paso 3: Cuentas de usuario
Solo root debe tener UID 0. Cualquier otra cuenta con UID 0 tiene privilegios completos:
awk -F: '$3 == 0 {print $1}' /etc/passwd
root
Ninguna cuenta debe tener la contraseña vacía:
sudo awk -F: '$2 == "" {print $1}' /etc/shadow
Esperado: ninguna salida. Corrección: bloquea la cuenta con sudo passwd -l nombre_usuario.
Lista las cuentas que pueden iniciar sesión con una shell interactiva y confirma que conoces a todas:
grep -Ev '(/nologin|/false)$' /etc/passwd | cut -d: -f1,3,7
Revisa los inicios de sesión recientes y los intentos fallidos:
last -n 20
sudo lastb -n 20
Corrección: elimina las cuentas que ya no se usan con sudo deluser --remove-home nombre_usuario, o bloquéalas con sudo usermod -L -e 1 nombre_usuario si necesitas conservar sus archivos.
Paso 4: Privilegios de sudo
Comprueba quién pertenece a los grupos con acceso de administración (sudo en Ubuntu y Debian, wheel en Rocky):
getent group sudo
sudo:x:27:your_user
Busca reglas que permitan sudo sin contraseña, que convierten cualquier sesión robada en root inmediato:
sudo grep -rn 'NOPASSWD' /etc/sudoers /etc/sudoers.d/
Esperado: ninguna regla o solo reglas justificadas para comandos concretos (no ALL). Edita siempre con sudo visudo o sudo visudo -f /etc/sudoers.d/archivo para que se valide la sintaxis.
Paso 5: Configuración de SSH
No revises solo /etc/ssh/sshd_config: en Ubuntu 24.04 los archivos de /etc/ssh/sshd_config.d/ (por ejemplo 50-cloud-init.conf) pueden cambiar los valores. sshd -T muestra la configuración efectiva:
sudo sshd -T | grep -Ei '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|maxauthtries|x11forwarding|permitemptypasswords) '
Valores recomendados:
| Parámetro | Valor recomendado |
|---|---|
permitrootlogin | no (o prohibit-password si necesitas root con clave) |
passwordauthentication | no |
kbdinteractiveauthentication | no (salvo que uses 2FA) |
pubkeyauthentication | yes |
maxauthtries | 3 o 4 |
x11forwarding | no |
permitemptypasswords | no |
Revisa también qué claves públicas pueden entrar en cada cuenta, incluida root:
sudo ssh-keygen -lf /root/.ssh/authorized_keys
ssh-keygen -lf ~/.ssh/authorized_keys
Cada línea muestra la huella y el comentario de una clave. Elimina las que no reconozcas.
Corrección: pon los valores en /etc/ssh/sshd_config.d/10-hardening.conf (el primer valor leído es el que se aplica, y 10- se lee antes que 50-cloud-init.conf), valida con sudo sshd -t y aplica con sudo systemctl reload ssh. La guía de gestión de claves SSH de esta sección lo explica en detalle.
Paso 6: Puertos abiertos y servicios
Lista los puertos en escucha y el proceso que los abre:
sudo ss -tulpn
Netid State Local Address:Port Peer Address:Port Process
tcp LISTEN 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=812,fd=3))
tcp LISTEN 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=945,fd=21))
tcp LISTEN 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1020,fd=6))
Esperado: los servicios internos (bases de datos, Redis, paneles de administración) escuchan en 127.0.0.1 o en una IP privada, nunca en 0.0.0.0 sin firewall.
Revisa los servicios en ejecución y deshabilita los que no uses:
systemctl list-units --type=service --state=running --no-pager
Corrección: sudo systemctl disable --now nombre_servicio.
Paso 7: Firewall
Confirma que el firewall está activo, que la política por defecto de entrada es denegar y que solo están abiertos los puertos que viste en el paso anterior:
sudo ufw status verbose
Status: active
Default: deny (incoming), allow (outgoing), disabled (routed)
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
Corrección: si UFW está inactivo, permite SSH antes de activarlo:
sudo ufw allow OpenSSH
sudo ufw enable
En Rocky Linux 9 usa sudo firewall-cmd --list-all.
Notalos puertos publicados por Docker con
-pse saltan las reglas de UFW. Si usas Docker, revisa tambiénsudo docker ps --format '{{.Names}} {{.Ports}}'.
Paso 8: Parámetros del kernel
Algunos parámetros de red y de memoria reducen la superficie de ataque. Consulta sus valores actuales:
sysctl net.ipv4.ip_forward net.ipv4.tcp_syncookies net.ipv4.conf.all.accept_redirects net.ipv4.conf.all.send_redirects net.ipv4.conf.all.accept_source_route net.ipv4.conf.all.log_martians kernel.randomize_va_space kernel.kptr_restrict kernel.dmesg_restrict fs.suid_dumpable
| Parámetro | Valor recomendado | Motivo |
|---|---|---|
net.ipv4.ip_forward | 0 | El servidor no enruta tráfico (1 si usas Docker, Kubernetes o VPN). |
net.ipv4.tcp_syncookies | 1 | Mitiga inundaciones SYN. |
net.ipv4.conf.all.accept_redirects | 0 | Ignora redirecciones ICMP. |
net.ipv4.conf.all.send_redirects | 0 | No envía redirecciones. |
net.ipv4.conf.all.accept_source_route | 0 | Rechaza paquetes con ruta de origen. |
net.ipv4.conf.all.log_martians | 1 | Registra paquetes con direcciones imposibles. |
kernel.randomize_va_space | 2 | ASLR completo. |
kernel.kptr_restrict | 1 o 2 | Oculta direcciones del kernel. |
kernel.dmesg_restrict | 1 | Solo root lee dmesg. |
fs.suid_dumpable | 0 | Sin volcados de memoria de binarios SUID. |
Corrección: crea un archivo con los valores que no coincidan:
sudo nano /etc/sysctl.d/99-hardening.conf
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
fs.suid_dumpable = 0
Aplícalo y vuelve a consultar los valores:
sudo sysctl --system
Paso 9: Permisos de archivos
Los archivos de credenciales deben tener permisos estrictos:
stat -c '%a %U:%G %n' /etc/passwd /etc/shadow /etc/group /etc/gshadow /etc/ssh/sshd_config
644 root:root /etc/passwd
640 root:shadow /etc/shadow
644 root:root /etc/group
640 root:shadow /etc/gshadow
644 root:root /etc/ssh/sshd_config
Busca archivos que cualquier usuario puede modificar fuera de los directorios temporales:
sudo find / -xdev -type f -perm -0002 -not -path '/proc/*' 2>/dev/null
Esperado: ninguna salida. Corrección: sudo chmod o-w ruta/al/archivo.
Lista los binarios SUID y SGID. Guarda la lista para comparar en la siguiente auditoría: un binario SUID nuevo que no viene de un paquete es una señal de alarma.
sudo find / -xdev -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null | sort | tee "$AUDIT_DIR/suid.txt"
Comprueba además que ningún archivo instalado por paquetes ha sido modificado. debsums -s solo muestra los archivos cuya suma no coincide:
sudo apt install debsums
sudo debsums -s
Esperado: ninguna salida. Un binario de /usr/bin modificado requiere investigación inmediata.
Paso 10: Tareas programadas
Los atacantes suelen dejar persistencia en cron o en temporizadores de systemd. Revisa ambos:
ls -la /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly
sudo ls -la /var/spool/cron/crontabs/
systemctl list-timers --all --no-pager
Revisa el contenido de cualquier crontab de usuario que no esperes con sudo crontab -l -u nombre_usuario.
Paso 11: Registros de autenticación
Cuenta los intentos fallidos de SSH de las últimas 24 horas y las IP que más lo intentan:
sudo journalctl -u ssh --since "24 hours ago" --no-pager | grep -c 'Failed password'
sudo journalctl -u ssh --since "24 hours ago" --no-pager | grep -oP 'Failed password .* from \K[0-9.]+' | sort | uniq -c | sort -rn | head
Revisa los inicios de sesión aceptados y el método usado:
sudo journalctl -u ssh --since "7 days ago" --no-pager | grep 'Accepted'
Esperado: solo Accepted publickey desde IP que reconozcas. Un Accepted password cuando la autenticación por contraseña debería estar desactivada indica un problema de configuración.
Comprueba que los registros se conservan tras reiniciar:
journalctl --list-boots --no-pager | tail -n 3
Si solo aparece el arranque actual, el diario no es persistente. En Ubuntu 24.04 basta con crear el directorio y reiniciar el servicio:
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
Paso 12: Cerrar la auditoría
Comprime las evidencias para archivarlas fuera del servidor:
tar -czf "$AUDIT_DIR.tar.gz" -C "$HOME" "$(basename "$AUDIT_DIR")"
ls -lh "$AUDIT_DIR.tar.gz"
Anota para cada punto si pasa, falla o se acepta el riesgo, y quién es responsable de corregirlo. En la siguiente auditoría, compara por ejemplo la lista SUID con diff.
Conclusión
Has revisado actualizaciones, cuentas, sudo, SSH, puertos, firewall, parámetros del kernel, permisos, tareas programadas y registros de autenticación, y has guardado las evidencias en un archivo fechado. Repite esta lista al menos cada trimestre y siempre después de un cambio importante en el servidor.
Como siguientes pasos puedes:
- Automatizar gran parte de estas comprobaciones con Lynis y comparar su índice de endurecimiento en el tiempo.
- Desactivar por completo el acceso por contraseña con claves SSH y añadir autenticación de dos factores.
- Instalar Fail2ban para bloquear automáticamente las IP con muchos intentos fallidos.
