How to Configure sudo Safely
Learn to configure sudo securely using visudo, sudoers syntax, per-command restrictions, NOPASSWD, and drop-in files to enforce least-privilege access on Linux.
Before you start
- ▸A non-root user account that currently has sudo or root access
- ▸Basic familiarity with a terminal text editor (nano, vim, etc.)
- ▸Root or existing sudo access to apply changes
sudo is the gatekeeper between a normal user account and root-level access. A misconfigured sudoers file can hand an attacker a trivial privilege escalation path, or lock out legitimate administrators from a production system. Neither outcome is acceptable. This guide walks through editing sudoers safely with visudo, understanding the syntax in depth, writing per-command rules, and using NOPASSWD only where it is genuinely justified.
Why You Must Use visudo
The sudoers file is parsed every time sudo is invoked. A syntax error does not crash a daemon — it silently breaks all sudo access. visudo validates the file before saving, preventing you from locking yourself out. Never edit /etc/sudoers directly with a plain text editor.
sudo visudo
By default visudo uses the editor defined in EDITOR or VISUAL. To override once:
sudo EDITOR=nano visudo
To change the default permanently, add this to /etc/sudoers (via visudo):
Defaults editor=/usr/bin/nano
Understanding sudoers Syntax
Every non-comment line in sudoers follows one of two forms: a Defaults entry or a rule entry.
The Rule Format
A privilege rule looks like this:
WHO WHERE=(AS_WHOM) COMMANDS
- WHO — a username, a
%groupname, or an alias. - WHERE — the hostname or
ALL. UseALLfor most setups. - AS_WHOM — the target user (and optionally group) to run as.
(ALL:ALL)means any user and any group;(root)means root only. - COMMANDS — a comma-separated list of absolute paths, or
ALL.
The classic wheel/sudo group line that ships with most distros:
%sudo ALL=(ALL:ALL) ALL # Debian/Ubuntu group
%wheel ALL=(ALL:ALL) ALL # Fedora/RHEL/Arch group
Defaults Entries
Defaults tune sudo's behaviour globally or per-user. Several are worth setting explicitly:
# Require re-authentication after 5 minutes of inactivity (default is 15)
Defaults timestamp_timeout=5
# Log each sudo invocation to syslog with the full command
Defaults log_output
# Prevent environment variable injection attacks
Defaults env_reset
Defaults env_keep += "LANG LC_ALL LC_MESSAGES TERM"
# Insult users who type the wrong password (optional, morale-based security)
Defaults insults
env_reset is enabled by default on most distros; verify it is present and not overridden.
Drop-in Files: /etc/sudoers.d/
Rather than editing /etc/sudoers directly for every change, place per-role or per-application rules in the /etc/sudoers.d/ directory. Each file is included automatically. This makes auditing and configuration management (Ansible, Puppet, etc.) far cleaner.
sudo visudo -f /etc/sudoers.d/webops
visudo -f applies the same syntax check to drop-in files. File names in sudoers.d must not contain a dot or end with a tilde — both cause sudo to silently ignore the file.
# Correct
/etc/sudoers.d/webops
# Will be ignored by sudo
/etc/sudoers.d/webops.conf
/etc/sudoers.d/webops~
Per-Command Rules
Granting full ALL access is appropriate for trusted admins. For service accounts, automation users, and junior operators, restrict to specific commands only.
Single Command
# Allow alice to restart nginx only
alice ALL=(root) /usr/bin/systemctl restart nginx
Multiple Commands
# Allow the 'dbops' group to manage the postgresql service
%dbops ALL=(root) /usr/bin/systemctl start postgresql, \
/usr/bin/systemctl stop postgresql, \
/usr/bin/systemctl restart postgresql, \
/usr/bin/systemctl status postgresql
Command Aliases for Readability
When the same command set appears in multiple rules, define a Cmnd_Alias:
Cmnd_Alias NGINX_CTL = /usr/bin/systemctl start nginx, \
/usr/bin/systemctl stop nginx, \
/usr/bin/systemctl restart nginx, \
/usr/bin/systemctl reload nginx
%webops ALL=(root) NGINX_CTL
Blocking Dangerous Escape Paths
Allowing a user to run an editor, shell, or interpreter via sudo effectively gives them unrestricted root access — they can drop to a shell or write arbitrary files. Be explicit about exclusions with !:
# Dangerous: do not do this
bob ALL=(root) /usr/bin/vim
# If you genuinely need file editing, use sudoedit instead
bob ALL=(root) sudoedit /etc/nginx/nginx.conf
sudoedit (or sudo -e) copies the file to a temporary location, opens it as the invoking user, then copies it back — no root-owned editor process that can be escaped.
Using NOPASSWD Carefully
NOPASSWD tells sudo not to prompt for a password before running the specified commands. It is appropriate for:
- Automated scripts and CI/CD pipelines where there is no interactive session.
- Specific, narrow, low-risk commands (e.g., reading a status file).
It is not appropriate as a blanket convenience for human admin accounts. An unattended terminal with a passwordless-sudo account is a trivial escalation vector.
Syntax
# CI runner: restart the app service without a password
ci-runner ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp
# Deployment user: limited deployment commands, no password
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl reload nginx, \
/usr/bin/systemctl restart myapp
Mixing PASSWD and NOPASSWD
You can require a password for most commands but not for specific ones on the same line:
alice ALL=(root) PASSWD: ALL, NOPASSWD: /usr/bin/systemctl status nginx
User and Host Aliases
For larger environments, aliases reduce repetition and make the policy readable:
User_Alias WEBTEAM = alice, bob, carol
User_Alias DBAGROUP = dave, eve
Host_Alias WEBSERVERS = web1, web2, web3
WEBTEAM WEBSERVERS=(root) NGINX_CTL
DBAGROUP ALL=(root) /usr/bin/systemctl restart postgresql
Verifying Your Configuration
Check what sudo privileges a specific user has without impersonating them:
sudo -l -U alice
Typical output (will vary):
User alice may run the following commands on hostname:
(root) /usr/bin/systemctl restart nginx
Validate the entire sudoers configuration (including all drop-in files) for syntax errors:
sudo visudo -c
A clean config returns: parsed OK.
Auditing sudo Usage
Every sudo invocation is logged. On systemd-based systems:
journalctl -t sudo --since today
On systems writing to syslog:
grep sudo /var/log/auth.log # Debian/Ubuntu
grep sudo /var/log/secure # Fedora/RHEL
Troubleshooting
Locked out of sudo
If you save a broken sudoers file outside of visudo, boot to a root shell via the GRUB recovery menu, or use another session where you are already root, then run visudo to fix the file.
"sudo: command not found" for a valid binary
sudo resets PATH by default (env_reset). Use the full absolute path in your rule and in the command invocation, or add the needed path to secure_path:
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Drop-in file silently ignored
Confirm the filename contains no dots and does not end in a tilde. Check that the main sudoers file still contains the #includedir /etc/sudoers.d directive (note: despite the #, this is not a comment — it is a sudo directive).
grep includedir /etc/sudoersFrequently asked questions
- Is it safe to give a user NOPASSWD: ALL?
- No. NOPASSWD: ALL means any process running as that user — including malware — can gain root silently. Restrict NOPASSWD to specific, low-risk commands on dedicated automation accounts only.
- What is the difference between sudo -i, sudo -s, and sudo su?
- All three effectively give an interactive root shell, but they set different environments and are logged differently. From a sudoers perspective, all three require the user to have ALL or shell access — which is why granting them to untrusted users is dangerous regardless of method.
- Why do my changes to /etc/sudoers.d/ seem to have no effect?
- The filename almost certainly contains a dot or a tilde. Rename the file to contain only letters, digits, hyphens, or underscores, then verify the main /etc/sudoers still has the #includedir /etc/sudoers.d line.
- Can I restrict sudo to specific source terminals or SSH sessions?
- Yes. The NOTTY and requiretty Defaults options control whether sudo requires a real TTY. You can also scope rules by hostname in multi-server environments using Host_Alias, though this is no substitute for host-level access control.
- How do I recover if I accidentally break the sudoers file?
- Boot into single-user mode or a GRUB recovery shell (which gives a root prompt without requiring sudo), then run visudo to correct the syntax error. On cloud instances, use the provider's emergency console or a recovery disk.
Related guides
Manage Secrets with Ansible Vault
Encrypt Ansible secrets with AES-256 using ansible-vault: encrypt files and inline vars, automate with password files, and isolate group-level secrets with vault IDs.
AppArmor Explained
Learn how AppArmor profiles work, how to switch between enforce and complain mode, create new profiles, and diagnose access denials on Ubuntu, Debian, and Arch.
Apply CIS Benchmarks with OpenSCAP
Use OpenSCAP and scap-security-guide to evaluate, report on, and remediate Linux systems against CIS Benchmarks — covering install, eval, and automation.
How to Audit a Linux System with auditd
Set up auditd on Linux to track file access, syscalls, and privilege use. Covers persistent rules, file watches, ausearch, and aureport across major distros.