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
- Multi-account servers that must force strong passwords for every user and block weak passwords such as 123456 and password
- SSH under continuous brute force; auto-lock the account or source IP after failures exceed a threshold
- MLPS / security-baseline assessments that require periodic password rotation and no reuse of historical passwords
- New-user workflows that must apply a unified password policy automatically, without per-user reminders
Prerequisites
- Linux using the PAM authentication framework (default on CentOS/RHEL/Rocky, Debian/Ubuntu)
- root or sudo; keep a second test session logged in so a bad config cannot lock you out
- Confirm module versions: CentOS 8+/RHEL 8+ and Debian 11+ use pam_faillock; CentOS 7 uses pam_tally2. Check with `ls /usr/lib64/security/ | grep -E "faillock|tally2"`
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:
- pam_pwquality: validates complexity at password-set time—minimum length, character classes, username inclusion, dictionary hits
- chage / login.defs: password max age, minimum change interval, expiry warning days
- pam_faillock (or pam_tally2): auth-class module; counts consecutive failures and temporarily locks the account at threshold, making online brute force infeasible in time
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.