PAM (Pluggable Authentication Modules) handles SSH authentication, and when it fails, you see errors like:

bash
$ ssh user@server
Permission denied, please try again.

Or in server logs:

bash
pam_unix(sshd:auth): authentication failure
pam_unix(sshd:account): account username has expired
fatal: Access denied for user username by PAM account configuration

PAM issues can block valid users from accessing SSH.

Understand PAM SSH Authentication

SSH uses PAM when UsePAM yes is enabled in sshd_config. PAM handles: - Password verification - Account validity checks - Session setup - Resource limits

Any PAM module failure blocks SSH access.

Check SSH PAM Configuration

Verify SSH uses PAM:

bash
sudo grep UsePAM /etc/ssh/sshd_config

Should show:

bash
UsePAM yes

If set to no, PAM authentication is disabled. Enable it:

bash
sudo sed -i 's/^UsePAM.*/UsePAM yes/' /etc/ssh/sshd_config
sudo systemctl restart sshd

Examine PAM SSH Configuration

Check PAM rules for SSH:

bash
cat /etc/pam.d/sshd

Typical configuration:

bash
auth       required     pam_sepermit.so
auth       substack     password-auth
auth       include      postlogin
account    required     pam_nologin.so
account    include      password-auth
password   include      password-auth
session    required     pam_selinux.so close
session    required     pam_loginuid.so
session    create       pam_selinux.so open
session    required     pam_namespace.so
session    optional     pam_keyinit.so force revoke
session    include      password-auth
session    include      postlogin

Check Account Status

PAM may reject users due to account issues:

bash
sudo passwd -S username

Output:

bash
username PS 2026-01-01 0 99999 7 -1 (Password set, SHA512 crypt.)

Check for locked accounts:

bash
sudo passwd -S username | grep L

Unlock account:

bash
sudo passwd -u username

Check account expiration:

bash
sudo chage -l username

Output:

bash
Last password change                    : Apr 03, 2026
Password expires                        : never
Password inactive                       : never
Account expires                         : never
Minimum number of days between password change  : 0
Maximum number of days between password change  : 99999
Number of days of warning before password expires : 7

Extend expiration:

bash
sudo chage -E -1 username
sudo chage -M 90 username

Check Password Lockout

Accounts can be locked after failed attempts:

bash
sudo faillock --user username

Unlock:

bash
sudo faillock --user username --reset

Or using pam_tally2:

bash
sudo pam_tally2 --user username --reset

Debug PAM Authentication

Add debug to PAM modules:

bash
# Edit /etc/pam.d/sshd
auth required pam_unix.so debug

Or run SSHD in debug mode:

bash
sudo /usr/sbin/sshd -d -p 2222

Connect to debug port:

bash
ssh -p 2222 user@server

Watch output for PAM messages.

Check Access Control

PAM access module restricts users:

bash
grep pam_access /etc/pam.d/sshd

If enabled, check rules:

bash
cat /etc/security/access.conf

Rules format:

bash
+ : @group : ALL
- : ALL : ALL EXCEPT LOCAL

Add your user:

bash
echo "+ : username : ALL" | sudo tee -a /etc/security/access.conf

Check Time Restrictions

PAM time module restricts access hours:

bash
grep pam_time /etc/pam.d/sshd

Check time rules:

bash
cat /etc/security/time.conf

Format:

bash
sshd;*;username;MoTuWeThFr0800-1700

This allows SSH only during business hours.

Check Login Defs

System-wide login settings:

bash
cat /etc/login.defs | grep -i uid

Check UID limits that PAM might enforce.

Verify Password Hash

Check password hash is valid:

bash
sudo grep username /etc/shadow

Hash should look like:

bash
username:$6$salt$hash:18000:0:99999:7:::

Empty or invalid hash causes PAM failure.

Reset password:

bash
sudo passwd username

Check SELinux Contexts

SELinux can block PAM:

bash
sudo ausearch -m avc -ts recent | grep pam

Restore contexts:

bash
sudo restorecon -R /etc/pam.d
sudo restorecon -R /etc/security

Check nologin File

PAM checks /etc/nologin:

bash
cat /etc/nologin

If this file exists, non-root users can't login. Remove it:

bash
sudo rm /etc/nologin

Check SSH AllowUsers

SSH might restrict users before PAM:

bash
sudo grep AllowUsers /etc/ssh/sshd_config

If set, your user must be listed:

bash
AllowUsers admin operator username

Add user:

bash
sudo sed -i 's/^AllowUsers.*/AllowUsers admin operator username/' /etc/ssh/sshd_config
sudo systemctl restart sshd

Monitor PAM Logs

Watch authentication logs:

bash
sudo tail -f /var/log/auth.log | grep pam

Or:

bash
sudo journalctl -u sshd -f | grep pam

Test Authentication

Test PAM authentication directly:

bash
# Test with specific user
sudo pamtester sshd username authenticate

This tests PAM without SSH.

Resolution Checklist

  1. 1.Verify UsePAM is enabled
  2. 2.Check account status: passwd -S username
  3. 3.Check account expiration: chage -l username
  4. 4.Reset password lockout: faillock --reset
  5. 5.Debug PAM: add debug option to modules
  6. 6.Check access.conf rules
  7. 7.Check time.conf restrictions
  8. 8.Remove /etc/nologin if exists
  9. 9.Check AllowUsers in sshd_config
  10. 10.Watch logs: journalctl -u sshd -f

PAM authentication failures are usually account status issues or policy restrictions. Start by checking the account status, then examine PAM configuration files for restrictive modules.