← News
/PLAYBOOK · 2026-09-05

Linux Password Policy Hardening: PAM Complexity and Login Lockout

Weak passwords are the primary server intrusion vector. Full Linux password policy hardening: PAM pwquality for complexity, login.defs and pwhistory for aging and history, faillock for failed-login lockout. Copy-ready configs, per-check verification commands, and accidental-lockout troubleshooting.

Weak passwords remain the top server intrusion vector. Most weak-password issues exist because the system never enforced a password policy. This article covers full Linux password policy hardening: force complexity with PAM pwquality, lock failed logins with pam_tally2/pam_faillock, and unify aging plus history limits. All configs are copy-ready.

Applicable Scenarios

Prerequisites

How It Works

PAM (Pluggable Authentication Modules) is Linux authentication middleware. login, sshd, sudo and others verify identity through it. Password policy is three cooperating parts:

The three cover password strength, password rotation, and trial cost. All three are required.

Procedure

Step 1: Configure password complexity

# RHEL/CentOS family
yum install -y libpwquality

# Edit /etc/security/pwquality.conf, write the following
minlen = 12            # minimum length 12
minclass = 3           # at least 3 of uppercase/lowercase/digit/symbol
maxrepeat = 2          # same character may repeat at most 2 times in a row
maxsequence = 3        # ban sequences longer than 3, e.g. abc, 123
dictpath = /usr/share/dict/words   # dictionary check (enabled if present)
usercheck = 1          # reject passwords that contain the username
# Edit /etc/pam.d/system-auth (Debian/Ubuntu: /etc/pam.d/common-password)
# Append as the first line of the password stack:
password    requisite    pam_pwquality.so try_first_pass local_users_only retry=3

retry=3 lets the user correct the password interactively up to 3 times, avoiding an overly rigid experience.

Step 2: Configure aging and password history

# /etc/login.defs default values (apply to new users)
PASS_MAX_DAYS   90
PASS_MIN_DAYS   7
PASS_WARN_AGE   14

# Apply in bulk to existing users
for user in $(awk -F: '$3>=1000 && $1!="nobody" {print $1}' /etc/passwd); do
    chage -M 90 -m 7 -W 14 "$user"
done

# Remember last 5 passwords; no reuse. Add to the password stack in /etc/pam.d/system-auth
password    required    pam_pwhistory.so use_authtok remember=5 enforce_for_root

PASS_MAX_DAYS=90 is a common MLPS baseline requirement. remember=5 stops users from bouncing between old and new passwords to bypass aging.

Step 3: Failed-login lockout

RHEL 8+/Rocky/Debian 11+ use faillock. Create `/etc/security/faillock.conf`:

deny = 5                # lock after 5 consecutive failures
fail_interval = 600     # failure count window 10 minutes
unlock_time = 900       # auto-unlock after 15 minutes
root_unlock_time = 900  # root also controlled; prevents root brute force

CentOS 7 uses pam_tally2. Add at the start and end of the auth stack in /etc/pam.d/sshd and /etc/pam.d/system-auth:

auth        required    pam_tally2.so onerr=fail deny=5 unlock_time=900
account     required    pam_tally2.so

Note: before enabling root lockout, confirm SSH key login is configured as a fallback. Otherwise the policy can lock you out of the management session.

Verification

# 1. Complexity: setting a weak password must be rejected
passwd testuser    # enter 12345678, expect "too simplistic/systematic"

# 2. Aging: confirm effective parameters
chage -l testuser

# 3. Failed-login lockout: enter the wrong password 5 times; the 6th attempt must be rejected even with the correct password
faillock --user testuser           # view failure count (tally2: pam_tally2 -u testuser)
faillock --user testuser --reset   # manual unlock, restore access

When all three checks match expectations, the policy is fully in effect. Fold the verification commands into a security-baseline inspection script so later image or admin-script changes cannot silently overwrite the policy.

FAQ

FAQ 1: All users fail login (including with the correct password), message Account locked?

Most likely brute-force traffic already locked the account, or faillock was written in the wrong file order and counting is wrong. Unlock as root with `faillock --user username --reset`. If root itself is locked, enter single-user or rescue mode and run `faillock --user root --reset`. Prevention: keep SSH key authentication on the management channel.

FAQ 2: pwquality rejects a password for containing a dictionary word, but the business must use that word?

That is dictpath dictionary checking. If it is a false positive, temporarily exempt per user: `echo "username" >> /etc/security/pwquality.conf-override` and add `enforcing=0` on the pwquality.so line. Prefer a longer passphrase over disabling dictionary checks.

Summary

Password policy hardening is the foundation of a Linux server security baseline: pwquality owns complexity, login.defs and pwhistory own lifecycle, faillock raises brute-force cost. Together they cut weak-password risk with no extra hardware. After rollout, pair with SSH key authentication and periodic account audit for a durable account-security line.