Pluggable Authentication Modules ( PAM )

In the linux machines we can login in multiple ways -
CLI Way - SSH keys, Password based
Web UI Way - Cockpit tool
Desktop Way - GDM
All of these programs depend on PAM (Pluggable Authentication Modules). PAM is a central, pluggable framework: the app asks PAM “is this OK?”, PAM runs a stack of checks against whatever backends you choose (shadow, LDAP/SSSD, Kerberos, etc.), and returns success or failure (0/1)
Developer angle: app authors don’t need to hard-code “check /etc/shadow” or “call LDAP”. They link libpam, show a user/pass prompt (or accept a key), and PAM does the rest.
We can have rules such as
If Sai user enters wrong password 3 times we will block him for 24 hours
Sai user cannot use ssh key based login but only Password based login
Sai user cannot SSH after 6:00 PM from Friday to Saturday
Sai user can only login SSH from IP range 192.168.x.x
We can also say one user can login to another user without any restriction, sai user can run ‘su rohit’ and successfully login to rohit user
To play with pam all the files sits in etc directory, /etc/pam.d., this is where all the files are present and we need to create entries in these files
If we have an entry like this in /etc/pam.d/su ( su stands for switch user )
auth sufficient pam_rootok.so
The above entry will give root user permission to switch to other users without needing for the password.
If we add these rule in /etc/pam.d/login ( login stands for user logins )
auth sufficient pam_permit.so
This will skip all the auth methods such as key based, kerberos, password, MFA.

If an user enters username the login program has no capability to check the username so it contact to PAM, in /etc/shadow file we have password stored in SHA hash. We can tell PAM to check username from shadow file, password from LDAP., so this will isolate developer logic all he has to do is to keep a username, password field and PAM behind the scene checks it. Developer uses libPAM library in the code, PAM gives either success or failure , so either 0 / 1.,
PAM can do 4 things for us
Auth - prove identity (passwords, keys, 2FA, pre-auth checks)
Account - is access allowed now? (expiry, time windows, source IP rules)
Password - password change policy (strength/quality, reuse)
Session - tasks when a session opens/closes (ulimits, env, logging)
To see all the pam modules in RHEL we run cd /lib64/security command

In case of Debian systems the modules generally sit in
/lib/x86_64-linux-gnu/security

If we dont need to allow root user to login completely then we can have an entry in the, /etc/pam.d/login file
auth requisite pam_deny.so
No one cannot login due to deny rule
Control Keywords
We have 4 control keywords - requisite, required, sufficient, optional
To understand what is the significance of the control keywords, we first need to see a sample file

This is how /etc/pam.d/login, /etc/pam.d/su , /etc/pam.d/ssh might look like. So in these files we will have the conditions
Requisite :
If requisite rule fails then we will skip all the rules and reply fail. In case we have,
auth requisite pam_deny.so
It means stop immediately on failure
Required :
This will check other rules as well, but if it is fail then it will throw fail
auth required pam_deny.so
This will be helpful to record the logs
Sufficient :
If any rule success and if it has sufficient we will skip all the rules and give success
auth sufficient pam_permit.so
Optional :
It is neutral even if its pass / fail we will go to the next line, we can create a log and other rules can take the decision, modules will create the logs
If we have a bunch of rules we can keep all inside system-auth and use substack option,
auth substack system-auth
Practical Usecase :
We can stop hacker trying to do brute force attack.
Having an entry in /etc/pam.d/login such as
auth required pam_faildelay.so delay=5000000 # 5,000,000 µs = 5 seconds
So if we type a wrong password then we can retry after 5secs, if the password is of 10 characters then to crack the password it would take years as after first attempt he can try only after 5 secs.
How developers can use PAM ?
Apps (like login, gdm, sshd) link libpam and hand off authentication to PAM. No need to store passwords or talk to LDAP/Kerberos in your code—PAM handles it behind the scenes (shadow, LDAP/SSSD, Kerberos, local DB, etc.) and simply returns True/False. Changing backends is just a PAM config change, not an app rewrite.
pip install pam
import pam p = pam.pam()
p.authenticate("vimal", "mypass", service="login") # → True or False
service="login" picks the PAM stack to use (e.g., login or sshd). PAM verifies the credentials (e.g., against /etc/shadow), then returns a simple pass/fail.
Allow/Deny exact users with pam_listfile.so
Sometimes you just want a short allow/deny list for a given service. That’s what pam_listfile.so does.
Works per service. If you put it in
/etc/pam.d/login, it controls console logins only.
If you want the same rule for SSH, add it to/etc/pam.d/sshdtoo.It matches the username (you can also match groups/items, but here we use
item=user).
File: /etc/pam.d/login (auth stack)
auth required pam_listfile.so \ onerr=fail item=user sense=allow file=/etc/loginusers
auth substack system-auth
If /etc/loginusers contains sai, harry then only they are allowed to login other users ram, kiran are denied.
Required means if this rule fails it runs other rules but throw failure, why it still run other rules why not work like requisite ?
Because we might need to run other modules which can log the attempt, update counters, enforce polices, perform clean up sessions.
But ram, kiran can still use ssh to login as only we blocked password based method.
To check logs navigate to /var/log/secure directory, all user login activity is recorded here.
Time windows & emergency blocks (account phase)
For SSH: /etc/pam.d/sshd
account required pam_time.so
Write time rules in /etc/security/time.conf. Times are in server time.
Examples
sshd;;root;We1300-1700 # root allowed Wed 13:00–17:00 via SSH
sshd;;!root;Th1300-2300 # non-root allowed Thu 13:00–23:00 via SSH
local;*;vimal;We1300-1900 # vimal allowed local console Wed 13:00–19:00
Works only for the service you placed it in. If you add it to sshd, it won’t affect console login, and vice-versa. (On EC2 there’s usually no login, only SSH.)
“Nologin” maintenance switch
If we have many users in the server and we need to block the login for all users except root user then we have /etc/nologin file, this file has been used by nologin module so even if the file exists /etc/nologin without having any entry it will block all the users.
As soon as we remove file then all the users will be back, rm /etc/nologin
Make an entry in /etc/pam.d/sshd like these
account required pam_nologin.so
Allow/deny by IP
If we have a requirement to allow / deny user based on the IP address we can use pam_access.so module,
Have an entry like this in /etc/pam.d/sshd file,
auth required pam_access.so
Write rules in /etc/security/access.conf :
- : root : 192.168.0.0/24 # block root from this subnet
+ : ALL : ALL # allow everyone else
+ : (lw) : ALL # allow group 'lw'
Strong passwords
Force length/complexity in the password stack:
password requisite pam_pwquality.so difok=3 minlen=15 dcredit=2 ocredit=2
Positive credits (
dcredit=1) reduce the length requirement; if you must include a class without reducing length, use negatives (dcredit=-1,ocredit=-1).Under the hood it checks dictionary words (via cracklib DB).
Session limits
Add to the session stack and set caps in /etc/security/limits.conf
session required pam_limits.so
Examples :
sai hard nproc 20
sai soft maxlogins 3
sai hard maxlogins 5
Cgroups are the modern way for RAM/CPU per session, but limits still help
Lockouts
Block brute force after a few bad tries:
Have entry in /etc/pamd.d/system-auth
auth requisite pam_faillock.so deny=3 fail_interval=60 unlock_time=86400
Check/reset:
faillock --user <name>
faillock --user <name> --reset
pam_tally is for RHEL 7, from RHEL 8+ we use pam_faillock.so
SSH hardening knobs
In /etc/ssh/sshd_config we can have UsePAM, max sessions, max tries. Using this method is common in companies.
UsePAM yes
MaxAuthTries 3
MaxSessions 10
For IP based blocking we can install fail2ban software, sudo apt install fail2ban
Cloud note (EC2)
EC2 usually doesn’t have the local login service. Put your rules in /etc/pam.d/sshd and keep UsePAM yes.
Watch Logs in /var/log/secure directory.



