sudo en Linux: sudoers, visudo, NOPASSWD y Defaults paso a paso

Índice
sudo en Linux: sudoers, visudo, NOPASSWD y Defaults paso a paso

En el Imperio , Darth Vader tenía acceso a todo. Ninguna puerta cerrada, ningún sistema restringido, ninguna contraseña que conociera, ….. usuario ALL=(ALL:ALL) ALL y listo — el equivalente en sudoers para darle la llave de la Estrella de la Muerte a alguien sin más explicación.

El problema es que no todos los usuarios de tu sistema son Darth Vader. Algunos son técnicos de mantenimiento que solo necesitan reiniciar un servicio. Otros son scripts de automatización que ejecutan un rsync a las 3 de la mañana. Y algunos, simplemente, no deberían tener acceso a un sudo bash de distancia.

La mayoría de guías de sudo se quedan en esa primera línea y punto. Útil para empezar, pero en la práctica real necesitas más matices: permisos granulares por comando, comportamiento fino con Defaults, diferencias entre distribuciones y saber auditar quién ha estado haciendo qué en tu infraestructura.

Este artículo va un paso más allá del básico. Vamos a pasar de aprendiz a caballero Jedi.


La diferencia entre distribuciones

Lo primero que hay que entender es que el comportamiento por defecto de sudo varía según la distribución, y no porque el binario sea diferente — sino por cómo está configurado el fichero /etc/sudoers de fábrica.

Debian y Ubuntu — grupo sudo

En Debian y Ubuntu, el grupo privilegiado se llama sudo. Cualquier usuario en ese grupo puede ejecutar cualquier comando como root, pidiendo su propia contraseña:

# Ver si un usuario está en el grupo sudo
groups usuario
id usuario

# Añadir un usuario al grupo sudo (como root)
usermod -aG sudo usuario

# El usuario necesita cerrar sesión y volver a abrir para que el grupo sea efectivo
# O cargar los grupos sin salir:
newgrp sudo

La configuración base en /etc/sudoers que lo permite:

%sudo   ALL=(ALL:ALL) ALL

RedHat, Rocky y CentOS — grupo wheel

En la familia RedHat el grupo privilegiado se llama wheel. Mismo concepto, distinto nombre:

# Añadir usuario al grupo wheel
usermod -aG wheel usuario

# Verificar
id usuario

La línea en /etc/sudoers que lo habilita:

%wheel  ALL=(ALL) ALL

En algunas versiones de RedHat/Rocky viene comentada una variante con NOPASSWD — no la actives a nivel global si no sabes lo que haces.

Resumen rápido

DistribuciónGrupo sudoActivado por defecto
Debian 11/12sudo
Ubuntusudo
RedHat 8/9wheel
Rocky Linuxwheel
CentOS Streamwheel

sudo -i, sudo -s y sudo su - — no son lo mismo

Es una de las confusiones más frecuentes, incluso entre administradores con experiencia:

ComandoQué haceEntorno
sudo -iShell de login como rootEntorno limpio de root (lee .profile, .bashrc de root)
sudo -sShell de root sin loginHereda el entorno del usuario actual
sudo su -Su como root con loginEntorno limpio de root (equivalente práctico a sudo -i)
sudo bashEjecuta bash como rootHereda el entorno del usuario actual

La diferencia importa cuando tienes variables de entorno, PATH personalizado o configuraciones en .bashrc de root. Para la mayoría de tareas de administración, sudo -i es la opción más predecible.


Editar sudoers correctamente — visudo

Nunca edites /etc/sudoers directamente con un editor normal. Si introduces un error de sintaxis, puedes quedarte sin acceso root en el sistema.

# Siempre así:
sudo visudo

visudo valida la sintaxis antes de guardar. Si hay un error, te avisa y no cierra el fichero hasta que esté correcto.

Para editar con un editor específico:

sudo EDITOR=nano visudo
sudo EDITOR=vim visudo

La sintaxis de sudoers — entenderla de verdad

La línea más habitual que ves en todos los tutoriales:

usuario ALL=(ALL:ALL) ALL

Significa:

  • usuario — el usuario al que se aplica la regla
  • ALL (primer campo) — desde cualquier host
  • (ALL:ALL) — puede ejecutar comandos como cualquier usuario y cualquier grupo
  • ALL (último campo) — cualquier comando

Desglosado con todos los campos:

QUIEN  DÓNDE=(COMO_QUIÉN:COMO_QUÉ_GRUPO)  QUÉ_COMANDO

En la práctica, para un servidor que administras tú:

# Acceso completo — pide contraseña
jaime ALL=(ALL:ALL) ALL

# Acceso completo — sin contraseña (ojo con esto)
jaime ALL=(ALL:ALL) NOPASSWD: ALL

# Solo como root, no como otros usuarios
jaime ALL=(root) ALL

Defaults — el comportamiento fino de sudo

La directiva Defaults controla cómo se comporta sudo más allá de qué puede ejecutar quién. Es lo que más se ignora en guías básicas y lo que más diferencia una configuración cuidada de una instalada por defecto.

Tiempo de caché de contraseña

Por defecto sudo recuerda la contraseña durante 15 minutos. Puedes cambiarlo:

# En /etc/sudoers o en un fichero de sudoers.d

# Sin caché — pide contraseña en cada ejecución
Defaults timestamp_timeout=0

# Caché de 5 minutos
Defaults timestamp_timeout=5

# Caché por terminal (no comparte entre terminales distintas)
Defaults timestamp_type=tty

# Solo para un usuario concreto
Defaults:jaime timestamp_timeout=30

Registro propio de sudo (fuera de syslog)

sudo puede escribir su propio log independientemente de syslog:

Defaults logfile=/var/log/sudo.log
Defaults log_input, log_output

Con log_input y log_output sudo registra también lo que escribe el usuario y lo que devuelve el comando — útil para auditorías. Los logs de I/O van a /var/log/sudo-io/ por defecto.

secure_path — el PATH de sudo

Cuando ejecutas algo con sudo, el PATH que usa no es el tuyo — es el que define secure_path en sudoers:

sudo visudo -c && sudo -V | grep "Secure path"
# Secure path: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Si instalas herramientas en rutas no estándar (como /home/usuario/.local/bin), sudo no las encuentra aunque tú sí puedas ejecutarlas. La solución: usar rutas absolutas en las reglas de sudoers, o añadir la ruta a secure_path:

Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/gonzo/.local/bin"

Preservar variables de entorno

Por defecto sudo limpia casi todas las variables de entorno. Si necesitas que alguna llegue al comando:

# Preservar variables concretas
Defaults env_keep += "HTTP_PROXY HTTPS_PROXY NO_PROXY"
Defaults env_keep += "EDITOR VISUAL"

# Permitir que el usuario pase variables con sudo -E
Defaults env_reset

Con sudo -E comando el usuario puede pasar su entorno completo (si la regla lo permite).

Avisos y notificaciones

# Enviar email al root si alguien introduce la contraseña mal
Defaults mail_badpass

# Mensaje de advertencia legal la primera vez que un usuario usa sudo
Defaults lecture=always
Defaults lecture_file=/etc/sudo_lecture.txt

sudoers.d — el modo correcto de gestionar reglas

En lugar de añadir todo en /etc/sudoers, usa el directorio /etc/sudoers.d/. Cada fichero es una regla o conjunto de reglas para un usuario, grupo o servicio. Ventajas:

  • Más fácil de mantener y auditar
  • Puedes borrar una regla eliminando un fichero, sin tocar el fichero principal
  • Los paquetes y herramientas de automatización (Ansible, por ejemplo) pueden añadir sus propias reglas sin conflictos
# Crear un fichero de regla para un usuario
sudo visudo -f /etc/sudoers.d/jaime

El nombre del fichero no puede contener . ni ~ — si lo hace, sudo lo ignora. Usa guiones o guiones bajos.

# Correcto
/etc/sudoers.d/jaime
/etc/sudoers.d/deploy-scripts
/etc/sudoers.d/99-automatizacion

# Ignorado por sudo (no uses estos nombres)
/etc/sudoers.d/jaime.conf     ← el punto lo invalida
/etc/sudoers.d/jaime~         ← la tilde lo invalida

Permisos correctos (sudo se queja si no son exactamente estos):

sudo chmod 440 /etc/sudoers.d/jaime
sudo chown root:root /etc/sudoers.d/jaime

Casos de uso reales

Un usuario con acceso total (el básico, para completar)

sudo visudo -f /etc/sudoers.d/jaime
jaime ALL=(ALL:ALL) ALL

Un usuario que solo puede reiniciar un servicio concreto

Escenario: el usuario deploy necesita reiniciar nginx después de un despliegue, pero no necesita acceso a nada más.

deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl reload nginx

El path del binario tiene que ser el absoluto y exacto. Si no sabes cuál es:

which systemctl
# /usr/bin/systemctl

Un usuario que puede gestionar un servicio completo

monitor ALL=(root) NOPASSWD: /usr/bin/systemctl start prometheus, \
                              /usr/bin/systemctl stop prometheus, \
                              /usr/bin/systemctl restart prometheus, \
                              /usr/bin/systemctl status prometheus

Un script de automatización — solo el comando exacto

Escenario: un script de backup necesita ejecutar rsync como root para acceder a ficheros del sistema.

backup ALL=(root) NOPASSWD: /usr/bin/rsync

Con argumentos fijos (más seguro — solo permite exactamente esa llamada):

backup ALL=(root) NOPASSWD: /usr/bin/rsync -az /datos/ /backup/

Un grupo con acceso a comandos de red

Escenario: el equipo de redes puede ejecutar herramientas de diagnóstico pero no puede modificar el sistema.

%redes ALL=(root) NOPASSWD: /usr/sbin/ip, \
                             /usr/sbin/ss, \
                             /usr/bin/tcpdump, \
                             /usr/sbin/iptables -L

Nota el % antes del nombre — indica que es un grupo, no un usuario.

Ejecutar como otro usuario (no como root)

Escenario: el usuario jaime necesita ejecutar comandos como el usuario app (que corre el servicio web), no como root.

jaime ALL=(app) NOPASSWD: ALL

Se ejecuta con:

sudo -u app comando

Alias para simplificar reglas complejas

Si tienes muchos comandos repetidos, puedes definir alias:

# En /etc/sudoers o en un fichero de sudoers.d

Cmnd_Alias SERVICIOS_WEB = /usr/bin/systemctl restart nginx, \
                            /usr/bin/systemctl reload nginx, \
                            /usr/bin/systemctl restart php-fpm, \
                            /usr/bin/systemctl reload php-fpm

Cmnd_Alias DIAGNOSTICO = /usr/sbin/ss, \
                          /usr/bin/netstat, \
                          /usr/bin/tcpdump

deploy  ALL=(root) NOPASSWD: SERVICIOS_WEB
monitor ALL=(root) NOPASSWD: DIAGNOSTICO

Verificar que las reglas funcionan

Antes de asumir que una regla está bien, compruébala:

# Ver qué puede hacer un usuario con sudo
sudo -l
sudo -l -U usuario   # como root, ver los permisos de otro usuario

# Simular una ejecución sin ejecutarla realmente
sudo -l | grep comando

# Comprobar si sudo acepta la configuración actual
sudo visudo -c

Errores habituales

El comando tiene argumentos y la regla no los contempla:

# Regla que solo permite systemctl sin argumentos
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl

# Esto falla:
sudo systemctl restart nginx   ← los argumentos no están en la regla

# Regla correcta para cualquier argumento de systemctl:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl *

Usar rutas relativas en lugar de absolutas:

# Incorrecto — sudo lo rechaza
deploy ALL=(root) NOPASSWD: systemctl restart nginx

# Correcto
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx

El fichero en sudoers.d tiene extensión .conf o similar:

# sudo ignora ficheros con punto en el nombre
ls /etc/sudoers.d/
# jaime.conf   ← ignorado
# jaime        ← correcto

El usuario no ha cargado el nuevo grupo:

Si añades un usuario a sudo o wheel y el usuario ya tiene sesión abierta, el grupo nuevo no está activo hasta que cierre sesión y vuelva a entrar. Alternativa sin cerrar sesión:

newgrp sudo    # Debian/Ubuntu
newgrp wheel   # RedHat/Rocky

Auditar quién usa sudo

Con journald (sistemas modernos con systemd)

En cualquier sistema con systemd puedes consultar los logs de sudo directamente con journalctl, sin buscar en ficheros:

# Todos los eventos de sudo
journalctl -t sudo

# Solo los comandos ejecutados (sin los intentos fallidos)
journalctl -t sudo | grep COMMAND

# En tiempo real
journalctl -t sudo -f

# Los últimos 50 eventos
journalctl -t sudo -n 50

# Filtrar por usuario
journalctl -t sudo | grep "jaime : "

Con los ficheros de log clásicos

En Debian/Ubuntu los intentos de sudo quedan en /var/log/auth.log:

grep sudo /var/log/auth.log | tail -20

En RedHat/Rocky en /var/log/secure:

grep sudo /var/log/secure | tail -20

Para ver solo los comandos ejecutados con éxito:

grep "sudo:.*COMMAND" /var/log/auth.log

Formato de un evento de sudo en el log

Saber leer una línea de log evita confusiones:

May 28 17:43:12 lola sudo: jaime : TTY=pts/1 ; PWD=/home/jaime ; USER=root ; COMMAND=/usr/bin/systemctl restart nginx
  • jaime — quién ejecutó sudo
  • TTY=pts/1 — desde qué terminal
  • PWD= — directorio de trabajo en el momento
  • USER=root — como quién se ejecutó
  • COMMAND= — el comando exacto

Referencia rápida

Referencia rápida de sudo — IMPERIAL SYSTEMS