LinuxXXVII · Authentication and Enterprise IdentityPAM
PAM architecture - pluggable authentication on Linux
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
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
| Type | Purpose |
|---|---|
| auth | Authenticate the user (verify credentials) |
| account | Verify the account is allowed to log in (expiry, time-of-day, etc.) |
| session | Set up the session (mount home dir, set env vars, etc.) |
| password | Change 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:
| Flag | Effect |
|---|---|
required | Module must succeed; failure ends the stack but all modules still run |
requisite | Module must succeed; failure ends immediately |
sufficient | Module success ends the stack as success; failure is ignored unless a prior required has failed |
optional | Module’s verdict is ignored unless no other module has decided |
include | Include another PAM config file |
The combination of these flags defines the auth policy.
Common PAM modules
| Module | Purpose |
|---|---|
pam_unix.so | Standard UNIX authentication (passwd, shadow) |
pam_faillock.so | Account locking after failed attempts |
pam_pwquality.so | Password complexity rules |
pam_limits.so | Set ulimits from /etc/security/limits.conf |
pam_loginuid.so | Set the kernel audit UID |
pam_namespace.so | Per-user tmp namespace |
pam_ldap.so | LDAP authentication |
pam_sss.so | SSSD authentication |
pam_google_authenticator.so | TOTP MFA |
pam_fprintd.so | Fingerprint |
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
-
preauthruns before the password is asked for. It checks whether the user is already blocked. It does not count anything. -
authfailruns after the modules that decide the outcome, on the failure path. This is the instance that increments the counter. -
The
accountinstance 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
sufficientmodule before arequiredcan let auth succeed when it should fail. This is how MFA stacks get silently downgraded to password-only. Test the stack withpamtesterbefore you trust it. - Missing deny rules: forgetting
pam_deny.soat 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.dmatter. Restoring files with wrong contexts breaks auth.
Knowledge check
Knowledge check · 4 questions
Q1. Which PAM control flag means "module must succeed, but failure ends the stack immediately"?
Q2. When the /etc/pam.d directory exists, the per-service files inside it take precedence over /etc/pam.conf.
Q3. Which of the following are PAM module types? Select all that apply.
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.