sudo is the main privilege boundary on most Linux servers, and a single loose sudoers rule, such as a wildcard argument or a NOPASSWD entry for an automation account, quietly turns it into a root shell for whoever reaches that account. sudoers syntax looks simple, but its matching rules (last match wins, no arguments means any arguments, wildcards that match spaces) defeat many rules that read as restrictive. This guide covers how sudo makes decisions, how to design rules for administrators and service accounts, how to log and centralise sudo activity, a hardening baseline, and the misconfigurations auditors find most often. It is for Linux administrators, platform teams and security engineers. Attack techniques are covered in the Linux Privilege Escalation Techniques Guide; this page is about designing and operating sudo safely.
Key takeaways
- Allow lists, not deny lists. The sudoers manual warns that negating
ALLto allow "all but a few" commands rarely works as intended; grant specific commands with fixed arguments instead. - Arguments are part of the rule. A command listed without arguments may be run with any arguments, and wildcards in arguments can match whitespace, so
systemctl restart app*can match far more than one unit. - Many commands are shells in disguise. Editors, pagers, interpreters,
find,tarand package managers can launch other programs; usesudoeditfor file edits and NOEXEC or INTERCEPT where they work. - Service account rules need the most care. Automation accounts usually get NOPASSWD, so their sudo rights equal whatever their SSH key or token can reach; scope them to the exact commands the job runs.
- Patch sudo itself. CVE-2021-3156 (Baron Samedit) and CVE-2025-32463 (chroot) gave root to users who were not in sudoers at all; both are in the CISA KEV catalog.
How identity and access work in sudo
When a user runs sudo command, the sudoers policy plugin identifies the invoking user, finds matching rules, authenticates the user through PAM (unless a tag says otherwise), then runs the command as the target user (root by default, set by runas_default) in a reset environment.
Anatomy of a rule
A user specification reads: who, on which hosts, as whom, may run what, with which tags. For example, %web-ops ALL = (root) /usr/bin/systemctl restart nginx.service lets members of the web-ops group restart one unit as root on any host that receives this file.
| Element | Example | Security note |
|---|---|---|
| User or group | alice, %web-ops, User_Alias | Prefer directory groups so joiners and leavers are handled by IGA |
| Host | ALL, Host_Alias WEB = web01, web02 | Host matching is by name the host reports; use it with central rules, not as a strong control |
| Run-as | (root), (postgres), (ALL : ALL) | (ALL) allows any target user; name the account instead |
| Command | /usr/bin/systemctl restart nginx.service | Full path; no arguments listed means any arguments; "" means none |
| Digest | sha256:<hash> /opt/tools/rotate.sh | Runs only if the file hash matches; the manual warns a user who can write the file can swap it after the check |
| Tags | NOPASSWD:, NOEXEC:, INTERCEPT:, SETENV:, LOG_INPUT: | SETENV is implied for ALL, so ALL rules also let users pass environment variables |
Matching rules that surprise people
- Last match wins: when several entries match, the last one applies, and it is not necessarily the most specific. A broad rule in a later include file overrides a careful rule earlier.
- Wildcards in paths do not cross directories (
/usr/bin/*does not match subdirectories) but wildcards in arguments match/and whitespace, so/usr/bin/cat /var/log/app/*also matches/var/log/app/../../../etc/shadowand additional file arguments. - Regular expressions (sudo 1.9.10 and later) must begin with
^and end with$and are matched separately for the path and the arguments. The manual prefers them over wildcards for arguments, and strongly discourages negated regular expressions because relative paths bypass them. - Includes:
@includedir /etc/sudoers.dreads files in lexical order and skips names containing a.or ending in~, so a file calledapp.confis silently ignored.visudoonly checks included files with-for-c.
Admin roles and privileged access
Most estates need three or four sudo patterns. Decide which applies to each role and encode it centrally.
| Pattern | Rule shape | When to use | Controls that must come with it |
|---|---|---|---|
| Full administrator | %linux-admins ALL=(ALL:ALL) ALL | Small number of platform engineers | MFA at login and for sudo, I/O logging, named accounts, short timestamp_timeout |
| Delegated operator | Cmnd_Alias of exact commands with fixed arguments | Application support, DBAs, on-call | No shells, editors or interpreters in the alias; sudoedit for config files |
| Run as application account | (appsvc) /opt/app/bin/* or specific scripts | Developers deploying or debugging an application | Scripts owned by root, not writable by the caller or the target |
| Just-in-time elevation | Rule present only while an approved request is open (PAM or IGA tool pushes it, or a time-bounded rule with NOTAFTER) | Regulated environments, production access | Approval record, automatic removal, session recording |
- Editing files safely:
%web-ops ALL = sudoedit /etc/nginx/conf.d/site.conflets users edit as themselves on a temporary copy, withsudoedit_checkdir(on by default) refusing paths through user-writable directories and symlinks not followed unlesssudoedit_followis set. sudoedit must be written without a path. - Re-authentication window:
timestamp_timeoutdefaults to 5 minutes and records are per terminal by default. ATT&CK T1548.003 notes adversaries abuse cached sudo credentials; reduce the timeout for high-risk hosts or set it to 0 for full administrators who use MFA. - Password choice:
rootpw,targetpwandrunaspware off by default and should stay off: they mean sharing the root or target password. - MFA for sudo: sudo authenticates through
/etc/pam.d/sudo, so MFA at SSH login does not automatically apply to sudo. See the Linux PAM Guide. - Central PAM tooling: where a privileged access management product brokers root access, keep sudo as the enforcement point on the host and let the tool manage rule lifecycle and session recording. See the Privileged Access Management Guide.
Integrations, API tokens and service accounts
Configuration management (Ansible become, Puppet, Salt), backup agents, monitoring, CI deployers and vulnerability scanners all elevate through sudo. They run unattended, so they almost always use NOPASSWD, which makes the account's SSH key or token the only barrier to root.
- Scope by job: a deploy account that restarts two services and copies artefacts needs those commands, not
ALL. A scanner needing read access may need only specific read commands or can use a dedicated read-only group instead of sudo. - Accept full root where it is real: configuration management agents genuinely need root. Then make the credential the control: short-lived SSH certificates, keys restricted with
from=and forced commands where possible, the controller host treated as Tier 0, and alerts on use from other sources. See the SSH Key Management Guide. - No interactive shells: service accounts should not have rules like
(ALL) NOPASSWD: /bin/bashleft over from troubleshooting. - Scripts as commands: a rule that allows
/opt/app/deploy.shis only safe if the script, its directory and anything it sources are owned by root and not writable by the calling account, and if the script does not pass caller-controlled arguments to a shell. - Environment: keep
env_reset(on by default) andsecure_path; do not addSETENVorenv_keepentries such asLD_PRELOADorPYTHONPATH, which turn any allowed command into arbitrary code (ATT&CK T1574.006). - Inventory: list every account with NOPASSWD rules alongside its owner and credential type in your non-human identity inventory. See the Linux Service Accounts and Keytab Management Guide.
Central rule management
Local /etc/sudoers.d files deployed by configuration management are the most transparent option. Directory sources (sudo rules stored in FreeIPA or LDAP and delivered through SSSD, or AD-backed rules) give one place to review and revoke, with SSSD caching rules for offline use. Pick one source of truth per host class; mixing local files and directory rules makes "last match wins" much harder to reason about. See the FreeIPA and Red Hat Identity Management Guide.
Logging and detection
sudo logs allowed and denied commands by default (log_allowed and log_denied are on) to syslog, and can also write a dedicated logfile. I/O logging (log_input, log_output, off by default) records full sessions that sudoreplay can play back. Since sudo 1.9, log_servers sends event and I/O logs to a central sudo_logsrvd over TLS, with log_server_verify (on by default) checking the server certificate.
| Setting or signal | Purpose | Notes |
|---|---|---|
Defaults log_output (or LOG_OUTPUT: tag on admin rules) | Session recording for full administrators | Turn log_passwords off so passwords typed in sessions are masked in I/O logs |
Defaults log_subcmds | Logs programs spawned by the allowed command (1.9.8+) | Off by default, uses the intercept mechanism and may break some commands; pilot first |
Defaults log_servers=logsrv.example:30344 | Central, tamper-resistant copy of sudo logs | Local logs can be removed by the root user the session created |
| Denied sudo attempts | Reconnaissance with sudo -l and trial commands | Alert on bursts per account and on service accounts |
Writes to /etc/sudoers, /etc/sudoers.d | Persistence by adding a rule (T1548.003) | auditd watch or FIM; compare with configuration management |
| sudo to a shell or interpreter | Escape from a restricted rule (T1059.004) | Parent sudo, child bash, sh, python, perl in EDR telemetry |
Metrics
- Broad rules: count of rules granting
ALLcommands, by account type; service accounts withALLshould trend to zero or be individually justified. - NOPASSWD inventory: percentage of NOPASSWD rules with a named owner and review date.
- Dangerous commands: number of rules that allow editors, pagers, interpreters or shells as root, found by comparing rules with a list such as GTFOBins.
- Logging coverage: percentage of hosts sending sudo logs to the central log server or SIEM.
- Patch level: hosts running a sudo version affected by a KEV-listed CVE; target zero.
Hardening baseline
| Area | Setting | Why |
|---|---|---|
| Environment | Defaults env_reset, Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" | Stops PATH and loader variables from reaching root commands |
| Terminal | Defaults use_pty (default in current releases, confirm with sudo -V) | Prevents a background process from injecting input into the user's terminal after the command |
| Caching | Defaults timestamp_timeout= policy value (default 5) | Limits reuse of cached credentials |
| Logging | Defaults logfile=/var/log/sudo.log, log_output for admins, log_servers | Evidence that survives a compromised host |
| Rules | Full paths, fixed arguments or anchored regular expressions, no negated ALL | Matching does what it reads |
| Edits | sudoedit rather than editors as root | No shell escape from the editor |
| Shell escapes | NOEXEC: on commands that should not spawn others; INTERCEPT: where supported | NOEXEC only works for dynamically linked programs on supported systems; test each command |
| Files | /etc/sudoers and sudoers.d owned by root, mode 0440; changes only via visudo -c validated configuration management | Syntax errors can lock out administration; tampering is detectable |
| Version | sudo at or above 1.9.17p1 or the distribution's patched build | CVE-2025-32463 and CVE-2021-3156 fixes |
Tightening an existing estate
- Collect: pull
/etc/sudoers,sudoers.dand directory sudo rules from every host class; runvisudo -con each. - Classify: tag each rule as full admin, delegated, run-as application, or service account; flag
ALL, wildcards in arguments, editors and interpreters, andSETENV. - Measure use: from two to four weeks of sudo logs, list the commands each group and account actually runs.
- Rewrite: replace broad rules with Cmnd_Aliases built from observed use; add sudoedit for files.
- Pilot: deploy to one host class with denied-command alerting turned up, and a full-admin break-glass group kept unchanged.
- Roll out by configuration management, validating with
visudo -c -fbefore each deploy. - Rollback: keep the previous file set versioned; a failed validation should block the deploy rather than push a broken sudoers file.
Common misconfigurations
- Deny lists:
ALL, !/bin/su, !/bin/bashis bypassed by copying the binary, using another shell or using any program that spawns one. - Wildcard arguments:
/usr/bin/chown appuser /srv/app/*or/bin/cat /var/log/*match path traversal and extra arguments. - Interpreters and escape-capable tools as root:
vim,less,find,tar,awk,python,pip,dockerand package managers can all launch commands. GTFOBins lists binaries that can be abused this way in a sudo context; check every allowed command against it. - Writable targets: allowing a script the caller can edit, or a binary in a directory the caller can write, hands over root.
- Ignored include files: a file in
sudoers.dnamed with a dot is skipped, so the rule the team believes is restricting access is not loaded, while an older broad rule still applies. - Unpatched sudo: Baron Samedit (CVE-2021-3156, affected 1.8.2 to 1.8.31p2 and 1.9.0 to 1.9.5p1, fixed in 1.8.32 and 1.9.5p2) and the chroot flaw (CVE-2025-32463, affected 1.9.14 to 1.9.17, fixed in 1.9.17p1) both allow local users not listed in sudoers to gain root. The sudoedit flaw CVE-2023-22809 (1.8.0 to 1.9.12p1) let sudoedit users edit arbitrary files. The
-Rchroot option is now deprecated.
Framework mapping
| Control area | NIST SP 800-53 Rev. 5 | NIST CSF 2.0 | ATT&CK |
|---|---|---|---|
| Least privilege rules | AC-6, AC-6(10) | PR.AA-05 | T1548.003 |
| Separate admin and normal use | AC-6(2) | PR.AA-05 | T1078.003 |
| Logging privileged functions | AC-6(9), AU-12 | PR.PS-04 | T1548.003 |
| Protecting logs | AU-9 | PR.PS-04 | T1059.004 |
| Change control of sudoers | CM-5, CM-6 | PR.PS-01 | T1548.003 |
| Patching sudo | SI-2 | ID.RA-01 | T1548 |
Practitioner checklist
- Inventory all sudo rules (local and directory) per host class and validate each with
visudo -c. - Remove negated
ALLrules and wildcard arguments; use fixed arguments or anchored regular expressions. - Replace editors run as root with
sudoeditand remove shells, interpreters and escape-capable tools from delegated rules. - Scope every service account NOPASSWD rule to its job and record an owner and review date.
- Keep
env_reset,secure_pathanduse_pty, and never allow loader or interpreter variables throughenv_keeporSETENV. - Send sudo event logs to the SIEM and, for full administrators, I/O logs to a central
sudo_logsrvd. - Monitor
/etc/sudoersandsudoers.dfor changes outside configuration management. - Keep sudo patched to a release that fixes all KEV-listed sudo CVEs.
Standards and references
- Sudo project: sudoers(5) manual
- Sudo project: sudo_logsrvd(8) manual
- Sudo project: Security advisories
- Sudo project: Buffer overflow in command line unescaping (CVE-2021-3156)
- Sudo project: Local privilege escalation via chroot option (CVE-2025-32463)
- MITRE ATT&CK: Abuse Elevation Control Mechanism: Sudo and Sudo Caching
- CISA: Known Exploited Vulnerabilities Catalog
- GTFOBins: Unix binaries that can be used to bypass local security restrictions
Related NHI Mgmt Group resources: Linux Privilege Escalation Techniques Guide · Linux PAM Guide · Service Account Security Guide · Linux Logging and auditd for Defenders Guide