Skip to main content
RunBook Academy

LinuxXXVII · Authentication and Enterprise IdentityPAM

PAM architecture - pluggable authentication on Linux

Intermediate⏱ ~12 minfaillockpamtester

What you'll learn

  • Describe PAM's role in authentication and authorisation
  • Read and write PAM configuration files
  • Distinguish auth, account, session, and password modules
  • Recognise common PAM pitfalls

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

PAM (Pluggable Authentication Modules) is the framework behind every authentication on Linux - login, sudo, sshd, screen lockers, and more. Understanding PAM is essential for configuring authentication, debugging auth failures, and integrating with central identity (LDAP, AD).

What PAM does

PAM provides a standard API that applications call to authenticate users. The application does not need to know about passwords, OTP tokens, LDAP, or biometrics - it just calls PAM and PAM returns a verdict.

login / sudo / sshd
       |
       v
    libpam
       |
       v
   pam_unix.so (passwords)
   pam_ldap.so (LDAP)
   pam_google_authenticator.so (TOTP)
   pam_faillock.so (account locking)

The application loads the appropriate PAM service config (/etc/pam.d/login, /etc/pam.d/sudo, etc.), which is a stack of modules called in order.

The four module types

TypePurpose
authAuthenticate the user (verify credentials)
accountVerify the account is allowed to log in (expiry, time-of-day, etc.)
sessionSet up the session (mount home dir, set env vars, etc.)
passwordChange the user’s credentials

A typical PAM stack has separate sections for each type:

# /etc/pam.d/login
auth       required   pam_env.so
auth       sufficient pam_unix.so
auth       required   pam_deny.so

account    required   pam_unix.so
account    required   pam_faillock.so

session    required   pam_limits.so
session    required   pam_loginuid.so

password   required   pam_pwquality.so

Control flags

Each line has a control flag that determines how the module’s verdict affects the overall result:

FlagEffect
requiredModule must succeed; failure ends the stack but all modules still run
requisiteModule must succeed; failure ends immediately
sufficientModule success ends the stack as success; failure is ignored unless a prior required has failed
optionalModule’s verdict is ignored unless no other module has decided
includeInclude another PAM config file

The combination of these flags defines the auth policy.

Common PAM modules

ModulePurpose
pam_unix.soStandard UNIX authentication (passwd, shadow)
pam_faillock.soAccount locking after failed attempts
pam_pwquality.soPassword complexity rules
pam_limits.soSet ulimits from /etc/security/limits.conf
pam_loginuid.soSet the kernel audit UID
pam_namespace.soPer-user tmp namespace
pam_ldap.soLDAP authentication
pam_sss.soSSSD authentication
pam_google_authenticator.soTOTP MFA
pam_fprintd.soFingerprint

Configuration files

  • /etc/pam.d/<service>: per-service config (preferred).
  • /etc/pam.conf: legacy fallback if /etc/pam.d/<service> is missing.

Always prefer /etc/pam.d/<service> - it makes per-service overrides clean.

Example: password + TOTP

# /etc/pam.d/sshd
auth       required   pam_env.so
auth       required   pam_unix.so
auth       required   pam_google_authenticator.so
auth       required   pam_deny.so
account    required   pam_unix.so
account    required   pam_faillock.so
session    required   pam_limits.so
session    required   pam_loginuid.so
password   required   pam_pwquality.so

Every factor is required, so the stack runs to the end and both verdicts count. The user gives their password (pam_unix) and then a TOTP code (pam_google_authenticator). Failing either one fails the login. The closing pam_deny.so is the backstop: if a future edit removes the modules above it, the stack denies rather than falling through to success.

Note what is not on the TOTP line. pam_google_authenticator.so accepts a nullok option that lets users with no enrolled secret through on the first factor alone. Omit it. With nullok absent - as above - an unenrolled user is denied, which is the behaviour you want while you roll enrolment out.

Prove the stack before you trust it. pamtester exercises a service’s PAM stack directly, without risking your session:

sudo apt install pamtester        # or: dnf install pamtester
pamtester sshd alice authenticate

A correctly stacked service prompts for the password and then the verification code. If it returns success after the password alone, the second factor is not being reached - go back and check the control flags.

Account locking

pam_faillock needs three auth-stage instances, not one. Each has a different job, and the position in the stack is what gives it that job:

# Debian: /etc/pam.d/common-auth   RHEL: /etc/pam.d/system-auth
auth      required   pam_faillock.so preauth silent deny=5 unlock_time=900 fail_interval=900
auth      sufficient pam_unix.so
auth      [default=die] pam_faillock.so authfail deny=5 unlock_time=900 fail_interval=900
auth      required   pam_deny.so

account   required   pam_faillock.so
account   required   pam_unix.so
  • preauth runs before the password is asked for. It checks whether the user is already blocked. It does not count anything.

  • authfail runs after the modules that decide the outcome, on the failure path. This is the instance that increments the counter.

  • The account instance enforces the lock on subsequent logins.

  • deny=5: lock after 5 failed attempts.

  • unlock_time=900: auto-unlock after 15 minutes.

  • fail_interval=900: the window failures must fall inside to count as consecutive.

Check current lockout status:

# Substitute your own values before running:
TARGET_USER=alice

faillock --user "$TARGET_USER"         # list this user's recorded failures
sudo faillock --user "$TARGET_USER" --reset

Note that a bare faillock --reset clears the tally for every user on the host, not just the one you were investigating. Always name the user.

pam_faillock replaced pam_tally2, and “replaced” is stronger than “deprecated”: the Linux-PAM 1.5.0 release notes record pam_tally and pam_tally2 as removed, not merely discouraged. The module is not present on any current distribution, so a pam_tally2.so line inherited from an old configuration-management template does not fall back to anything - PAM fails to load the module and the stack behaves according to its control flag, which for required means every login on that host fails. If you find one, replace it with pam_faillock rather than reinstating the module.

Debugging PAM

Enable debug logging:

auth       required   pam_unix.so debug

Check the system log for PAM messages:

sudo journalctl -t sshd -t sshd-session | grep pam
sudo journalctl | grep -i 'pam\|auth'

Filter by syslog identifier (-t), not by unit (-u). The SSH unit is sshd.service on RHEL-family systems but ssh.service on Debian-family systems, where sshd.service exists only as an alias. journalctl -u sshd returns an empty result on Ubuntu with no error, which looks exactly like “PAM logged nothing”.

For a login failure:

pam_unix(sshd:auth): authentication failure; logname=... user=...

The line tells you the service (sshd), the module (pam_unix), and the type (auth).

Common pitfalls

  • PAM stack order: putting a sufficient module before a required can let auth succeed when it should fail. This is how MFA stacks get silently downgraded to password-only. Test the stack with pamtester before you trust it.
  • Missing deny rules: forgetting pam_deny.so at the end means an unmatched service can succeed by default.
  • Local changes to /etc/pam.d: configuration management should own this directory. Manual edits get lost.
  • PAM and SELinux: SELinux contexts on /etc/pam.d matter. Restoring files with wrong contexts breaks auth.

Knowledge check

Knowledge check · 4 questions

  1. Q1. Which PAM control flag means "module must succeed, but failure ends the stack immediately"?

  2. Q2. When the /etc/pam.d directory exists, the per-service files inside it take precedence over /etc/pam.conf.

  3. Q3. Which of the following are PAM module types? Select all that apply.

  4. Q4. A stack reads: auth sufficient pam_unix.so, then auth required pam_google_authenticator.so. What happens when a user enters a correct password?

Passing score: 75%. Answers are checked in this browser.