PRACTITIONER GUIDE | IDENTITY SECURITY
Practitioner Guide12 min read

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

pam_faillock
Replacement for pam_tally2 -- provides account lockout after configurable failed login attempts including for root
SHA-512
Minimum password hashing algorithm for pam_unix -- MD5 and SHA-1 are cryptographically broken and must not be used
minlen=14
CIS Benchmark minimum password length recommendation for pam_pwquality -- 8-character minimums are insufficient
pam_tty_audit
Kernel-level TTY keystroke logging for privileged sessions -- audit trail independent of shell history manipulation

SponsoredRetool

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.

Start building for free today

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.

Free daily briefing

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

  1. Linux-PAM System Administrators Guide
  2. Red Hat: Using pam_faillock to lock out user accounts
  3. CIS Red Hat Enterprise Linux 9 Benchmark

Free resources

25
Free download

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.

No spam. Unsubscribe anytime.

Free download

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.

No spam. Unsubscribe anytime.

Free newsletter

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.

Eric Bang
Author

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.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.