Linux PAM Authentication Hardening: A Practitioner Guide to pam_faillock, pam_pwquality, and Secure PAM Stacks

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
Every login, sudo elevation, and SSH session on a Linux system passes through the PAM stack. A default PAM configuration on most distributions provides minimal protections: accounts can often be brute-forced without lockout, passwords may be hashed with weak algorithms, and core dumps can expose credential material from memory. Hardening PAM requires understanding the module stack for each service, the order modules are evaluated, and which control flags (required, requisite, sufficient) apply to each step. This guide covers the PAM module configurations that correspond to specific CIS Benchmark and STIG controls and explains the security reasoning behind each setting.
PAM Stack Structure: /etc/pam.d/ and Module Types
Every service that uses PAM has a configuration file in /etc/pam.d/. The file defines a stack of module rules, each specifying a module type (auth, account, password, session), a control flag, and the module to invoke with its options. Understanding this structure is required before modifying any PAM configuration.
Module types and their roles
auth modules verify credentials (password check, MFA). account modules check whether the account is allowed to log in (locked, expired, time-of-day restrictions). password modules manage credential changes (password update logic). session modules set up and tear down the login session (resource limits, audit logging, environment setup).
Control flags: required vs requisite vs sufficient
required: the module must succeed, but even on failure the remaining stack continues (to prevent revealing which step failed). requisite: failure immediately stops the stack and returns failure -- use this for first-factor authentication to prevent continuing to second factor with a failed first factor. sufficient: success stops the remaining stack and returns success -- use carefully, as it can skip security modules. optional: the module result is advisory and does not affect the stack outcome.
Check current PAM configuration
Review the active PAM configuration for key services: cat /etc/pam.d/sshd, /etc/pam.d/sudo, /etc/pam.d/login, /etc/pam.d/passwd. On RHEL/CentOS, also review /etc/pam.d/system-auth and /etc/pam.d/password-auth, which are included by service-specific files. On Ubuntu/Debian, review /etc/pam.d/common-auth, common-account, common-password, common-session.
pam_faillock: Account Lockout and Brute-Force Protection
pam_faillock implements account lockout after a configurable number of failed authentication attempts. It replaces the deprecated pam_tally2 module and is the standard for modern RHEL and Ubuntu systems. Without pam_faillock, local and remote brute-force attacks on PAM-authenticated services are unlimited.
Configure /etc/security/faillock.conf
Create or edit /etc/security/faillock.conf: deny = 5 (lock after 5 failures), unlock_time = 900 (unlock after 15 minutes), fail_interval = 900 (count failures within a 15-minute window), even_deny_root (apply lockout to root as well), audit (log failures to audit subsystem). These values match CIS Benchmark Level 1 recommendations.
Add pam_faillock to the auth stack
In /etc/pam.d/system-auth (RHEL) or /etc/pam.d/common-auth (Debian/Ubuntu), add before pam_unix: 'auth required pam_faillock.so preauth' and after pam_unix: 'auth [default=die] pam_faillock.so authfail'. Also add to the account stack: 'account required pam_faillock.so'. The preauth call checks for an existing lockout before attempting authentication; authfail records each failure.
Monitor and manually unlock accounts
Check an account's lockout status: faillock --user username. Unlock a locked account: faillock --user username --reset. Set up alerting on lockout events: pam_faillock writes to /var/run/faillock/ and optionally to the audit log when 'audit' is set. Alert on repeated lockouts of the same account, which may indicate an ongoing brute-force attack.
Verify lockout with a test account
After configuring pam_faillock, verify it works: create a test account, attempt more than deny= failed logins, confirm the account is locked (faillock --user testuser shows a non-zero failure count and a locked timestamp), confirm a legitimate login fails with the lockout error, then unlock and confirm login succeeds.
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
pam_pwquality: Password Complexity Requirements
pam_pwquality validates proposed passwords against quality rules before pam_unix stores the new password hash. Without it, users can set single-character passwords that are trivially brute-forced if the hash is leaked.
Configure /etc/security/pwquality.conf
Set: minlen = 14 (14-character minimum per CIS L1), dcredit = -1 (require at least 1 digit), ucredit = -1 (require at least 1 uppercase), lcredit = -1 (require at least 1 lowercase), ocredit = -1 (require at least 1 special character), maxrepeat = 3 (no more than 3 consecutive identical characters), maxclassrepeat = 4 (no more than 4 consecutive characters from the same class), retry = 3 (give users 3 attempts to enter a compliant password).
Add pam_pwquality to the password stack
In /etc/pam.d/system-auth or common-password, add: 'password requisite pam_pwquality.so try_first_pass local_users_only'. The requisite flag stops the password change immediately on quality failure rather than continuing to pam_unix with a weak password. try_first_pass re-uses the password entered by the previous module if one exists.
Configure SHA-512 hashing in pam_unix
Ensure pam_unix uses SHA-512 for password hashing: in the password stack, the pam_unix line should include 'sha512 shadow remember=5'. The 'sha512' option uses SHA-512 (yescrypt on newer systems); remove any 'md5' or 'sha1' options. The 'remember=5' option prevents password reuse for the last 5 passwords, stored in /etc/security/opasswd.
Remove nullok from auth and account lines
Find all instances of 'nullok' in /etc/pam.d/ files: grep -r nullok /etc/pam.d/. Remove the nullok argument from all pam_unix module invocations in auth and account context. This prevents accounts with blank passwords from authenticating. Any service account with a blank password should have its password field set to '!' (locked) in /etc/shadow and use key-based authentication.
pam_limits and Resource Restriction
pam_limits applies session resource limits from /etc/security/limits.conf and /etc/security/limits.d/ when a session is opened. These limits prevent denial-of-service conditions and reduce the impact of credential exposure from core dumps.
Disable core dumps system-wide
In /etc/security/limits.conf, add: '* hard core 0'. Core dumps can contain sensitive memory content including passwords, private keys, and authentication tokens that were in memory at the time of a crash. Disabling them prevents this data from being written to disk. Also set 'fs.suid_dumpable = 0' in /etc/sysctl.conf for setuid programs.
Restrict nproc for non-privileged users
Add '* hard nproc 65535' or a lower value appropriate for your workload in limits.conf. This limits the number of processes a single user can create, preventing fork bomb attacks. For service accounts with known process requirements (database accounts, web server users), set tighter limits in /etc/security/limits.d/<service>.conf.
Configure pam_limits in the session stack
Ensure pam_limits is present in the session stack for all relevant services: 'session required pam_limits.so'. It should appear in /etc/pam.d/common-session (Debian) or /etc/pam.d/system-auth (RHEL). Without this line, limits.conf settings are not applied to sessions for that service regardless of what limits are configured.
pam_tty_audit and SSH PAM Integration
pam_tty_audit provides kernel-level TTY input auditing for privileged sessions. SSH PAM integration enables MFA for SSH logins while retaining key-based authentication as a required first factor.
Enable pam_tty_audit for privileged users
In /etc/pam.d/system-auth or common-session, add: 'session required pam_tty_audit.so disable=* enable=root,wheel'. This enables TTY keystroke auditing only for root and members of the wheel/sudo group, avoiding excessive audit volume from regular users. Audit records are written to /var/log/audit/audit.log and can be extracted with the ausearch tool.
Configure sshd_config for PAM MFA
In /etc/ssh/sshd_config, set: UsePAM yes, KbdInteractiveAuthentication yes, AuthenticationMethods publickey,keyboard-interactive. This requires users to provide a valid SSH key (first factor) and then complete a PAM keyboard-interactive challenge (second factor). In /etc/pam.d/sshd, add an auth module for your MFA provider (pam_google_authenticator.so, pam_duo.so, or your organization's RADIUS/LDAP MFA module) before pam_unix.
Configure sudo PAM security
In /etc/pam.d/sudo, ensure pam_faillock is included so that failed sudo authentication attempts count toward account lockout. Set 'Defaults timestamp_timeout=5' in /etc/sudoers to require re-authentication after 5 minutes of inactivity rather than the default 15. Set 'Defaults requiretty' to require sudo commands to be run from a TTY, blocking use of sudo in non-interactive contexts such as web shell exploits.
The bottom line
PAM hardening priority order: first, add pam_faillock with even_deny_root to prevent brute-force on all accounts including root; second, configure pam_pwquality with minlen=14 and all character class requirements; third, set SHA-512 hashing in pam_unix and remove nullok from all services; fourth, disable core dumps in limits.conf to prevent credential material in crash files; fifth, add pam_tty_audit for root and privileged group sessions; and sixth, configure SSH with AuthenticationMethods publickey,keyboard-interactive to require both key and MFA. These six changes address the majority of PAM-related CIS Benchmark findings and STIG controls on both RHEL and Debian-family systems.
Frequently asked questions
What is the difference between pam_tally2 and pam_faillock?
pam_tally2 is the older account lockout module used in older distributions. pam_faillock is the replacement that is included in Linux-PAM 1.3+ and is the current standard on RHEL 8+, Ubuntu 20.04+, and other modern distributions. pam_faillock stores failure records in per-user files under /var/run/faillock/ and supports more granular options. If your system has pam_tally2, you are on an older distribution and should plan an upgrade; pam_faillock is the correct module for new configurations.
Should pam_faillock apply to the root account?
The 'even_deny_root' option in pam_faillock causes the root account to be subject to lockout just like regular accounts. Whether to enable it depends on your environment: on systems where root login is permitted (via console or SSH PermitRootLogin), even_deny_root protects against brute-force attacks on root. On systems where root login is disabled and all privilege elevation goes through sudo, the risk of root lockout outweighs the benefit. Most CIS benchmarks recommend enabling even_deny_root when root login is permitted.
What does the nullok option in pam_unix mean and should it be removed?
The 'nullok' option in pam_unix allows authentication to succeed for accounts with no password set (an empty password field in /etc/shadow). Removing nullok prevents passwordless accounts from authenticating via PAM. It should be removed from auth and account management lines in all PAM configurations for interactive services. Service accounts that require no password should be configured with a locked password (! in /etc/shadow) and use key-based authentication instead.
How does pam_pwquality enforce password policy?
pam_pwquality checks proposed passwords against a set of quality rules before accepting them. The rules are configured in /etc/security/pwquality.conf or passed as module arguments. Key options: minlen sets the minimum length (14 is the common CIS recommendation), dcredit=-1 requires at least one digit, ucredit=-1 requires one uppercase character, lcredit=-1 requires one lowercase character, ocredit=-1 requires one special character, retry=3 gives users three attempts before failing. Credit values work such that negative values set a minimum count requirement.
What is pam_tty_audit and when should it be used?
pam_tty_audit is a PAM session module that enables kernel-level TTY input logging for sessions opened under it. When enabled for privileged users (root, sudo), all keystrokes typed in that session are logged to the audit subsystem (auditd). This provides a command audit trail independent of shell history (which can be cleared). It is recommended for any environment where privileged account activity must be audited. Enable it selectively for root and groups with sudo access rather than all users, as the audit volume can be significant.
How should PAM be configured for SSH with MFA?
Enable 'UsePAM yes' and 'ChallengeResponseAuthentication yes' (or 'KbdInteractiveAuthentication yes' on newer OpenSSH) in sshd_config, and set 'AuthenticationMethods publickey,keyboard-interactive' to require both key-based and PAM-based authentication. In /etc/pam.d/sshd, add 'auth required pam_google_authenticator.so' (or your MFA module) after pam_unix. With AuthenticationMethods set to 'publickey,keyboard-interactive', SSH will require a valid key AND a successful PAM second factor, implementing hardware-backed MFA for SSH sessions.
What resource limits should be set in /etc/security/limits.conf?
Critical limits: set 'hard core 0' to prevent core dump files that may contain sensitive memory content including credentials, set 'hard nproc 65535' for non-privileged users to prevent fork bomb exhaustion (or lower for service accounts), and set 'hard nofile 65536' for application users that need many file descriptors. The 'soft' limit is the default; users can raise their soft limit up to the hard limit. Service accounts should have restrictive limits set in their PAM service file rather than globally in limits.conf.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
