How to Configure PAM on RHEL 7
Pluggable Authentication Modules (PAM) is the authentication framework that underlies almost every login mechanism on Red Hat Enterprise Linux 7. When a user runs su, logs in over SSH, unlocks a screensaver, or authenticates to any PAM-aware application, the PAM stack determines whether that authentication succeeds, what restrictions apply, and what happens to the session. Because PAM is responsible for enforcing password complexity rules, account lockout policies, resource limits, and access control, understanding how to configure it correctly is one of the most important system administration skills on RHEL 7. This tutorial covers the PAM stack model, the layout of the /etc/pam.d/ directory, control flag semantics, and how to configure four of the most commonly needed PAM modules: pam_pwquality for password complexity, pam_tally2 for brute-force lockout, pam_limits for resource constraints, and pam_access for host and user-level access control.
Prerequisites
- RHEL 7 system with root access.
- A basic understanding of the Linux login process.
- A second terminal or console session open before making PAM changes — a misconfigured PAM file can lock you out of the system entirely.
- The packages
pam,libpwquality, andpam_tally2are all included in the base RHEL 7 install.
Step 1: The PAM Stack Model
Every PAM-aware service has a configuration file under /etc/pam.d/ named after the service (for example, /etc/pam.d/sshd, /etc/pam.d/sudo, /etc/pam.d/login). Each file contains a list of rules called a stack. PAM processes the stack from top to bottom, and the combined result of all applicable rules determines whether the operation is permitted.
Each rule line has four fields:
type control module-path [module-arguments]
Module Types
- auth — Verifies the user’s identity (password, token, fingerprint). Also sets credentials.
- account — Checks account restrictions: is the account expired? Is login allowed at this time of day? Has the password expired?
- password — Handles password changes. Enforces complexity rules and updates the credential store.
- session — Sets up and tears down the user session: mounting home directories, applying limits, logging.
Control Flags
- required — The module must succeed for the overall result to be success. If it fails, processing continues through the stack (so the user does not know exactly which module failed), but the final result is failure.
- requisite — Like
required, but on failure PAM stops processing immediately and returns failure. Use for fast-fail checks. - sufficient — If this module succeeds and no previous
requiredmodule has failed, PAM immediately returns success and stops processing. If it fails, processing continues. - optional — The result is only significant if it is the only module of this type in the stack. Used for modules that provide supplementary functionality (such as session logging) rather than access control.
Step 2: The /etc/pam.d/ Directory Structure
Inspect the directory to see the service-specific files:
ls /etc/pam.d/
You will see files like sshd, sudo, su, login, system-auth, and password-auth. The last two are the most important: they are shared stacks included by most other service files via the include directive, meaning a change to system-auth automatically propagates to all services that include it.
grep include /etc/pam.d/sshd
auth include password-auth
account include password-auth
password include password-auth
session optional pam_keyinit.so force revoke
session include password-auth
On RHEL 7, these shared files are managed by authconfig. Changes made directly to /etc/pam.d/system-auth may be overwritten if authconfig is run. The safe approach is to edit the -ac suffixed files (system-auth-ac, password-auth-ac) and then let symlinks point to them, or to use the authconfig command to make changes.
Step 3: Configure Password Complexity with pam_pwquality
pam_pwquality replaces the older pam_cracklib on RHEL 7 and enforces rules for minimum length, character classes, dictionary checks, and more. Its system-wide settings live in /etc/security/pwquality.conf.
vi /etc/security/pwquality.conf
# Minimum acceptable password length
minlen = 14
# Minimum number of required character classes (lower, upper, digit, other)
minclass = 3
# Maximum number of characters in the same character class in a row
maxclassdif = 4
# Maximum number of consecutive same characters
maxrepeat = 3
# Credit for including an uppercase letter (-1 = require at least one)
ucredit = -1
# Credit for including a lowercase letter
lcredit = -1
# Credit for including a digit
dcredit = -1
# Credit for including a special character
ocredit = -1
# Reject passwords containing the user's name
gecoscheck = 1
# Number of previous passwords to remember (used with pam_pwhistory)
# (set in pam_pwhistory, not here)
The pam_pwquality module must be in the password stack. Check /etc/pam.d/system-auth:
grep pwquality /etc/pam.d/system-auth
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
If you need to pass additional arguments, you can set them inline or in pwquality.conf. Using the config file is cleaner for multiple settings.
Step 4: Configure Account Lockout with pam_tally2
pam_tally2 maintains a per-user count of failed authentication attempts and can lock accounts after a configured threshold. This is essential protection against brute-force password attacks on local accounts.
Edit /etc/pam.d/system-auth to add pam_tally2 to both the auth and account stacks:
vi /etc/pam.d/system-auth
# Add to the auth section — BEFORE the existing pam_unix.so line:
auth required pam_tally2.so deny=5 unlock_time=900 onerr=fail
# The existing unix auth line stays in place:
auth sufficient pam_unix.so nullok try_first_pass
# Add to the account section:
account required pam_tally2.so
Key arguments for pam_tally2:
deny=5— Lock the account after 5 failed attempts.unlock_time=900— Automatically unlock after 900 seconds (15 minutes). Useunlock_time=0for permanent lockout requiring manual intervention.onerr=fail— If the module cannot access its tally file, fail securely (deny access).even_deny_root— Also apply lockout to the root account (use with caution; ensures console access first).
To view and manage tally counts:
# View failed attempt count for a specific user
pam_tally2 --user=bob
# Manually reset (unlock) a locked account
pam_tally2 --user=bob --reset
Step 5: Apply Resource Limits with pam_limits
pam_limits enforces per-user and per-group resource limits defined in /etc/security/limits.conf (and files in /etc/security/limits.d/). These limits control things like the maximum number of open files, maximum number of processes, and CPU time — critical for preventing a single user or runaway process from exhausting system resources.
vi /etc/security/limits.conf
# Format: domain type item value
# domain: username, @groupname, or * (all users)
# type: soft (advisory, user can increase up to hard) or hard (ceiling)
# Prevent a normal user from creating core dumps (security best practice)
* hard core 0
# Limit maximum number of open file descriptors for all users
* soft nofile 1024
* hard nofile 65536
# Limit for a specific high-load service account
webapp soft nofile 8192
webapp hard nofile 65536
webapp soft nproc 512
webapp hard nproc 1024
# Prevent forks bombs by limiting processes for regular users
@users hard nproc 200
For services like databases or web servers that need higher limits, place overrides in /etc/security/limits.d/:
cat > /etc/security/limits.d/mysql.conf <<'EOF'
mysql soft nofile 65536
mysql hard nofile 65536
mysql soft nproc 2048
mysql hard nproc 2048
EOF
Verify the module is loaded in the session stack:
grep pam_limits /etc/pam.d/system-auth
session required pam_limits.so
Step 6: Restrict Login Access with pam_access
pam_access enforces an access control list defined in /etc/security/access.conf. It allows you to permit or deny logins based on username, group membership, and source hostname or IP address.
vi /etc/security/access.conf
# Format: permission : users/groups : origins
# permission: + (allow) or - (deny)
# origins: hostname, IP address, LOCAL (console), ALL (any)
# Allow root only from the console and from the management network
+ : root : LOCAL
+ : root : 192.168.10.0/24
- : root : ALL
# Allow members of the sysadmin group from anywhere on the private network
+ : @sysadmin : 10.0.0.0/8
- : @sysadmin : ALL
# Deny all other users from remote access (must log in locally)
- : ALL : ALL EXCEPT LOCAL
Enable pam_access in the account stack of /etc/pam.d/system-auth:
vi /etc/pam.d/system-auth
# Add in the account section:
account required pam_access.so
Test thoroughly before relying on this in production — an incorrect rule can lock out all remote access.
Conclusion
PAM is one of the most powerful and most dangerous subsystems to configure on RHEL 7: a single misplaced line can lock out all users, including root. Always keep a second terminal session open while editing PAM files, and always test changes by opening a fresh login session before closing the one you used to make the change. The combination of pam_pwquality for strong passwords, pam_tally2 for lockout, pam_limits for resource control, and pam_access for network-based access restrictions gives you a well-rounded hardening baseline that meets the requirements of most security frameworks. Because the shared system-auth and password-auth stacks propagate changes to all PAM-aware services automatically, you can enforce consistent policy across SSH, su, sudo, and local console logins from a single configuration point.