sudo, su, and PAM
Three pieces sit behind every privileged action on a Debian or Ubuntu server. su switches your identity to another user — usually root — by asking for that user's password and spawning a new shell. sudo runs a single command as another user according to a policy file, prompts for your own password, and writes a log line for each invocation. Underneath both, and underneath login, sshd, and passwd as well, sits PAM — the Pluggable Authentication Modules stack — which is the code that actually decides whether a password, a token, or a fingerprint is accepted.
The operational consequence is that "how does someone become root here" is answered in two separate places: the sudoers policy decides who may do what, and the PAM stack decides how they prove it. Get the first wrong and an ordinary account quietly holds full root; get the second wrong and a brute-force attempt against sshd runs forever with no lockout. Both are edited as plain text, and both can lock you out of your own machine in one bad save.
su versus sudo
su - opens a full login shell as the target user, reading that user's environment, profile, and login scripts. It needs the target's password, which on a multi-admin box means everyone who can become root shares one secret. There is no record of which human typed it, and rotating it means telling everyone the new value. That model is why su to a shared root account lost the server world: it has no per-person accountability.
sudo inverts the trust. Each administrator authenticates with their own password against a policy in /etc/sudoers, runs the command, and leaves an audit line naming them, the command, and the working directory. Revoking access is removing one user from a group or file — no shared secret to rotate. sudo -i still gives you a root login shell when you genuinely need one, but every step that got you there is attributable to a named account.
The sudoers Policy
The policy lives in /etc/sudoers and in drop-in files under /etc/sudoers.d/. Never open either with a plain editor. visudo locks the file, runs a syntax check on save, and refuses to install a file that would not parse — which is the only thing standing between you and a sudoers typo that disables sudo for everyone. Validate an existing file or a drop-in with visudo -cf /etc/sudoers.d/deploy before trusting it.
A rule maps a user or group, on a set of hosts, to the commands they may run and the identities they may run them as. The grammar reads left to right: who, then (runas), then the command list. Debian and Ubuntu grant admin rights through membership in the sudo group; Red Hat and Fedora use wheel. Defaults lines tune behavior globally — password timeout, the environment that survives, whether I/O is logged.
# /etc/sudoers.d/ops — installed via visudo -cf, mode 0440 # Debian/Ubuntu admin group; RHEL/Fedora would use %wheel %sudo ALL=(ALL:ALL) ALL # Scope a deploy account to exactly one command, no password Cmnd_Alias DEPLOY = /usr/bin/systemctl restart app.service deploy ALL=(root) NOPASSWD: DEPLOY # Cache the prompt 15 min; keep PATH and TERM only Defaults timestamp_timeout=15 Defaults env_reset, secure_path="/usr/sbin:/usr/bin:/sbin:/bin"
Custom rules must sit below the @includedir /etc/sudoers.d line in the main file, because sudo reads rules in order and the last match wins — a drop-in loaded after your hand-written grant can silently override it. NOPASSWD: ALL for a human account is the rule you will regret: it turns a stolen shell into instant unprompted root with no second factor.
PAM Stack Architecture
PAM decouples "am I authenticated" from the program asking. Instead of sshd and login and sudo each implementing password checks, they call libpam, which consults a per-service file in /etc/pam.d/. Each file is an ordered stack of modules grouped into four management types: auth (prove identity), account (is the account allowed and unexpired), password (rules for changing the secret), and session (set up and tear down the session — mounts, limits, logging).
Each line carries a control flag that decides how its result feeds the stack. required must pass, but the stack keeps running so a failure is not telegraphed by an early exit. requisite fails the whole stack immediately. sufficient short-circuits to success if it passes and nothing required has already failed. optional rarely affects the outcome. Order matters: a sufficient module placed before a required one can grant access before the required check ever runs.
# /etc/pam.d/common-auth (Debian/Ubuntu) — simplified # lock the account after repeated failures, BEFORE pam_unix auth required pam_faillock.so preauth auth [success=1 default=ignore] pam_unix.so nullok auth [default=die] pam_faillock.so authfail auth required pam_faillock.so authsucc
Debian and Ubuntu factor the common logic into common-auth, common-account, common-password, and common-session, which the per-service files pull in with @include. Editing the common file changes every service at once — convenient and dangerous in equal measure. Red Hat instead generates its stack with authselect, so hand-editing /etc/pam.d there fights the tool and gets reverted.
Common PAM Modules
The default authenticator is pam_unix.so, which checks the hashed password in /etc/shadow. Around it you stack policy modules. pam_pwquality.so enforces length and complexity at password-change time; pam_pwhistory.so blocks reuse of recent passwords. pam_faillock.so counts failed attempts and locks the account for a window — the single most important module for surviving credential-stuffing, and absent by default on a stock install.
Session-type modules do setup work: pam_limits.so applies the ulimits from /etc/security/limits.conf, pam_env.so loads environment variables, and pam_systemd.so registers the login with systemd-logind so the session lands in its own cgroup slice. Multi-factor authentication slots in as an extra auth line — pam_google_authenticator.so for TOTP, for instance — placed so that both the password module and the token module are required, which makes them additive rather than alternatives.
Auditing and Logging
Every sudo invocation, successful or denied, is logged. On Debian and Ubuntu it lands in /var/log/auth.log and the journal; the precise filter is journalctl _COMM=sudo, which shows who ran what, when, and from which directory. That per-user trail is the practical payoff of choosing sudo over a shared root password: an incident review can reconstruct exactly which named account did which privileged thing.
For high-value accounts you can go further with full session capture. Adding Defaults log_output (or a targeted log_output on a specific rule) records the complete terminal I/O of the command, replayable with sudoreplay. It is verbose and stores keystrokes, so scope it to the accounts where the forensic value justifies the storage and the privacy cost — not as a blanket default.
- Editing
/etc/sudoerswithnanoorvimdirectly and saving a syntax error —sudothen refuses to run for everyone, and if you have no open root shell the only fix is single-user mode or a rescue boot. - Granting
NOPASSWD: ALLto a human account for convenience — a stolen SSH key or hijacked shell becomes unprompted root with no second authentication step in the way. - Adding a user to the
sudogroup (orwheelon RHEL) without realizing the default rule grants that group full root, not a limited subset — the group name reads narrower than it acts. - Writing a broad
ALL=(ALL) ALLrule when the user only ever needs to restart one service — the over-grant sits unused until it is the thing that gets abused. - Leaving
pam_faillockout of the stack, so failed password attempts never lock the account and a brute-force againstsshdruns unbounded. - Placing a hand-written sudoers rule above the
@includedirline — a drop-in in/etc/sudoers.d/loads later, wins on last-match, and silently overrides the grant you thought was authoritative.
- Edit sudoers only through
visudo, and validate drop-ins withvisudo -cf /etc/sudoers.d/<file>before they take effect — the syntax check is what keeps a typo from disablingsudo. - Prefer per-user
sudoover a shared root password on every multi-admin host, so revocation is a one-line change and every privileged action names a human. - Put custom rules in
/etc/sudoers.d/as separate files, never in the main/etc/sudoers— drop-ins are easier to package, review, and remove. - Scope commands tightly with
Cmnd_Aliasand an explicit(runas), granting the exact binaries an account needs instead ofALL. - Enable
pam_faillockfor lockout andpam_pwqualityfor complexity in thecommon-authandcommon-passwordstacks — neither ships active on a default install. - Keep hand-written sudoers rules below the
@includedirline, or convert them into drop-ins, so last-match ordering works for you rather than against you. - Review
journalctl _COMM=sudo(or/var/log/auth.log) as part of routine auditing, and enablelog_outputon the highest-value accounts where replayable sessions earn their storage cost.
runas: elevation by consent prompt and group membership, with no per-command policy file or text-based audit trail like sudoersmacOS — the same sudo binary and /etc/sudoers, but identities and auth come from the dscl/OpenDirectory database rather than /etc/passwd and PAMBSD — OpenBSD's doas: a deliberately minimal sudo alternative whose /etc/doas.conf trades sudoers' expressive policy for a tiny, auditable codebaseKnowledge Check
Why did sudo displace su to a shared root account on multi-admin servers?
- It authenticates each admin with their own password and logs every command per user, giving an audit trail and one-line revocation with no shared secret to rotate
- It runs commands noticeably faster because it skips the work of loading the target user's full login shell, profile, and environment on each and every single invocation
- It encrypts the root password on disk with a per-host key, whereas
suleaves that very same password sitting in plain text on the system - It is the only one of the two commands that can run a single command as some user other than root, which
sucannot do at all
What does running visudo give you that editing /etc/sudoers in a normal editor does not?
- It locks the file and runs a syntax check on save, refusing to install a file that would not parse and lock everyone out of
sudo - It automatically grants the editing user passwordless root access for the full duration of the edit session itself
- It encrypts the resulting sudoers file on disk so that it can never be read by any non-root user anywhere on the system afterward
- It merges all the files from
/etc/sudoers.d/directly into the main file on save, so their ordering no longer matters at all
In a PAM stack, how does a sufficient control flag differ from required?
sufficientshort-circuits the stack to success if it passes (and nothing required failed earlier), whilerequiredmust pass but lets the stack keep runningsufficientmust always pass for the login to proceed at all, whilerequiredonly contributes anything when an earlier module in the same stack has already succeeded- Both flags behave completely identically in practice; the two names are merely aliases that were kept around purely for backward compatibility
sufficientfails the entire stack immediately on failure, whereasrequireddefers the failure to the end
What is the role of pam_faillock in an authentication stack?
- It counts failed authentication attempts and locks the account for a window, so brute-force attempts against services like
sshdare throttled instead of unbounded - It enforces the minimum password length and complexity rules every single time that a user on the host attempts to change their own account password
- It applies the per-user resource ulimits defined in
/etc/security/limits.confat the moment each new session starts up, capping memory use and the open-file and process counts - It rotates the root password automatically after a configured number of successful logins have accumulated across the host over time
A hand-written grant in /etc/sudoers is being silently overridden. What ordering rule explains it?
sudoapplies last-match, and a drop-in under/etc/sudoers.d/loaded by@includedirafter your rule wins over itsudoapplies first-match, so any rule placed earlier in the file always wins and takes precedence over later ones- Rules in
/etc/sudoers.d/are ignored entirely at runtime unless each file is explicitly imported by name from the main file - Group rules always override user rules regardless of where either of them happens to appear in the sudoers file
You got correct