pam_tally2: lock user account after X failed login attempts in Linux


Tips and Tricks

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

 

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
pam_tally2: lock user account after X failed login attempts in Linux

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=300
account     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.so
auth        required      pam_tally2.so deny=3 onerr=fail unlock_time=300
auth        required      pam_faildelay.so delay=2000000
auth        [default=1 ignore=ignore success=ok] pam_succeed_if.so uid >= 1000 quiet
auth        [default=1 ignore=ignore success=ok] pam_localuser.so
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 1000 quiet_success
#auth        sufficient    pam_sss.so forward_pass
auth        required      pam_deny.so
account     required      pam_tally2.so

account     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.so
auth        required      pam_tally2.so deny=3 onerr=fail unlock_time=300
auth        required      pam_faildelay.so delay=2000000
auth        [default=1 ignore=ignore success=ok] pam_succeed_if.so uid >= 1000 quiet
auth        [default=1 ignore=ignore success=ok] pam_localuser.so
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 1000 quiet_success
#auth        sufficient    pam_sss.so forward_pass
auth        required      pam_deny.so
account     required      pam_tally2.so

account     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 password
Apr 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=user1
Apr 12 00:21:15 server sshd[2436]: Failed password for user1 from 10.0.2.2 port 64064 ssh2

Apr 12 00:21:17 server unix_chkpwd[2439]: password check failed for user (user1)  <-- Second attempt with incorrect password
Apr 12 00:21:20 server sshd[2436]: Failed password for user1 from 10.0.2.2 port 64064 ssh2

Apr 12 00:21:24 server unix_chkpwd[2440]: password check failed for user (user1)  <-- Third attempt with incorrect password
Apr 12 00:21:26 server sshd[2436]: Failed password for user1 from 10.0.2.2 port 64064 ssh2

Apr 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 user1
Login           Failures Latest failure     From
user1               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 user1
Login           Failures Latest failure     From
user1               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_root
account     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/secure

Apr 12 00:30:41 server unix_chkpwd[2451]: password check failed for user (root)  <-- First attempt with incorrect password
Apr 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=root
Apr 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 ssh2

Apr 12 00:30:46 server unix_chkpwd[2452]: password check failed for user (root)  <-- Second attempt with incorrect password
Apr 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 ssh2


Apr 12 00:30:50 server unix_chkpwd[2453]: password check failed for user (root)  <-- Third attempt with incorrect password
Apr 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 ssh2

Apr 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 root
Login           Failures Latest failure     From
root                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 0
auth        required      pam_tally2.so deny=3 onerr=fail unlock_time=300 even_deny_root

account     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=user1
Apr 12 00:38:29 server sshd[2462]: Failed password for user1 from 10.0.2.2 port 64510 ssh2  <-- First attempt with incorrect password

Apr 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 password

Apr 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 password

Apr 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 password

Apr 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 ssh2
Apr 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=root
Apr 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 password

Apr 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 password

Apr 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 password

Apr 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 user1
auth        required      pam_tally2.so onerr=fail deny=3 unlock_time=300

account     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 others
  • test can be a simple "in" / "notin" (or "ingroup" / "notingroup") test
  • test 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:user10
auth [success=3 default=ignore] pam_succeed_if.so gid > 1000
auth [success=2 default=ignore] pam_succeed_if.so uid in 1000:1200
auth [success=1 default=ignore] pam_succeed_if.so user ingroup admingroup
auth        required      pam_tally2.so deny=3 onerr=fail unlock_time=300

account     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 user3
uid=1003(user3) gid=1003(user3) groups=1003(user3),1004(admin)

Following is my sample /etc/pam.d/system-auth file (output is trimmed):
pam_tally2: lock user account after X failed login attempts in Linux

 

Following is my sample /etc/pam.d/password-auth file (output is trimmed):
pam_tally2: lock user account after X failed login attempts in Linux

 

Let's verify these PAM configuration. I will try to login as user3 which should be excluded as it is part of admin group:
pam_tally2: lock user account after X failed login attempts in Linux

 

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

Deepak Prasad

Deepak Prasad

He is the founder of GoLinuxCloud and brings over a decade of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels in various domains, from development to DevOps, Networking, and Security, ensuring robust and efficient solutions for diverse projects. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment