Even with SSH keys, Linux passwords still matter. They protect sudo, console logins, and services that authenticate against system accounts, such as mail and FTP. A sensible password policy enforces length and quality, and locks accounts after repeated failures. On Linux, both are handled by PAM modules.
Modern guidance first
Current recommendations (NIST SP 800-63B) differ from older habits:
- Length beats complexity. A 15-character passphrase is stronger, and easier to remember, than
P@ssw0rd1!. - Check against known-bad passwords (dictionary words, breached lists).
- Don’t force periodic changes without reason. Change passwords on suspicion of compromise.
- Use MFA for important access.
Some compliance standards still require expiry and complexity rules. Configure what your environment needs.
Password quality: pam_pwquality
Install it:
sudo apt install libpam-pwquality # Debian/Ubuntu
# RHEL family: included (libpwquality); used via authselect
Configure /etc/security/pwquality.conf:
minlen = 14
dcredit = 0
ucredit = 0
lcredit = 0
ocredit = 0
minclass = 2
maxrepeat = 3
dictcheck = 1
usercheck = 1
enforce_for_root
minlen— minimum length. The most important setting.minclass— the minimum number of character classes (lowercase, uppercase, digits, other).dictcheck— reject dictionary words.usercheck— reject passwords containing the username.enforce_for_root— apply the rules when root sets passwords too.
Test it:
passwd # try a weak password — it should be rejected with a reason
On Debian/Ubuntu, installing the package adds pam_pwquality.so to /etc/pam.d/common-password automatically.
Password ageing: login.defs and chage
Defaults for new accounts are in /etc/login.defs:
PASS_MAX_DAYS 365
PASS_MIN_DAYS 1
PASS_WARN_AGE 14
Existing accounts aren’t affected. Change them with chage:
sudo chage -M 365 -m 1 -W 14 alice
sudo chage -l alice
PASS_MIN_DAYS 1 stops users from cycling through passwords to get back to an old one. See passwd and chage.
Prevent password reuse
Debian/Ubuntu, in /etc/pam.d/common-password, add remember=5 to the pam_unix.so line:
password [success=1 default=ignore] pam_unix.so obscure use_authtok try_first_pass yescrypt remember=5
RHEL family: pam_pwhistory (authselect enable-feature with-pwhistory, with remember = 5 in /etc/security/pwhistory.conf).
Lock accounts after failed logins: pam_faillock
pam_faillock counts failed attempts and locks the account temporarily.
RHEL family:
sudo authselect enable-feature with-faillock
Debian/Ubuntu: add it to /etc/pam.d/common-auth. Order matters, so follow your release’s documentation. A typical setup:
auth required pam_faillock.so preauth
auth [success=1 default=ignore] pam_unix.so nullok
auth [default=die] pam_faillock.so authfail
auth sufficient pam_faillock.so authsucc
and add account required pam_faillock.so to /etc/pam.d/common-account.
Settings in /etc/security/faillock.conf:
deny = 5
unlock_time = 900
fail_interval = 900
even_deny_root = false
Manage locks:
sudo faillock --user alice # show failures
sudo faillock --user alice --reset # unlock
Test PAM changes carefully. Keep a root session open while you edit PAM files, and test logins and sudo in a new session. A broken PAM config can lock everyone out, and then you’ll need the VNC console.
Note: faillock protects password logins. SSH key logins don’t hit it. For SSH brute-force attempts, Fail2Ban is the better tool, and key-only SSH removes the risk entirely. See Fail2Ban and key-only SSH.
Hashing algorithm
Modern distributions hash passwords with yescrypt (Debian 11+, Ubuntu 22.04+, recent Fedora) or SHA-512. Check:
sudo grep alice /etc/shadow | cut -d: -f2 | cut -c1-3 # $y$ = yescrypt, $6$ = SHA-512
grep ENCRYPT_METHOD /etc/login.defs
Hashes from older systems ($1$ MD5) should be replaced. Users get the new algorithm the next time they change their password.
Audit existing accounts
sudo awk -F: '($2 == "" ) {print $1 " has an empty password"}' /etc/shadow
sudo passwd -Sa | awk '$2=="P"{print $1}' # accounts with a usable password
awk -F: '$3 >= 1000 && $7 !~ /nologin|false/ {print $1}' /etc/passwd # human accounts with a shell
Lock accounts that shouldn’t log in, and remove ones that are no longer needed. See usermod.
Quick reference
| Goal | Where |
|---|---|
| Length and quality | /etc/security/pwquality.conf |
| Ageing defaults | /etc/login.defs |
| Ageing per user | chage |
| Reuse prevention | remember=N (pam_unix / pam_pwhistory) |
| Lockout | pam_faillock + /etc/security/faillock.conf |
| Unlock user | faillock --user NAME --reset |
Related: Linux server hardening and Lynis.