Users, Groups and sudoLesson 4 of 4
Privileged access with sudo
Give people the root powers they need and no more: wheel, drop-in sudoers files, command lists and passwordless rules.
sudovisudosudo -lsudo -uvisudo -c/etc/sudoers.djournalctl _COMM=sudo
After this lesson you can
- Grant full administrative access with the wheel group, and revoke it.
- Write a rule for specific commands, with or without a password, in a drop-in file that is syntax-checked.
- Read what a user may run with sudo -l, and find who ran what in the logs.
The idea
sudo runs one command as another user (root by default), after checking /etc/sudoers and every file in /etc/sudoers.d/. Compared with sharing the root password it gives you three things: individual accountability (each use is logged with the real user), least privilege (a user can be limited to certain commands), and no shared secret to change when someone leaves.
On RHEL, members of the group wheel are administrators. The line %wheel ALL=(ALL) ALL in /etc/sudoers is already active, so adding a user to wheel is the standard way to make them an admin.
For anything more specific, add a drop-in file in /etc/sudoers.d/. The rule format is:
who where=(as_whom) [NOPASSWD:] commands
| Part | Example | Meaning |
|---|---|---|
| who | erin, %webops | A user, or a group (prefix %) |
| where | ALL | Which hosts the rule applies to |
| as_whom | (root), (ALL) | The users they may become |
| commands | /usr/bin/systemctl restart chronyd | Full paths. ALL allows everything |
Try it
sudo usermod -aG wheel dana
sudo -l -U dana
User dana may run the following commands on rhel10:
(ALL) ALL
sudo visudo -f /etc/sudoers.d/erin
erin ALL=(root) NOPASSWD: /usr/bin/systemctl restart chronyd
sudo visudo -c
sudo -l -U erin
/etc/sudoers: parsed OK
/etc/sudoers.d/erin: parsed OK
User erin may run the following commands on rhel10:
(root) NOPASSWD: /usr/bin/systemctl restart chronyd
Command aliases for readability
Cmnd_Alias WEB = /usr/bin/systemctl start httpd, \
/usr/bin/systemctl stop httpd, \
/usr/bin/systemctl restart httpd, \
/usr/bin/systemctl status httpd
%webops ALL=(root) WEB
Matching is on the exact command line. systemctl restart httpd is allowed above; systemctl restart httpd --now or systemctl restart sshd are not.
The audit trail
Every sudo use is logged with the real user. On RHEL the entries go to the journal and to /var/log/secure:
sudo journalctl _COMM=sudo --no-pager | tail -2
Nov 03 11:21:40 rhel10 sudo[3011]: dana : TTY=pts/0 ; PWD=/home/dana ; USER=root ; COMMAND=/usr/bin/id
Nov 03 11:21:40 rhel10 sudo[3011]: pam_unix(sudo:session): session opened for user root(uid=0) by dana(uid=1002)
Going further
sudo -l (no user) lists your own rights. sudo -u bob command runs as another user, and sudo -k forgets your cached password so the next use asks again (the cache lasts 5 minutes by default). Never give NOPASSWD: ALL to a human account, and never allow commands that can start a shell (vi, less, find -exec): a user can escape to a root shell from them. For passwordless rules use the narrowest possible command line.
Practice
0 of 5 done
Do these on your own system. They get harder. Check each result, then tick it off.
- Warm-up
Make someone an administrator
Give
danafull sudo rights using group membership, not by editing the sudoers file. Verify as dana, then remove the right again.Check your result
sudo -l -U danalists(ALL) ALLwhile dana is inwheel, and saysUser dana is not allowed to run sudoafter removal.Hint
On RHEL the line
%wheel ALL=(ALL) ALLis already active. Add or remove dana inwheel.Solution
sudo usermod -aG wheel dana sudo -l -U dana sudo gpasswd -d dana wheel sudo -l -U dana - Practice
Allow one command, no password
Let the user
erinrestart thechronydservice with sudo and no password, but nothing else. Write the rule in its own file under/etc/sudoers.dand check its syntax before relying on it. Test as erin thatsystemctl restart chronydworks andsystemctl restart sshdis refused.Check your result
sudo visudo -cprintsparsed OK.sudo -l -U erinshowsNOPASSWD: /usr/bin/systemctl restart chronyd. As erin the sshd restart asks for a password or is denied.Hint
erin ALL=(root) NOPASSWD: /usr/bin/systemctl restart chronyd. Usevisudo -f /etc/sudoers.d/erinso the file is checked on save.Solution
sudo visudo -f /etc/sudoers.d/erin # erin ALL=(root) NOPASSWD: /usr/bin/systemctl restart chronyd sudo visudo -c sudo -l -U erin sudo -u erin sudo -n systemctl restart chronyd sudo -u erin sudo -n systemctl restart sshd - Practice
A rule for a whole group, with aliases
Create a group
webops. Let its members runsystemctl start|stop|restart|status httpdand read the Apache log withtailon/var/log/httpd/error_logas root, entering their own password. Use aCmnd_Aliasso the rule stays readable.Check your result
sudo visudo -cparses OK.sudo -l -U <member>lists the allowed commands.Hint
Cmnd_Alias WEB = /usr/bin/systemctl start httpd, ...then%webops ALL=(root) WEB.Solution
sudo groupadd webops sudo visudo -f /etc/sudoers.d/webops # Cmnd_Alias WEB = /usr/bin/systemctl start httpd, /usr/bin/systemctl stop httpd, \ # /usr/bin/systemctl restart httpd, /usr/bin/systemctl status httpd, \ # /usr/bin/tail /var/log/httpd/error_log # %webops ALL=(root) WEB sudo visudo -c - Practice
Find who did what
As
dana(with sudo rights) run a harmless sudo command. Then, as an administrator, find the log entry that records the user, the directory, the target user and the exact command.Check your result
The journal or
/var/log/securehas a line containingdana : TTY=... ; PWD=... ; USER=root ; COMMAND=....Hint
sudo journalctl _COMM=sudoorsudo grep 'COMMAND=' /var/log/secure.Solution
sudo -u dana sudo id sudo journalctl _COMM=sudo --no-pager | tail -3 - Challenge
Recover from a broken sudoers file
Explain why you should never edit
/etc/sudoerswith a normal editor. Then create a deliberately wrong file/etc/sudoers.d/badcontainingthis is not validin a way that is caught (usevisudo -c -f), and show that the live system is not affected because the check ran before the file was installed.Check your result
sudo visudo -c -f /tmp/badreports a syntax error and a non-zero exit status.sudo -lstill works because the bad file never reached/etc/sudoers.d.Hint
Write the candidate to
/tmp/bad, check it withvisudo -cf, and onlyinstall -m 0440it if the check passes.Solution
echo 'this is not valid' > /tmp/bad sudo visudo -c -f /tmp/bad; echo $? sudo -l