4

Usuario no-root y hardening de sudo

Vistas: 1
Usuario no-root y hardening de sudo

Llevas tres capítulos blindando tu servidor. SSH reforzado, nftables como una muralla, fail2ban echando a los pesados. Pero hay un problema fundamental que aún no hemos tocado: estás usando root para todo. Cuando instalaste Ubuntu Server, el asistente te pidió crear un usuario. Luego, durante la configuración de los capítulos anteriores, usaste sudo para casi todo. O quizá, directamente, te conectaste como root para hacer los cambios. Si es lo segundo, para. Ya. Ahora mismo.

Trabajar como root es como llevar la llave maestra de tu casa colgando del cuello, a la vista de todos. Es cómodo, sí. Pero si te la quitan, no solo pierdes una llave: pierdes la caja fuerte entera. Este capítulo cierra el Bloque 1 del tutorial. Cuando termines, tu servidor tendrá una base sólida: SSH seguro, firewall, protección contra fuerza bruta, y un sistema de usuarios con los privilegios justos y necesarios. Ni más, ni menos.

¿Por qué no trabajar como root?

El principio de mínimo privilegio dice: un proceso o usuario debe tener solo los permisos necesarios para hacer su trabajo, y ni uno más.

Traducción: si vas a leer el correo, no necesitas poder borrar el kernel. Si vas a arrancar un contenedor Docker, no necesitas poder modificar /etc/shadow. Si vas a cambiar la configuración de Nginx, no necesitas poder formatear el disco.

Root puede hacerlo TODO. Ese es el problema.

Cada vez que ejecutas un comando como root, cualquier error (un rm -rf en la carpeta equivocada, un script malicioso que se cuela por una vulnerabilidad, un comando copiado de internet sin leerlo) tiene consecuencias catastróficas. No hay red de seguridad. No hay lo siento, me he equivocado. Hay disco formateado, datos perdidos, servidor comprometido.

Te cuento una anécdota de cuando empecé con esto. Tenía un servidor en el que trabajaba directamente como root porque total, soy el único administrador. Un día ejecuté un rm -rf pensando que estaba en /tmp y resulta que estaba en /. El servidor murió en el acto. No hubo recuperación. Desde entonces, tengo una regla sagrada: root solo para lo que sea estrictamente necesario, y con sudo, no con una sesión abierta. La incomodidad de escribir sudo delante de cada comando es un precio muy pequeño comparado con tener que restaurar un servidor desde cero.

Trabajar con un usuario normal y sudo para tareas específicas añade una capa de protección: el error tiene que ser deliberado (tienes que escribir sudo delante) o la vulnerabilidad tiene que escalar privilegios primero. No es infalible, pero levanta una barrera más que el root pelado.

Crear un usuario con sudo

Puede que ya tengas un usuario no-root de cuando instalaste el sistema o del capítulo 1. Si es así, este paso lo tienes hecho. Pero vamos a asegurarnos.

Comprueba tu usuario actual:

whoami

Si te dice root, tienes trabajo. Si te dice otro nombre, comprueba si tiene permisos sudo:

sudo -l

Si te pide contraseña y te muestra una lista de comandos permitidos, estás listo. Si te dice que no tienes permisos, sigue leyendo.

Crear el usuario

Vamos a crear un usuario con tu nombre. O con el que quieras, pero que no sea root y que no sea algo obvio como admin o administrador. Los bots también prueban esos.

adduser lorenzo

Te va a preguntar: contraseña, nombre completo, teléfono, etc. Lo único obligatorio es la contraseña. El resto lo puedes dejar vacío.

adduser (el script interactivo) hace más cosas que useradd (el binario de bajo nivel): crea el directorio home, copia los archivos de configuración por defecto, asigna el grupo correcto. Úsalo a menos que sepas bien lo que haces con useradd.

Añadir al grupo sudo

En Ubuntu, los usuarios con permisos sudo pertenecen al grupo sudo. No confundas con wheel, que es el grupo equivalente en Red Hat/Fedora. Aquí es sudo.

usermod -aG sudo lorenzo

El flag -a es importante: significa append. Sin él, -G sobreescribe todos los grupos secundarios del usuario y podrías dejarlo sin grupos esenciales. Siempre usa -aG junto.

Probar el acceso

Sal de la sesión actual y vuelve a entrar como tu nuevo usuario:

ssh lorenzo@tu-servidor

Y prueba:

sudo whoami

Si te dice root, funciona. Si te dice que no tienes permisos, revisa el grupo.

Configurar sudoers correctamente

El archivo /etc/sudoers controla quién puede hacer qué con sudo. Y tiene una regla de oro: no lo edites directamente con un editor de texto. Usa visudo.

visudo abre el archivo en un editor (normalmente nano o vim), pero antes de guardar, valida la sintaxis. Si metes una línea mal escrita, visudo te avisa y no te deja salir hasta que la corrijas o fuerces la salida. Esto es crítico: un sudoers mal escrito puede dejarte sin acceso sudo para siempre.

La línea que nos interesa en Ubuntu es la que da permisos al grupo sudo:

%sudo ALL=(ALL:ALL) ALL

Traducción: cualquier usuario del grupo sudo puede ejecutar cualquier comando como cualquier usuario en cualquier máquina.

Esto está bien para un servidor personal. Pero si quieres más control, puedes crear reglas más específicas.

Buenas prácticas con visudo

Crea un archivo separado para tus reglas personalizadas en /etc/sudoers.d/:

sudo visudo -f /etc/sudoers.d/lorenzo

Esto crea un archivo nuevo que complementa al sudoers principal. Pon dentro:

lorenzo ALL=(ALL:ALL) ALL

O si prefieres usar el grupo:

%sudo ALL=(ALL:ALL) ALL

La ventaja de usar sudoers.d/ es que si algo va mal, puedes borrar ese archivo desde una sesión root (por consola física o VNC) sin tener que tocar el archivo principal.

Evitar NOPASSWD para tareas críticas

Hay quien pone esto:

%sudo ALL=(ALL:ALL) NOPASSWD: ALL

Y se siente muy listo porque ya no tiene que escribir la contraseña cada vez. Pues no. Esto es eliminar la protección de sudo. Con NOPASSWD, cualquier script que se ejecute en tu sesión, cualquier comando que copies sin leer, cualquier vulnerabilidad en un proceso que corra con tu usuario, puede ejecutar sudo sin pedir permiso. La contraseña es el ¿seguro? que te da tiempo a pensar.

No lo hagas. La contraseña de sudo se teclea unas cuantas veces al día. Es un precio bajo por la seguridad que aporta.

Si el problema es que te cansas de escribirla, usa sudo -v al principio de una sesión de trabajo: renueva el timestamp de sudo y no te la vuelve a pedir durante 5 minutos (por defecto). O ajusta timestamp_timeout en sudoers si quieres más tiempo.

Sudoers avanzado: permisos específicos

Aquí es donde sudo brilla. Puedes permitir que un usuario ejecute comandos concretos sin contraseña, pero no todo.

Ejemplo típico: tu usuario necesita reiniciar Nginx cuando cambias la configuración. No necesita parar el sistema, ni montar discos, ni matar procesos de otros usuarios. Solo reiniciar Nginx.

Crea una regla en /etc/sudoers.d/lorenzo:

lorenzo ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx, /usr/bin/systemctl status nginx

Con esto, puedes hacer:

sudo systemctl restart nginx

Y no te pide contraseña. Pero si intentas:

sudo systemctl restart sshd

Te la pide. Y si intentas:

sudo rm -rf /

También te la pide. Justo lo que queremos.

Puedes extender esto a otros servicios. Si sigues el tutorial de Docker de atareao.es, probablemente quieras añadir:

lorenzo ALL=(root) NOPASSWD: /usr/bin/docker *, /usr/local/bin/docker-compose *

Ojo con los asteriscos. * significa cualquier argumento. Es un comodín útil pero peligroso si no controlas bien qué argumentos se pueden pasar. Un docker run --privileged puede comprometer el host entero. Úsalo con criterio.

Alias de comandos en sudoers

Para no repetirte, puedes definir alias de comandos directamente en sudoers:

sudo visudo -f /etc/sudoers.d/comandos-servicio
Cmnd_Alias SERVICES = /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx, /usr/bin/systemctl status nginx, /usr/bin/systemctl restart docker, /usr/bin/systemctl status docker

lorenzo ALL=(root) NOPASSWD: SERVICES

Esto queda más limpio cuando tienes varios usuarios o varios comandos. También puedes definir User_Alias para agrupar usuarios, o Host_Alias para restringir por máquina en entornos con varios servidores.

Desactivar login root

Si hiciste el capítulo 1, ya desactivaste el acceso root por SSH. Pero vamos a asegurarnos.

En /etc/ssh/sshd_config:

PermitRootLogin no

Si tienes dudas, compruébalo:

sudo sshd -T | grep permitrootlogin

Debe devolver permitrootlogin no.

¿Y el acceso local? En la consola física o por VNC, root puede seguir entrando si tiene contraseña. Para desactivarlo también:

sudo passwd -l root

Esto bloquea la contraseña de root (pone un ! en /etc/shadow). No la elimina, la bloquea. Si algún día necesitas recuperar el acceso root por consola, puedes desbloquearla con sudo passwd -u root desde un usuario con sudo.

Con esto, nadie puede entrar como root por SSH ni por consola. Todo pasa por tu usuario con sudo.

Proteger /root

El directorio /root es el home de root. Aquí guarda cosas: claves SSH, scripts, archivos de configuración, historial de comandos. Si alguien con acceso de solo lectura consigue ver esto, tiene información sensible.

Por defecto, /root suele tener permisos 750 (rwx para root, rx para el grupo root, nada para otros). Pero no está de más verificarlo:

sudo ls -la / | grep root

Debe mostrar:

drwx------  12 root root  4096 ...

Si ves drwxr-xr-x o algo con permisos para otros, corrígelo:

sudo chmod 750 /root

O incluso 700 si quieres ser más restrictivo (solo root accede, ni siquiera el grupo root):

sudo chmod 700 /root

En un servidor personal, 700 es buena opción. Si compartes el servidor con otros administradores de confianza, 750 con el grupo root puede tener sentido.

Hardening de PAM

PAM (Pluggable Authentication Modules) es el sistema que gestiona la autenticación en Linux. Cada vez que escribes una contraseña, ya sea al hacer login, al usar sudo o al desbloquear la pantalla, PAM está detrás.

Podemos configurar PAM para limitar intentos fallidos y bloquear temporalmente al usuario después de demasiados errores.

pam_faillock

En Ubuntu 24.04, la herramienta recomendada es pam_faillock. Reemplaza a la antigua pam_tally2 que ya no viene en versiones recientes.

Configúralo en /etc/security/faillock.conf:

sudo nano /etc/security/faillock.conf

Descomenta o añade:

deny = 5
unlock_time = 900
fail_interval = 900

Traducción: después de 5 intentos fallidos en 15 minutos, la cuenta se bloquea durante 15 minutos.

Si quieres que el bloqueo sea permanente hasta que un admin lo desbloquee:

deny = 5
unlock_time = 0

Pero en un servidor personal igual es demasiado drástico. Con 900 segundos (15 minutos) vale.

Para que funcione, PAM debe cargar este módulo. En Ubuntu 24.04 ya viene configurado, pero verifica que pam_faillock.so aparece en /etc/pam.d/common-auth:

grep faillock /etc/pam.d/common-auth

Debe mostrar algo como:

auth    [default=die] pam_faillock.so authfail
auth    sufficient      pam_faillock.so authsucc

Si no aparece, añádelo al principio del archivo common-auth:

auth    required                        pam_faillock.so preauth
auth    [default=die]                   pam_faillock.so authfail
auth    sufficient                      pam_faillock.so authsucc

Pero en Ubuntu 24.04 esto suele venir por defecto. Confírmalo antes de tocar nada.

Verificar el bloqueo

Para probar, abre otra terminal y equivócate de contraseña 5 veces seguidas con tu usuario. Luego intenta entrar con la contraseña correcta. Debería salirte algo como:

Account locked due to 5 failed logins

Para desbloquear manualmente:

sudo faillock --user lorenzo --reset

Para ver el estado de los bloqueos:

sudo faillock

Historial seguro

El historial de comandos (~/.bash_history) es una mina de oro para un atacante. Contraseñas escritas por error, rutas de archivos sensibles, comandos con tokens de API… todo queda registrado.

Vamos a hacer tres cosas: limitar el tamaño del historial, poner marcas de tiempo, y proteger el archivo.

HISTSIZE y HISTFILESIZE

En /etc/bash.bashrc (global para todos los usuarios) o en ~/.bashrc de cada usuario:

export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTCONTROL=ignoreboth:erasedups
  • HISTSIZE: número de comandos en memoria (la sesión actual)
  • HISTFILESIZE: número de líneas en el archivo de historial
  • HISTCONTROL=ignoreboth: no guarda comandos duplicados ni los que empiezan por espacio (si pones un espacio antes del comando, no se registra)

10000 comandos en memoria y 20000 en disco es razonable para un servidor. Si quieres minimizar, pon 2000 y 5000.

HISTTIMEFORMAT

Añade la fecha y hora a cada comando del historial. Esto no es seguridad directamente, pero ayuda en auditorías: si ves un comando sospechoso, sabes cuándo se ejecutó.

export HISTTIMEFORMAT="%F %T "

Con esto, history muestra:

 1234  2026-07-13 14:32:15 sudo systemctl restart nginx
 1235  2026-07-13 14:35:01 ls -la /etc/nginx/

HISTFILE protegido

El archivo ~/.bash_history debe tener permisos restrictivos. Normalmente viene con 600 (solo el usuario lee/escribe), pero verifícalo:

ls -la ~/.bash_history

Si ves algo como -rw-------, está bien. Si no:

chmod 600 ~/.bash_history

Logging de comandos sudo

Cada vez que ejecutas sudo, el comando se registra en /var/log/auth.log. Pero podemos hacer que sea más visible y fácil de auditar.

Configura sudo para que registre todos los comandos ejecutados. En /etc/sudoers.d/logging:

sudo visudo -f /etc/sudoers.d/logging

Añade:

Defaults    logfile=/var/log/sudo.log
Defaults    log_input, log_output
Defaults    iolog_dir=/var/log/sudo-io
  • logfile: archivo de texto plano con cada comando sudo
  • log_input/log_output: registra toda la entrada y salida del terminal durante la sesión sudo (más pesado, pero muy útil para auditorías)
  • iolog_dir: dónde se guardan los logs de entrada/salida

Para ver los comandos sudo ejecutados:

sudo cat /var/log/sudo.log

Para reproducir una sesión sudo registrada:

sudo sudoreplay /var/log/sudo-io/00/00/01

El logging de entrada/salida ocupa espacio. En un servidor personal puedes omitirlo y quedarte solo con logfile. Pero en un servidor con varios usuarios, es muy recomendable.

Auditoría básica con auditd

auditd es el sistema de auditoría de Linux. Es como un CCTV para tu servidor: registra quién accede a qué, cuándo y desde dónde.

No necesitas una configuración compleja. Con vigilar los archivos críticos del sistema es suficiente.

Instalar auditd

En Ubuntu 24.04 viene instalado pero no activo:

sudo apt install auditd audispd-plugins
sudo systemctl enable auditd
sudo systemctl start auditd

Reglas básicas de auditoría

Las reglas se definen en /etc/audit/rules.d/audit.rules. Vamos a vigilar los archivos que nunca deberían cambiar sin tu permiso:

-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/group -p wa -k group_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/ssh/sshd_config -p wa -k sshd_config_changes
-w /etc/nginx/ -p wa -k nginx_changes

Desglose:

  • -w /ruta/al/archivo: archivo a vigilar
  • -p wa: vigila escrituras (w) y cambios de atributos (a)
  • -k clave: palabra clave para filtrar después

Aplica las reglas:

sudo auditctl -R /etc/audit/rules.d/audit.rules

Y haz que sean persistentes reiniciando auditd:

sudo systemctl restart auditd

Buscar eventos

Para ver quién ha modificado /etc/passwd:

sudo ausearch -k passwd_changes

Para ver eventos recientes:

sudo ausearch -ts today

Para monitorizar en tiempo real:

sudo auditctl -w /etc/passwd -p wa
sudo tail -f /var/log/audit/audit.log | grep passwd

Si alguien (o algo) modifica estos archivos, queda registrado con usuario, proceso, fecha y hora. No hay forma de que pase desapercibido.

Cuánto guardar

Por defecto, auditd guarda logs rotándolos cuando alcanzan un tamaño. En /etc/audit/auditd.conf:

max_log_file = 50
num_logs = 5

50 MB por archivo, 5 archivos rotados. En un servidor personal es suficiente. Si ves que los logs crecen muy rápido, ajusta las reglas para vigilar menos cosas o aumentar el tamaño.

Verificación checklist final del bloque 1

Has llegado al final del Bloque 1. Cuatro capítulos de blindaje básico. Vamos a comprobar que todo está en su sitio con una checklist de verificación.

Copia y pega estos comandos uno a uno. Cada uno te confirmará que una pieza del puzzle está montada.

SSH

# Verificar que el acceso root está desactivado
sudo sshd -T | grep permitrootlogin
# Debe mostrar: permitrootlogin no

# Verificar que solo usas claves
sudo sshd -T | grep passwordauthentication
# Debe mostrar: passwordauthentication no

# Verificar los usuarios permitidos
sudo sshd -T | grep allowusers
# Debe mostrar tu usuario

Firewall nftables

# Verificar reglas activas
sudo nft list ruleset
# Debe mostrar reglas con policy drop en input

# Verificar puertos abiertos
sudo nft list chain inet filter input | grep dport
# Debe mostrar solo los puertos que necesitas: el de SSH, 80, 443, y tu VPN si usas

fail2ban

# Verificar jails activas
sudo fail2ban-client status
# Debe mostrar sshd y las que hayas añadido

# Verificar estado de SSH
sudo fail2ban-client status sshd
# Debe mostrar banned IP list (puede estar vacía si tienes suerte)

Usuario y sudo

# Verificar tu usuario
whoami
# No debe ser root

# Verificar permisos sudo
sudo -l
# Debe mostrar los comandos que puedes ejecutar

# Verificar grupo sudo
groups
# Debe incluir sudo

/root

# Verificar permisos de /root
sudo ls -la / | grep root
# Debe mostrar drwx------ o drwx-x---

PAM

# Verificar faillock config
sudo cat /etc/security/faillock.conf | grep -E "(deny|unlock_time)"
# Debe mostrar deny = 5 y unlock_time = 900 (o los valores que configuraste)

# Verificar estado de bloqueos
sudo faillock
# Debe estar vacío si no hay bloqueos activos

auditd

# Verificar auditd activo
sudo systemctl status auditd
# Debe mostrar active (running)

# Verificar reglas cargadas
sudo auditctl -l
# Debe mostrar las reglas que configuraste

Historial

# Verificar HISTSIZE
echo $HISTSIZE
# Debe mostrar 10000 (o el valor que configuraste)

# Verificar formato de tiempo
echo $HISTTIMEFORMAT
# Debe mostrar %F %T o similar

# Verificar permisos del historial
ls -la ~/.bash_history
# Debe mostrar -rw-------

Verificación completa con un script

Si quieres lanzar todo de golpe, guarda esto como check-bloque1.sh:

#!/bin/bash

echo "=== BLOQUE 1 - CHECKLIST DE SEGURIDAD ==="
echo ""

# SSH
echo "[SSH]"
echo "  PermitRootLogin: $(sudo sshd -T | grep permitrootlogin)"
echo "  PasswordAuth: $(sudo sshd -T | grep passwordauthentication)"
echo "  AllowUsers: $(sudo sshd -T | grep allowusers)"

# nftables
echo "[NFTABLES]"
echo "  Default policy: $(sudo nft list chain inet filter input 2>/dev/null | head -1)"

# fail2ban
echo "[FAIL2BAN]"
if systemctl is-active --quiet fail2ban; then
    echo "  Status: activo"
    echo "  Jails: $(sudo fail2ban-client status 2>/dev/null | grep 'Jail list' | cut -d: -f2)"
else
    echo "  Status: INACTIVO"
fi

# Usuario
echo "[USUARIO]"
echo "  Usuario actual: $(whoami)"
echo "  Grupos: $(groups)"
echo "  Sudo: $(sudo -n whoami 2>/dev/null && echo OK || echo 'No sudo (o requiere contraseña)')"

# /root
echo "[/ROOT]"
echo "  Permisos: $(ls -la / | grep root | awk '{print $1}')"

# PAM
echo "[PAM]"
if [ -f /etc/security/faillock.conf ]; then
    echo "  faillock.conf: existe"
    grep -E "^(deny|unlock_time)" /etc/security/faillock.conf 2>/dev/null | while read line; do echo "    $line"; done
else
    echo "  faillock.conf: NO EXISTE"
fi

# auditd
echo "[AUDITD]"
if systemctl is-active --quiet auditd; then
    echo "  Status: activo"
    echo "  Reglas: $(sudo auditctl -l 2>/dev/null | wc -l) reglas cargadas"
else
    echo "  Status: INACTIVO"
fi

echo ""
echo "=== FIN DEL CHECKLIST ==="

Hazlo ejecutable y ejecútalo:

chmod +x check-bloque1.sh
./check-bloque1.sh

Si todo está verde, tu servidor tiene una base sólida. Si algo falla, revisa el capítulo correspondiente.

Conclusión

Cuatro capítulos. Cuatro piezas del mismo puzzle.

  • Capítulo 1: Blindaste SSH. Claves en lugar de contraseñas, configuración ajustada, usuarios controlados.
  • Capítulo 2: Pusiste nftables. Firewall con política restrictiva, solo lo necesario abierto.
  • Capítulo 3: Instalaste fail2ban. Protección reactiva contra fuerza bruta y escáneres.
  • Capítulo 4 (este): Configuraste un usuario no-root, hardening de sudo, PAM, historial y auditoría.

Tu servidor ya no es un blanco fácil. Pero esto es solo el principio.

Si tienes Docker instalado (y si no, te recomiendo el tutorial de Docker de atareao.es), el siguiente paso natural es configurar Traefik v3 como proxy inverso. Cuando lo hagas, vuelve aquí para el Bloque 2, donde blindaremos los servicios que se asoman a internet: cabeceras HTTP, rate limiting, SSL y más.

La seguridad no es un destino, es un proceso. Has dado el primer paso. Los siguientes son cuestión de disciplina.


Más información

Deja una respuesta