Security

Linux Security: Hardening the sudoers File for Least Privilege

Restrict sudo to the exact commands each administrator needs. Write safe sudoers drop-in files, validate them with visudo and keep an audit trail of privileged commands.

AdvancedLinux server6 min read

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 nginx or journalctl -u nginx.
  • The real path of each binary. Run command -v as shown below; /bin is a symlink to /usr/bin on 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:

Bash
sudo -V | head -n 1
sudo grep -R "includedir" /etc/sudoers
ls -l /etc/sudoers.d/
command -v systemctl nginx journalctl useradd visudo

You 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)  what

For 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 nginx does not allow restart apache2, and it does not allow a bare systemctl.

Create a dedicated admin group

Avoid adding staff to sudo or wheel. Use a group named after the job, so permissions stay meaningful.

Bash
sudo groupadd -r webops
sudo useradd -m -s /bin/bash -G webops deploy
sudo passwd deploy
id deploy

id 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.

Bash
sudo visudo -f /etc/sudoers.d/90-webops

Add 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_SVC

Notes 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 NOPASSWD unless 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:

Bash
sudo visudo -c
sudo -l -U deploy
sudo -ll -U deploy

visudo -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:

Bash
su - deploy
id
sudo -k
sudo -l
sudo systemctl status nginx

The sudo -l output should list only the alias members. Then confirm the deny path:

Bash
sudo systemctl restart apache2

Expect 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:

Bash
sudo tail -n 5 /var/log/sudo.log

Troubleshooting

  • deploy is not in the sudoers file — the user is not in the group yet, and group membership only applies to new sessions. Run id deploy, then log in again. Also check the drop-in filename has no dot and the file is mode 0440.
  • sudo: sorry, you must have a tty to run sudo — a global Defaults requiretty is left over from an old template. Remove that line from /etc/sudoers with visudo rather 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 with sudo visudo -f and fix, then rerun sudo visudo -c.
  • The rule exists but sudo still denies the command — the path or arguments differ. Compare sudo -ll -U deploy with the output of command -v and 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

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.