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.

  • Security
  • Linux
  • Hardening