Handbook sections▾
Foundations
Understanding the threats to a Linux hostWhy we hardenThe defense principles behind every ruleEffective configuration: the truth no file holdsThe standards Pavois maps toSOCLE control ID modelEvidence & exportsHow the A-E grade is computedWhat a PASS proves: the qualified verdictTrust model for the evidence bundleIs SOCLE just your own norm? (governance & the circularity question)What Pavois covers, and what it doesn'tDomains
Hardening SSHHardening PAMMandatory Access Control (SELinux / AppArmor)Hardening the host firewallHardening sudoFile Permissions & OwnershipMount & filesystem hardeningHardening kernel modulesHardening the kernel & network with sysctlAudit logging with auditdHardening logging with journald and rsyslogHardening systemd servicesPackage hygieneHardening the bootloader (GRUB)Synchronizing timeLogin banners and MOTDControlling cron and at accessHardening the GNOME desktop (dconf)Tooling
Undo a hardening run: restore pointsOperating Pavois: privileges, air-gap, timing, exceptionsRun Pavois in CI (GitHub Actions)Feature status: delivered, partial, roadmapGovernance: licence, versioning, provenance, securityHardening sudo
Last reviewed
sudo is the controlled bridge from a user account to root. Harden the policy so one stolen session can't quietly become unaccountable, unauthenticated root, and log every escalation.
The threat: a lax sudoers policy is passwordless root
sudo is how an ordinary account becomes root for a moment, which makes its policy a permanent target. The danger is rarely sudo itself: it is a lax sudoers policy. A blanket NOPASSWD: ALL rule turns any hijacked or simply unlocked session into instant root, with no re-authentication and no challenge to prove who is really at the keyboard. Even when a password is required, the way escalation happens can leak privilege: a command run through sudo can be coerced into spawning a sub-shell (vi, less, find -exec, the GTFOBins catalogue), injecting keystrokes into the controlling terminal via the TIOCSTI ioctl, inheriting hostile environment variables like LD_PRELOAD or PATH, or picking up a malicious binary from the current directory. An escalation that is not logged is an attacker's dream: root actions with no authentication trail.
Why harden sudo
sudo sits exactly on the boundary between a user and the whole machine. Get the policy wrong and you have handed out passwordless root, the single worst privilege-escalation outcome. Hardening keeps that bridge narrow, authenticated and observable: every crossing is challenged, attributable to a person, recorded, and stripped of the side-channels (sub-shells, terminal injection, environment carry-over) that let a foothold widen into full control. Two axes matter: reduce what is granted, and make everything traceable.
What Pavois audits: the effective, merged policy
Pavois audits the effective sudoers policy, not a single file. Because sudo merges /etc/sudoers with every drop-in under /etc/sudoers.d/, Pavois scans the whole tree, so an exemption hidden in any drop-in is caught exactly as sudo would apply it. Across its Sudo rules it checks: no uncommented !authenticate and no blanket NOPASSWD (sudo-require-authentication, sudo-remove-no-authenticate), Defaults use_pty and a real-tty requirement (sudo-use-pty), noexec so escalated commands cannot launch sub-shells (sudo-noexec), env_reset (sudo-env-reset), ignore_dot (sudo-ignore-dot), and a restrictive sudo umask. Each Defaults check greps the active sudoers tree for the uncommented directive, reflecting the merged, applied policy rather than one config file.
How do you verify and operate sudo?
- Always edit with
visudo(visudo -cf /etc/sudoers.d/10-hardeningfor a drop-in): it validates syntax before writing, so a typo cannot lock you out of sudo. Keep a second root session open while you change the policy. - See the effective rules for a user with
sudo -lU alice: it lists exactly what the merged policy grants them;visudo -cvalidates the whole tree. - Baseline the Defaults:
grep -hE '^Defaults' /etc/sudoers /etc/sudoers.d/*. Note thatuse_ptyis the upstream default only since sudo 1.9.14; Debian and Ubuntu backport it, but an older RHEL/Rocky may not, so checksudo --version.
Limit the privileges (ANSSI BP-028 R38 to R44)
The Defaults reduce abuse, but the rules are what grant power. Grant only the strict minimum:
| Practice | Rule of thumb |
|---|---|
| Grant via a dedicated group (R38) | %sudo (Debian) or %wheel (RHEL), never a user directly |
| Target a non-root account (R40) | alice ALL=(www-data) ... beats a reflexive (root) |
| Allowlist, never negate (R42) | ALL, !/bin/su is bypassable via alternate paths or copies |
| Pin the arguments (R43) | systemctl restart nginx, not bare systemctl (which allows edit then a root shell). A * in a path is almost always a hole: cat /var/log/messages* can read /etc/shadow via ../ |
| Block shell escapes (R41) | NOEXEC: on commands that can spawn a shell (less, vi, find, awk) |
| Edit files without a root shell (R44) | sudoedit (sudo -e) edits as the user then reinstalls; the editor never runs as root |
Log every escalation
By default sudo only logs to syslog. Make each use replayable: Defaults logfile, Defaults log_input, log_output, Defaults iolog_dir. sudoreplay -l lists sessions by their six-character TSID, and sudoreplay <TSID> replays the keystrokes and output, decisive for incident response. Caveat: log_input captures everything typed, including passwords, so add Defaults !log_passwords and lock /var/log/sudo-io to 0700 root:root. Forward the logs off-host (an attacker with root erases local logs) and watch sudoers changes with auditd (key scope).
Pitfalls
- Do not use
requiretty. It is often presented as hardening, but it breaks every non-interactive sudo (cron, deploy scripts,runuser -u app -- sudo ...). The modern, safe equivalent isuse_pty, already the default on recent distributions. - A
*in a command path is a trap: path traversal can reach/etc/shadow. Pin exact arguments instead. - Negation rules (
!command) are bypassable: allowlist, never denylist. log_inputrecords secrets unless!log_passwordsis set and the I/O logs are locked down.
Patch sudo too
No Defaults protects against a flaw in sudo itself. Two reminders: CVE-2021-3156 (Baron Samedit) let any local user become root without being in sudoers at all (fixed in 1.9.5p2); CVE-2023-22809 bypassed sudoedit via an injected -- in $EDITOR to edit out-of-scope files like /etc/shadow (fixed in 1.9.12p2). Reducing privileges and applying patches are complementary, never one without the other.
FAQ
Why does Pavois scan all of /etc/sudoers.d/? Because sudo applies the merged policy; a NOPASSWD exemption hidden in any drop-in is live, and Pavois audits what sudo actually enforces.
requiretty or use_pty? use_pty. requiretty breaks non-interactive sudo; use_pty gives the same terminal-injection protection without that cost.
How do I let someone edit a config without a root shell? sudoedit (R44): it edits the file as the user and reinstalls it, so the editor never runs as root.