Sudoers least privilege means every administrator gets only the commands their job actually needs, instead of a blanket ALL=(ALL) ALL rule that hands out full root. You will create a dedicated admin group, write a per-group drop-in file under /etc/sudoers.d/, validate it with visudo, and confirm that a normal account can run one service command and nothing else.
The examples work on Ubuntu 22.04/24.04, Debian 12 and AlmaLinux/Rocky 8–9. Exact binary paths differ slightly between distributions, so check them on your own server before you copy a rule.
Before you start
You need:
- A Linux VPS with root access, or an account that already has full sudo.
- A second SSH session open and logged in as root. Keep it open until you have tested the new rules.
- Names of the commands the user must run, for example
systemctl restart nginxorjournalctl -u nginx. - The real path of each binary. Run
command -vas shown below;/binis a symlink to/usr/binon modern distributions, but sudo matches the literal string you write.
Warning: A malformed sudoers file can remove all sudo access, including your own. Never edit /etc/sudoers or its drop-ins with a plain text editor. Use visudo only, and keep that root SSH session open while you test.
Collect the facts first:
sudo -V | head -n 1
sudo grep -R "includedir" /etc/sudoers
ls -l /etc/sudoers.d/
command -v systemctl nginx journalctl useradd visudoYou should see an @includedir /etc/sudoers.d or #includedir /etc/sudoers.d line. That line is what makes drop-in files work, and it means you never have to touch the main file.
Understand the rule format
Every sudoers rule has four parts:
who where=(as_whom) whatFor example, %webops ALL=(root) /usr/bin/systemctl restart nginx reads as: members of the webops group, on any host, may run that single command as root. A % prefix means a group, # means a numeric user ID.
Two details matter for least privilege:
(root)limits the rule to the root account.(ALL)also allows running as any other user, which is wider than most people intend.- Sudo compares the command line, including arguments, exactly.
/usr/bin/systemctl restart nginxdoes not allowrestart apache2, and it does not allow a baresystemctl.
Create a dedicated admin group
Avoid adding staff to sudo or wheel. Use a group named after the job, so permissions stay meaningful.
sudo groupadd -r webops
sudo useradd -m -s /bin/bash -G webops deploy
sudo passwd deploy
id deployid deploy should list webops. The account must not appear in the sudo or wheel group, and it needs no entry in /etc/sudoers at all.
Write the sudoers drop-in file
Open a new file with visudo -f. This creates it with the correct 0440 permissions and runs the syntax check on save.
sudo visudo -f /etc/sudoers.d/90-webopsAdd this content, adjusting the paths to match the output of command -v:
Cmnd_Alias WEB_SVC = /usr/bin/systemctl restart nginx, \
/usr/bin/systemctl reload nginx, \
/usr/bin/systemctl status nginx, \
/usr/bin/journalctl -u nginx -n 200
%webops ALL=(root) WEB_SVCNotes on this file:
- Command aliases keep rules readable and let you change the set in one place.
- The backslash continues the alias across lines. Never leave a space after it.
- File names in
/etc/sudoers.d/must not contain a dot and must not end in~, or sudo silently ignores them. - Skip
NOPASSWDunless the command genuinely runs from automation. A password prompt is one more control on an interactive account.
Warning: Do not grant sudo on interpreters or tools that can spawn a shell or write files: bash, sh, python3, vim, less, find, tar, systemctl without a verb, docker, journalctl with a trailing *. Any of these is effectively full root.
Check the syntax and the effective rules
Parse the whole configuration, then ask sudo what the account can actually run:
sudo visudo -c
sudo -l -U deploy
sudo -ll -U deployvisudo -c ends with parsed OK when nothing is broken. sudo -ll prints a long listing showing the exact command strings, which is the fastest way to spot a rule you did not intend.
To keep a record of privileged commands, add these lines to /etc/sudoers.d/90-webops:
Defaults logfile="/var/log/sudo.log"
Defaults log_input, log_output
Defaults iolog_dir="/var/log/sudo-io"log_input and log_output record sessions, so plan for log rotation and disk use on a busy server. Without these lines, sudo events still reach the journal on most systems.
Check it works
Test as the real user, not as root:
su - deploy
id
sudo -k
sudo -l
sudo systemctl status nginxThe sudo -l output should list only the alias members. Then confirm the deny path:
sudo systemctl restart apache2Expect a message like Sorry, user deploy is not allowed to execute '/usr/bin/systemctl restart apache2' as root on <hostname>. That refusal is the point of the exercise: the account is useful for its job and useless for anything else.
Also confirm logging works:
sudo tail -n 5 /var/log/sudo.logTroubleshooting
deploy is not in the sudoers file— the user is not in the group yet, and group membership only applies to new sessions. Runid deploy, then log in again. Also check the drop-in filename has no dot and the file is mode0440.sudo: sorry, you must have a tty to run sudo— a globalDefaults requirettyis left over from an old template. Remove that line from/etc/sudoerswithvisudorather than overriding it per group.parse error in /etc/sudoers.d/90-webops near line 4— usually a missing newline at the end of the file, a backslash followed by a space, or a comma in the wrong place. Reopen withsudo visudo -fand fix, then rerunsudo visudo -c.- The rule exists but sudo still denies the command — the path or arguments differ. Compare
sudo -ll -U deploywith the output ofcommand -vand the exact command line the user types. Sudo is strict about arguments. - You locked yourself out of sudo — use the browser console in your VPS provider's control panel to log in as root, then repair the file with
visudo. Do not reboot first; you may lose the chance to fix it cleanly.
Next steps
- Review the basics of safe editing in Edit the sudoers file safely with visudo.
- Cut down which accounts can reach a shell at all in SSH Hardening: Auditing and Limiting Shell Access for Service Accounts.
- Detect unauthorised changes to
/etc/sudoers.d/with Linux Security: Using aide for File Integrity Monitoring on VPS. - Keep sudo itself patched using Proactive Patch Management: Automating Security Updates on Linux VPS.
