The sudo (superuser do) tool allows designated users to run commands with elevated privileges without sharing the root password. This is fundamental to the principle of least privilege: each administrator gets exactly the access they need for their role, no more. When access is misconfigured — for example with blanket ALL=(ALL) ALL entries or unsafe NOPASSWD directives — sudo becomes a privilege escalation path rather than a control. On RHEL 9, the sudoers configuration system is mature and powerful, supporting per-user rules, per-group rules, command aliases, runas aliases, host aliases, audit logging, and per-command password requirements.
This guide covers configuring sudo on RHEL 9: using visudo safely, adding users to the wheel group, writing fine-grained sudoers rules, using the /etc/sudoers.d/ drop-in directory, configuring password-less sudo for specific commands, setting up sudo audit logging, and avoiding common misconfigurations.
Prerequisites
- RHEL 9 server with root access (you need root to configure sudoers)
- At least one non-root user account to grant sudo access
Step 1 — Understand How sudo Works on RHEL 9
On RHEL 9, members of the wheel group have full sudo access by default. This is configured in /etc/sudoers:
## Allows people in group wheel to run all commands
%wheel ALL=(ALL) ALL
When a user runs sudo command, sudo checks the sudoers rules in order and grants or denies the request based on the first matching rule.
Step 2 — Add a User to the wheel Group
The simplest way to grant full sudo access is to add the user to wheel:
usermod -aG wheel adminuser
The change takes effect on next login. Verify:
groups adminuser
# or
id adminuser
Test as that user:
su - adminuser
sudo whoami # Should print: root
Step 3 — Always Edit sudoers with visudo
Never edit /etc/sudoers directly with a text editor. A syntax error in sudoers can lock all users out of sudo and prevent recovery without booting to rescue mode. visudo validates the syntax before saving:
visudo
To use a different editor:
EDITOR=nano visudo
Step 4 — Grant Full sudo Access to a Specific User
Add directly in /etc/sudoers (using visudo):
adminuser ALL=(ALL) ALL
Rule format: USER HOST=(RUNAS_USER:RUNAS_GROUP) COMMAND
USER: who this rule applies to (%groupnamefor a group)HOST: which host the rule applies to (ALLmeans any host)RUNAS_USER: which user to run as (ALLmeans any user, typically root)COMMAND: what commands are allowed (ALLmeans any command)
Step 5 — Grant Limited sudo Access
Best practice is to grant only the specific commands a user needs. Use sudoers drop-in files in /etc/sudoers.d/ to keep rules organised:
visudo -f /etc/sudoers.d/webteam
# Allow webteam members to restart Nginx and Apache only
%webteam ALL=(ALL) /usr/bin/systemctl restart nginx, /usr/bin/systemctl restart httpd
%webteam ALL=(ALL) /usr/bin/systemctl reload nginx, /usr/bin/systemctl reload httpd
%webteam ALL=(ALL) /usr/bin/systemctl status nginx, /usr/bin/systemctl status httpd
# Allow dbadmin to run only database backup script
dbadmin ALL=(ALL) /opt/scripts/backup-db.sh
# Allow appuser to restart only their application
appuser ALL=(ALL) /usr/bin/systemctl restart myapp, /usr/bin/systemctl stop myapp, /usr/bin/systemctl start myapp
Step 6 — Configure Password-Less sudo for Specific Commands
Some automation scenarios require running a command via sudo without interactive password entry. Restrict NOPASSWD to specific commands, never to ALL:
visudo -f /etc/sudoers.d/deploy
# CI/CD deployment user can restart the app without a password
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl reload nginx
Never use deploy ALL=(ALL) NOPASSWD: ALL — this gives root equivalent access without any authentication barrier.
Step 7 — Use Command Aliases for Cleaner Rules
Command aliases group related commands for readable policy definitions:
visudo
# Define aliases
Cmnd_Alias NETWORKING = /usr/sbin/ip, /usr/bin/nmcli, /usr/sbin/ifconfig
Cmnd_Alias SERVICES = /usr/bin/systemctl start *, /usr/bin/systemctl stop *, /usr/bin/systemctl restart *, /usr/bin/systemctl reload *
Cmnd_Alias STORAGE = /usr/sbin/lvm, /usr/sbin/fdisk, /usr/bin/parted
Cmnd_Alias USERTOOLS = /usr/sbin/useradd, /usr/sbin/usermod, /usr/sbin/userdel
# Apply to a group
%netadmin ALL=(ALL) NETWORKING
%sysadmin ALL=(ALL) SERVICES, STORAGE
%useradmin ALL=(ALL) USERTOOLS
Step 8 — Configure sudo Audit Logging
By default, sudo logs to /var/log/secure and via syslog. To also log the full command output (useful for compliance), install sudo-logsrvd or configure the IO log:
visudo
# Enable IO logging for all commands
Defaults log_input, log_output
Defaults iolog_dir="/var/log/sudo-io/%{user}/%{command}"
View the audit log for all sudo activity:
grep sudo /var/log/secure | tail -20
Or use auditd for more detailed tracking:
auditctl -w /usr/bin/sudo -p x -k sudo_exec
ausearch -k sudo_exec -ts today
Step 9 — Prevent Privilege Escalation via sudo
Be aware of commands that can be used to escape to a full shell even when granted only for a specific purpose:
# DANGEROUS — these commands allow shell escape and should never be in sudoers:
# vim, nano, less, more, man, find, awk, python3, perl, bash, sh
# To prevent shell escapes, explicitly deny dangerous commands
# Cmnd_Alias SHELLS = /usr/bin/bash, /usr/bin/sh, /usr/bin/zsh
# Deny_Cmnd SHELLS
# Only allow specific, absolute paths — no wildcards at the end of paths
# GOOD: /usr/bin/systemctl restart nginx
# BAD: /usr/bin/systemctl * (allows "sudo systemctl start bash")
Step 10 — Configure sudo Timeout (Credential Caching)
By default, sudo remembers your password for 5 minutes. Adjust this:
visudo
# Set sudo password cache to 10 minutes
Defaults timestamp_timeout=10
# Require password each time (no caching)
Defaults timestamp_timeout=0
# Per-user override
Defaults:adminuser timestamp_timeout=30
Verification Checklist
# List all sudo rules (requires sudo itself)
sudo -l
# List sudo rules for a specific user (as root)
sudo -l -U adminuser
# Check which groups a user is in
id adminuser
# Test that syntax is valid
visudo -c
# Check drop-in files syntax
visudo -c -f /etc/sudoers.d/webteam
Troubleshooting
- sudo: command not found — sudo is not in PATH. Use the full path
/usr/bin/sudoor checkecho $PATH. - user is not in the sudoers file — add to wheel group with
usermod -aG wheel usernamethen log out and in again. - sudo asks for password even with NOPASSWD — ensure the NOPASSWD rule is the last matching rule. Later rules override earlier ones. Verify with
sudo -l -U username. - Locked out of sudo due to syntax error — boot to rescue mode or use the console. Run
visudo -cto find the error, correct it, and reboot.
Security Considerations
- Grant least privilege — give users only the commands they actually need.
- Audit sudo usage regularly:
grep sudo /var/log/secure | grep -i "command". - Review sudoers files whenever staff leave. Remove or disable their entries.
- Never grant sudo access to text editors, shell interpreters, or file managers — they all allow full shell escape.
Conclusion
Your RHEL 9 server now has a properly configured sudo policy: users are added to the wheel group for full access, drop-in files in /etc/sudoers.d/ provide fine-grained per-team rules, and audit logging captures all sudo activity. Sudo is properly restricted to prevent privilege escalation through editor escape paths.
Next steps: How to Perform a System Security Audit with auditd on RHEL 9, How to Configure SELinux on RHEL 9, and How to Set Up SSH Key-Based Authentication on RHEL 9.