How to Set Up Password Policies on Linux Systems
Learn step-by-step how to set up strong password policies on Linux systems, including PAM, /etc/login.defs, and password aging. Ideal for Linux and DevOps beginners who want to secure user accounts quickly.
Short introduction
Setting and enforcing good password policies on Linux helps protect systems from casual and targeted attacks. This guide walks you through the practical steps to create, enforce, and test password policies using standard Linux tools (PAM, pwquality, chage, and account-locking utilities).
Core concepts and quick commands
Before changing files, understand the building blocks of password policy:
- Complexity/quality (length, character types, repeats) — enforced by libpwquality/pam_pwquality.
- History and minimum change (prevent reuse) — enforced by pam_unix or pam_pwhistory.
- Aging and expiration (force periodic changes) — managed via chage and /etc/login.defs.
- Lockout on failed attempts — handled by faillock or pam_tally2 (older systems).
- Password hashing — modern distros use SHA-512 (configured in /etc/login.defs or /etc/libuser.conf).
Useful commands (quick reference):
| Command | Purpose |
|---|---|
| passwd user | Change a user’s password interactively |
| chage -l user | Show password aging for a user |
| chage -M 90 user | Set max days before expiry to 90 for user |
| grep -E 'pam_pwquality | pam_unix |
| faillock --user user | Show failed attempts for user (RHEL/CentOS) |
| pam_tally2 --user user | Show failed attempts (older systems) |
| authselect/pam-auth-update | Tools for applying PAM profiles on some distros |
Terminal examples:
# Check PAM password-related lines
grep -E 'pam_pwquality|pam_unix|pam_pwhistory|pam_tally2|faillock' /etc/pam.d/*
# Show aging for a user
chage -l alice
# Lock account manually (example)
passwd -l alice
Configure pwquality (libpwquality) and PAM
Most distributions use pam_pwquality (libpwquality) to enforce complexity. Edit /etc/security/pwquality.conf to define minimum length and composition.
Example /etc/security/pwquality.conf settings:
# /etc/security/pwquality.conf
minlen = 12
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
difok = 4 # number of characters in new password not present in old
maxrepeat = 3
maxclassrepeat = 2
enforce_for_root = 1
Then make sure PAM calls pam_pwquality.so. On Debian/Ubuntu, edit /etc/pam.d/common-password; on RHEL/CentOS, edit /etc/pam.d/system-auth (or use authselect tool where applicable). Example PAM line:
# Debian/Ubuntu example in /etc/pam.d/common-password
password requisite pam_pwquality.so retry=3
password [success=1 default=ignore] pam_unix.so obscure sha512 shadow remember=5
Or RHEL/CentOS style (system-auth):
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password sufficient pam_unix.so sha512 shadow remember=5
Commands to verify:
# Search for pam_pwquality usage
grep -n 'pam_pwquality' /etc/pam.d/*
# Confirm pwquality config exists
cat /etc/security/pwquality.conf
Notes:
- difok enforces that the new password must differ by at least N characters from the old password.
- remember=5 instructs pam_unix to remember the last 5 passwords (may require pam_unix that supports the remember option or use pam_pwhistory on some distros).
- Always test on a non-critical account before rolling out globally.
Set password aging and expiration
Password aging forces users to change passwords periodically and prevents indefinite reuse.
Edit /etc/login.defs for system-wide defaults (Debian/RHEL provide slightly different knobs). Important settings:
# /etc/login.defs examples
PASS_MAX_DAYS 90 # maximum days a password is valid
PASS_MIN_DAYS 1 # minimum days between password changes
PASS_WARN_AGE 14 # days warning before password expires
You can set per-user aging with chage. Examples:
# Set max days to 90, min days to 1, and warn 14 days for user bob
sudo chage -M 90 -m 1 -W 14 bob
# Show bob's aging settings
sudo chage -l bob
To expire a user's password immediately (force change on next login):
sudo chage -d 0 alice
Automated approach example: set defaults for all users created henceforth by adjusting /etc/default/useradd (useradd -D):
# Set default max days to 90 for new users
sudo useradd -D -f 35 # example: configure defaults; check your distro's flags
(Behavior of useradd defaults varies across distributions—consult man useradd.)
Enable account lockout and protect against brute-force
To prevent unlimited password guessing, configure account lockout. Modern RHEL/CentOS use faillock; other distributions might use pam_tally2 or pam_faillock.
Example: using faillock in PAM (RHEL/CentOS):
Add to /etc/pam.d/password-auth and /etc/pam.d/system-auth:
# account phase
account required pam_faillock.so
# auth phase (before pam_unix)
auth required pam_faillock.so preauth silent deny=5 unlock_time=900
auth [default=die] pam_faillock.so authfail deny=5 unlock_time=900
Check failed attempts and unlock:
# Show failed attempts for a user
sudo faillock --user alice
# Unlock user manually
sudo faillock --user alice --reset
Older systems (or some Debian systems) may use pam_tally2:
# Show tally
sudo pam_tally2 --user alice
# Reset tally
sudo pam_tally2 --user alice --reset
Log review example:
# See lockout-related messages in auth logs
sudo grep -i 'failed\|lock' /var/log/auth.log | tail -n 50
Be cautious: overly aggressive lockout rules can lock out administrators. Consider exceptions for emergency access or allow unlock_time with email alerts.
Common Pitfalls
- Mis-editing PAM files: a bad PAM configuration can lock out all logins. Always test in a second root shell or use an SSH session with a fallback account before applying changes.
- Inconsistent modules across distros: names/parameters differ (pam_pwquality vs pam_cracklib, pam_pwhistory vs remember). Verify your distribution’s PAM module documentation rather than copy-pasting blindly.
- Forgetting to test password changes: policies may block acceptable passwords or break scripts that set passwords non-interactively (CI pipelines, cloud-init). Test interactive and programmatic password changes.
Quick diagnostic snippet:
# Basic checks after changes:
grep -nE 'pam_pwquality|pam_unix|pam_pwhistory|pam_faillock|pam_tally2' /etc/pam.d/*
sudo tail -n 200 /var/log/auth.log # or /var/log/secure on RHEL
Next Steps
- Audit current accounts and set per-user aging: use chage -l for each non-system account.
- Deploy and test policy on a pilot group: pick a few users, apply policy, and collect feedback before rolling out.
- Automate policy enforcement with configuration management: push consistent /etc/security/pwquality.conf and PAM snippets using Ansible/Chef/Puppet.
Example automation check:
# Example: verify all servers have pwquality configured consistently
ssh admin@server1 'grep -n "minlen" /etc/security/pwquality.conf || echo "missing"'
This guide gives you the practical steps to set up reasonable password policies on Linux while avoiding common traps. Start with conservative settings (reasonable length and aging), test thoroughly, and iterate based on user experience and your organization’s risk tolerance.
👉 Explore more IT books and guides at dargslan.com.