In this article we will explore pam_tally2 module which is used to maintain login counter in Linux environment. We will use pam_tally2 to lock user account after X failed login attempts, where X
can be any predefined integer value. Normally, failed attempts to access root will not cause the root account to become blocked, to prevent denial-of-service: if your users aren't given shell accounts and root may only login via su or at the machine console (not telnet/rsh, etc), this is safe.
We will cover following scenarios in this article
- Lock normal user after X failed password attempts
- Lock all users including root user after X after failed password attempts
- Lock only root user after X failed login attempts
- Provide exception to certain users
Topics we will cover hide
1. Introduction to pam_tally2 module
2. Check for pam_tally2 module availability
3. Pre-requisite - PAM configuration file
4. pam_tally2 syntax to lock user account after X failed login attempts
5. Lock non-root (normal user) after 3 failed login attempts
6. Lock all users (including root) after 3 failed login attempts
7. Lock only root user after 3 failed login attempts
8. Exclude certain users and groups from being locked with pam_tally2
9. Summary
10. Further Readings
1. Introduction to pam_tally2 module
pam_tally2
is a part of Linux-PAM (Pluggable Authentication Modules for Linux) which is a suite of shared libraries that controls authentication of users for applications such as login, ssh, su, and others.- This module maintains a count of attempted accesses, can reset count on success, can deny access if too many attempts fail.
pam_tally2
comes in two parts: pam_tally2.so and pam_tally2. The former is the PAM module and the latter, a stand-alone program- Linux locates the PAM configuration files in the
/etc/pam.d
directory. Configuration files for services such as login, ssh, and others are located here.
For example, here is a sample output from /etc/pam.d/sudo
The first column represents authentication tasks, grouped by account, authentication, password, and session:
- account: Provides account verification services (for example, has the password expired? Is the user allowed access to a specific service?).
- auth: Used to authenticate the user, request a password, and set up credentials.
- password: Requests the user enters a replacement password when updating the password.
- session: Manages what happens during setup or cleanup of a service (for example, mounting the home directory or setting resource limits).
The second column represents the control keyword to manage the success or failure processing:
- required: If required fails, the entire operation fails after running through all the other modules.
- requisite: Operation fails immediately if requisite fails.
- sufficient: If successful, this is enough to satisfy the requirements of the service.
- optional: Will cause an operation to fail if it is the only module in the stack for that facility.
The third column displays the module that gets invoked, which can take additional options and arguments. For example, in /etc/pam.d/sudo
we have three different module entries:
system-auth
refers to/etc/pam.d/system-auth
file which provides system authentication and authorization.- The
pam_keyinit.so
module ensures that the invoking process has a session keyring other than the user default session keyring. - The
pam_limits
PAM module sets limits on the system resources that can be obtained in a user-session.
2. Check for pam_tally2 module availability
In later releases of Linux, now pam_tally2
is replaced by pam_faillock
. So you should be sure that pam_tally2 module is available on your Linux server. You can check if your Linux system has pam_tally2
module available using following command:
~]# rpm -ql pam | grep pam_tally2/usr/lib64/security/pam_tally2.so/usr/sbin/pam_tally2/usr/share/doc/pam-1.1.8/html/sag-pam_tally2.html/usr/share/doc/pam-1.1.8/txts/README.pam_tally2/usr/share/man/man8/pam_tally2.8.gz
3. Pre-requisite - PAM configuration file
We must make the changes to following two configuration files to lock any type of user account after X number of failed login attempts:
/etc/pam.d/system-auth/etc/pam.d/password-auth
4. pam_tally2 syntax to lock user account after X failed login attempts
The syntax to be used with pam_tally2.so
module:
pam_tally2.so [file=/path/to/counter] [onerr=[fail|succeed]] [even_deny_root] [deny=n] [lock_time=n] [unlock_time=n] [root_unlock_time=n] [audit] [silent]
Here,
onerr=[fail|succeed] If something weird happens (like unable to open the file), return with PAM_SUCCESS if onerr=succeed is given, else with the corresponding PAM error code.deny=n Deny access if tally for this user exceeds n.lock_time=n Always deny for n seconds after failed attempt.unlock_time=n Allow access after n seconds after failed attempt. If this option is used the user will be locked out for the specified amount of time after he exceeded his maximum allowed attempts. Otherwise the account is locked until the lock is removed by a manual intervention of the system administrator.file=/path/to/counter File where to keep counts. Default is /var/log/tallylog.
5. Lock non-root (normal user) after 3 failed login attempts
Following is the syntax to lock a user account after 3 failed login attempts. You can modify deny=X
to increase or decrease the counter value required to lock an account. Additionally we have also defined an unlock time of 5 minutes after which the user will be allowed to access the server again.
auth required pam_tally2.so deny=3 onerr=fail unlock_time=300account required pam_tally2.so
We would need to add the following entries in the required PAM configuration file i.e. /etc/pam.d/system-auth
and /etc/pam.d/password-auth
Sample /etc/pam.d/system-auth
file from my Linux server:
#%PAM-1.0# This file is auto-generated.# User changes will be destroyed the next time authconfig is run.auth required pam_env.soauth required pam_tally2.so deny=3 onerr=fail unlock_time=300auth required pam_faildelay.so delay=2000000auth [default=1 ignore=ignore success=ok] pam_succeed_if.so uid >= 1000 quietauth [default=1 ignore=ignore success=ok] pam_localuser.soauth sufficient pam_unix.so nullok try_first_passauth requisite pam_succeed_if.so uid >= 1000 quiet_success#auth sufficient pam_sss.so forward_passauth required pam_deny.soaccount required pam_tally2.soaccount required pam_unix.so broken_shadow...<output trimmed>
Sample /etc/pam.d/password-auth
file
#%PAM-1.0# This file is auto-generated.# User changes will be destroyed the next time authconfig is run.auth required pam_env.soauth required pam_tally2.so deny=3 onerr=fail unlock_time=300auth required pam_faildelay.so delay=2000000auth [default=1 ignore=ignore success=ok] pam_succeed_if.so uid >= 1000 quietauth [default=1 ignore=ignore success=ok] pam_localuser.soauth sufficient pam_unix.so nullok try_first_passauth requisite pam_succeed_if.so uid >= 1000 quiet_success#auth sufficient pam_sss.so forward_passauth required pam_deny.soaccount required pam_tally2.soaccount required pam_unix.so broken_shadow...<output trimmed>
5.1 Verify the pam.d configuration
Let us verify our configuration. I will attempt to login to my Linux server via SSH using user1 and give wrong password:
Apr 12 00:21:12 server unix_chkpwd[2438]: password check failed for user (user1) <-- First attempt with incorrect passwordApr 12 00:21:12 server sshd[2436]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.2.2 user=user1Apr 12 00:21:15 server sshd[2436]: Failed password for user1 from 10.0.2.2 port 64064 ssh2Apr 12 00:21:17 server unix_chkpwd[2439]: password check failed for user (user1) <-- Second attempt with incorrect passwordApr 12 00:21:20 server sshd[2436]: Failed password for user1 from 10.0.2.2 port 64064 ssh2Apr 12 00:21:24 server unix_chkpwd[2440]: password check failed for user (user1) <-- Third attempt with incorrect passwordApr 12 00:21:26 server sshd[2436]: Failed password for user1 from 10.0.2.2 port 64064 ssh2Apr 12 00:21:29 server sshd[2436]: pam_tally2(sshd:auth): user user1 (1002) tally 4, deny 3 <-- Fourth attempt with incorrect password (Account locked by pam_tally2)Apr 12 00:21:30 server unix_chkpwd[2441]: password check failed for user (user1)Apr 12 00:21:32 server sshd[2436]: Failed password for user1 from 10.0.2.2 port 64064 ssh2
You can verify the same using pam_tally2
binary:
~]# pam_tally2 --user user1Login Failures Latest failure Fromuser1 4 04/12/21 00:21:29 10.0.2.2
Since our threshold limit was 3, after 3 failed login attempts the user1
account was locked. To unlock this user you can again use pam_tally2
binary command:
~]# pam_tally2 --user user1 --reset
This command will reset the failed login counter:
~]# pam_tally2 --user user1Login Failures Latest failure Fromuser1 0
6. Lock all users (including root) after 3 failed login attempts
To also include root user in the list of users which should be locked after 3 failed login attempts, update the syntax we used above to:
auth required pam_tally2.so deny=3 onerr=fail unlock_time=300 even_deny_rootaccount required pam_tally2.so
Add these lines in the same format and in the same line number as we added in the previous section into /etc/pam.d/system-auth
and /etc/pam.d/password-auth
Next I try to login via root using SSH and in another terminal I will monitor /var/log/secure
:
~]# tail -f /var/log/secureApr 12 00:30:41 server unix_chkpwd[2451]: password check failed for user (root) <-- First attempt with incorrect passwordApr 12 00:30:41 server sshd[2449]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.2.2 user=rootApr 12 00:30:41 server sshd[2449]: pam_succeed_if(sshd:auth): requirement "uid >= 1000" not met by user "root"Apr 12 00:30:44 server sshd[2449]: Failed password for root from 10.0.2.2 port 64308 ssh2Apr 12 00:30:46 server unix_chkpwd[2452]: password check failed for user (root) <-- Second attempt with incorrect passwordApr 12 00:30:46 server sshd[2449]: pam_succeed_if(sshd:auth): requirement "uid >= 1000" not met by user "root"Apr 12 00:30:48 server sshd[2449]: Failed password for root from 10.0.2.2 port 64308 ssh2Apr 12 00:30:50 server unix_chkpwd[2453]: password check failed for user (root) <-- Third attempt with incorrect passwordApr 12 00:30:50 server sshd[2449]: pam_succeed_if(sshd:auth): requirement "uid >= 1000" not met by user "root"Apr 12 00:30:52 server sshd[2449]: Failed password for root from 10.0.2.2 port 64308 ssh2Apr 12 00:30:59 server sshd[2449]: pam_tally2(sshd:auth): user root (0) tally 4, deny 3 <-- Fourth attempt with incorrect password (Account locked by pam_tally2)Apr 12 00:30:59 server unix_chkpwd[2454]: password check failed for user (root)Apr 12 00:30:59 server sshd[2449]: pam_succeed_if(sshd:auth): requirement "uid >= 1000" not met by user "root"Apr 12 00:31:01 server sshd[2449]: Failed password for root from 10.0.2.2 port 64308 ssh2
We can also verify this using pam_tally2
command:
~]# pam_tally2 --user rootLogin Failures Latest failure Fromroot 4 04/12/21 00:30:59 10.0.2.2
To unlock this user you can reset the failed login counter using pam_tally2
command:
~]# pam_tally2 --user root --reset
7. Lock only root user after 3 failed login attempts
In the previous examples I shared the syntax to lock either all normal users or both normal and root user account after X failed login attempts. To only lock root user account we need to add one additional line to allow login for all other users if GID is not equal to 0
.
auth [success=1 default=ignore] pam_succeed_if.so gid ne 0auth required pam_tally2.so deny=3 onerr=fail unlock_time=300 even_deny_rootaccount required pam_tally2.so
Add these lines into both /etc/pam.d/system-auth
and /etc/pam.d/password-auth
file. After adding these lines we do a quick check:
First I try to login via a normal user i.e. user1
and monitor the logs from /var/log/secure
:
Apr 12 00:38:27 server sshd[2462]: pam_succeed_if(sshd:auth): requirement "gid ne 0" was met by user "user1"Apr 12 00:38:27 server unix_chkpwd[2464]: password check failed for user (user1)Apr 12 00:38:27 server sshd[2462]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.2.2 user=user1Apr 12 00:38:29 server sshd[2462]: Failed password for user1 from 10.0.2.2 port 64510 ssh2 <-- First attempt with incorrect passwordApr 12 00:38:32 server sshd[2462]: pam_succeed_if(sshd:auth): requirement "gid ne 0" was met by user "user1"Apr 12 00:38:32 server unix_chkpwd[2465]: password check failed for user (user1)Apr 12 00:38:34 server sshd[2462]: Failed password for user1 from 10.0.2.2 port 64510 ssh2 <-- Second attempt with incorrect passwordApr 12 00:38:44 server sshd[2462]: pam_succeed_if(sshd:auth): requirement "gid ne 0" was met by user "user1"Apr 12 00:38:44 server unix_chkpwd[2466]: password check failed for user (user1)Apr 12 00:38:46 server sshd[2462]: Failed password for user1 from 10.0.2.2 port 64510 ssh2 <-- Third attempt with incorrect passwordApr 12 00:38:52 server sshd[2462]: pam_succeed_if(sshd:auth): requirement "gid ne 0" was met by user "user1"Apr 12 00:38:52 server unix_chkpwd[2467]: password check failed for user (user1)Apr 12 00:38:54 server sshd[2462]: Failed password for user1 from 10.0.2.2 port 64510 ssh2 <-- Fourth attempt with incorrect passwordApr 12 00:38:56 server sshd[2462]: pam_succeed_if(sshd:auth): requirement "gid ne 0" was met by user "user1"Apr 12 00:38:56 server sshd[2462]: Accepted password for user1 from 10.0.2.2 port 64510 ssh2Apr 12 00:38:57 server sshd[2462]: pam_unix(sshd:session): session opened for user user1 by (uid=0) <-- Correct password accepted in fifth attempt
So as you see, even when the provided password was incorrect for four times, the user account was not locked as the fifth attempt of correct password was successfully accepted.
Let's verify the same with root
user now:
Apr 12 00:42:22 server sshd[2496]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.2.2 user=rootApr 12 00:42:22 server sshd[2496]: pam_succeed_if(sshd:auth): requirement "uid >= 1000" not met by user "root"Apr 12 00:42:24 server sshd[2496]: Failed password for root from 10.0.2.2 port 64626 ssh2 <-- First attempt with incorrect passwordApr 12 00:42:26 server sshd[2496]: pam_succeed_if(sshd:auth): requirement "gid ne 0" not met by user "root"Apr 12 00:42:26 server unix_chkpwd[2499]: password check failed for user (root)Apr 12 00:42:26 server sshd[2496]: pam_succeed_if(sshd:auth): requirement "uid >= 1000" not met by user "root"Apr 12 00:42:28 server sshd[2496]: Failed password for root from 10.0.2.2 port 64626 ssh2 <-- Second attempt with incorrect passwordApr 12 00:42:29 server sshd[2496]: pam_succeed_if(sshd:auth): requirement "gid ne 0" not met by user "root"Apr 12 00:42:29 server unix_chkpwd[2500]: password check failed for user (root)Apr 12 00:42:29 server sshd[2496]: pam_succeed_if(sshd:auth): requirement "uid >= 1000" not met by user "root"Apr 12 00:42:32 server sshd[2496]: Failed password for root from 10.0.2.2 port 64626 ssh2 <-- Third attempt with incorrect passwordApr 12 00:42:34 server sshd[2496]: pam_succeed_if(sshd:auth): requirement "gid ne 0" not met by user "root"Apr 12 00:42:34 server sshd[2496]: pam_tally2(sshd:auth): user root (0) tally 4, deny 3 <-- Fourth attempt with incorrect password (Account locked by pam_tally2)Apr 12 00:42:34 server unix_chkpwd[2501]: password check failed for user (root)Apr 12 00:42:34 server sshd[2496]: pam_succeed_if(sshd:auth): requirement "uid >= 1000" not met by user "root"Apr 12 00:42:36 server sshd[2496]: Failed password for root from 10.0.2.2 port 64626 ssh2
So our pam.d
configuration is working as expected. The applied configuration will not lock normal user account and is only valid for root user.
8. Exclude certain users and groups from being locked with pam_tally2
We can utilise pam_succeed_if.so
module in the auth phase to skip over rules when specific conditions are met. This works well to prevent certain users from being affected by pam_tally2
.
For example:
auth [default=1 success=ignore] pam_succeed_if.so user in user1auth required pam_tally2.so onerr=fail deny=3 unlock_time=300account required pam_tally2.so
As per above example, if the user trying to authenticate is "user1
", the pam_succeed_if
test will pass, and the authentication process will continue to the pam_tally2
check, If user is other then user1
then it will skip 1 line and will continue over pam_unix
.
pam_succeed_if
takes three arguments: field, test, and value to test:
field
can be "user", "uid", and "gid", among otherstest
can be a simple "in" / "notin" (or "ingroup" / "notingroup") testtest
can also be a numerical comparison test like "=", "<", ">="value
can be a user, uid, group, gid, or a colon-delimited list of the same when using in/notin
For example:
auth [success=4 default=ignore] pam_succeed_if.so user in user1:user4:user10auth [success=3 default=ignore] pam_succeed_if.so gid > 1000auth [success=2 default=ignore] pam_succeed_if.so uid in 1000:1200auth [success=1 default=ignore] pam_succeed_if.so user ingroup admingroupauth required pam_tally2.so deny=3 onerr=fail unlock_time=300account required pam_tally2.so
The key thing to understand when using multiple lines is that N
in the "success=N
" part needs to equal the number of lines it takes to skip past the pam_tally2
line.
To demonstrate this example I have created user3 and added it to admin group. Now we will exclude user1
and admin
group from the list of accounts to be locked after 3 failed login attempts:
~]# id user3uid=1003(user3) gid=1003(user3) groups=1003(user3),1004(admin)
Following is my sample /etc/pam.d/system-auth
file (output is trimmed):
Following is my sample /etc/pam.d/password-auth
file (output is trimmed):
Let's verify these PAM configuration. I will try to login as user3
which should be excluded as it is part of admin
group:
As you can see the following entry, which means that pam_succeed_if
has matched the user3
inside admin group which should be excluded from account lockout:
pam_succeed_if(sshd:auth): requirement "user ingroup admin" was met by user "user3"
I have written another article covering this topic in much more depth and examples: How to exclude some accounts from being locked after multiple incorrect password
9. Summary
In this article we learned about pam_tally2 module which is part of Linux PAM. pam_tally2 is the successor of pam_tally
which had a per_user
option that allowed one to modify the login policy on a per-user basis by using the faillog
utility; however, pam_tally2
lacks an equivalent feature. Now pam_tally2 is also deprecated and is succeeded by pam_faillock
which we will discuss in another article. But if your distribution still uses pam_tally2 then you can use the steps from this article to lock out user accounts based on your requirement.
10. Further Readings
How to exclude certain users from being affected by pam_tally2
How to configure pam_tally2 to lock user account after certain number of failed login attempts
How to enable faillog with pam_tally2