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 PAM
Last reviewed
PAM is the gatekeeper every login passes through. Tune its modules so weak passwords, password reuse and brute-force never reach a shell, and lock accounts on repeated failure.
The threat: one permissive stack and every login is open
PAM (Pluggable Authentication Modules) is the shared authentication layer that login, sshd, sudo, su, cron and the display manager all call. Every credential check flows through the stacks in /etc/pam.d/, which makes it the place where authentication policy is enforced or silently absent. The threats it must blunt are timeless: weak passwords that fall to a dictionary in seconds, online brute-force that simply tries until it wins, password reuse where a user rotates back to a known-leaked secret, and fast hashing that lets a stolen /etc/shadow be cracked offline at scale. A default PAM stack opposes almost none of this: no complexity floor, no lockout, no history.
Why harden it
PAM is the single choke point where you impose policy on all services at once, and the defaults are deliberately permissive. Hardening converts authentication from a yes/no match into a layered gate: the secret must be strong enough (pam_pwquality), it must not repeat a recent one (pam_pwhistory), repeated failures must lock the account (pam_faillock), and the stored hash must be expensive to crack (pam_unix rounds=, yescrypt/SHA-512). Each layer raises attacker cost; together they turn a guessable password into a non-event. It maps to ANSSI R31 (passwords), R67 (PAM authentication) and R68 (password storage).
Order is the whole game
PAM is a stack, evaluated top to bottom, and a module's control decides the flow:
| Control | Effect |
|---|---|
required |
must pass; failure is remembered but the stack continues (no early hint to the attacker) |
requisite |
must pass; failure returns immediately |
sufficient |
success short-circuits the rest of the stack |
optional |
result ignored unless it is the only module |
A strong module placed after a permissive sufficient line never runs. So pam_pwhistory must come before pam_unix and carry use_authtok, otherwise it checks the wrong token. The effective behaviour is the merged result of every line, not any single directive.
What Pavois audits: the effective PAM stacks
Pavois audits the effective PAM stacks under /etc/pam.d/, the files actually consulted at authentication time, including the distro common-* (Debian) / *-auth (RHEL) includes that service files pull in, rather than trusting one snippet. Its rules confirm the right modules are present and enabled (pam_pwquality, pam_faillock, pam_pwhistory, pam_unix) and then check their options: even_deny_root on faillock so the lockout covers root, the audit option so lockouts are logged, use_authtok on pwhistory so the new token is enforced, a non-default rounds= on pam_unix, and that history is not delegated to the deprecated remember= on pam_unix. Each control reads the live stack and matches only active, uncommented lines, so a directive behind a # or shadowed by ordering is reported non-compliant.
How do you configure and verify it?
The knobs live in dedicated files, but enabling the module differs by distribution, which is the central trap:
- Debian / Ubuntu: the stack is in
/etc/pam.d/common-authandcommon-password, managed bypam-auth-update;pam_faillockmust be referenced inpreauthandauthfail. Settings go in/etc/security/faillock.confand/etc/security/pwquality.conf. - RHEL / AlmaLinux / Rocky: never edit
system-auth/password-authby hand, they are regenerated byauthselectand your edits are overwritten. Enable cleanly:authselect enable-feature with-faillock && authselect apply-changes.
Verify the live state: faillock --user alice shows the failure count, faillock --user alice --reset unlocks. A typical policy: deny=3, unlock_time=600 (faillock); minlen=12, minclass=3 (pwquality); remember=5 (pwhistory, history in /etc/security/opasswd).
Modern password doctrine (NIST 800-63B, ANSSI 2024)
NIST SP 800-63B and ANSSI now advise against forced periodic rotation and imposed complexity, which push users toward weak, predictable passwords. The priority is length, a banlist of known-bad passwords, and rate-limiting (pam_faillock). Forced rotation is reserved for privileged accounts or a compliance requirement: some CIS profiles still mandate complexity credits, which you treat as an audit requirement, not a security ideal.
Pitfalls
- A syntax error in a PAM file blocks all authentication at once, sudo and SSH included. Back up the file, keep a root session open, and test login in a second session before committing. Recovery is via rescue mode.
pam_tally2is gone: removed from Linux-PAM since 1.5.0 (2020); apam_tally2.soline now causes a PAM load error. Usepam_faillock.even_deny_rootcan lock root itself; if you use it, addroot_unlock_time=60.- Lock counters are not persistent by default (
/var/run/faillock, cleared on reboot); adddir=/var/log/faillockto keep them.
FAQ
Why does Pavois read all the common-* / *-auth includes? Because the effective stack is the merge of the service file and its includes; a module enabled only in an include is live, and Pavois audits what PAM actually evaluates.
Should I force password rotation? Not for ordinary users (NIST/ANSSI advise against it). Prioritize length, a banlist and lockout; reserve rotation for privileged accounts or a compliance mandate.
My pam_tally2 line breaks login. Why? It was removed in Linux-PAM 1.5.0. Replace it with pam_faillock.