Follow through this guide to learn how to lock Linux user account after multiple failed login attempts. Linux through Pluggable Authentication Modules, PAM, can be configured to lock accounts after specific number of failed login attempts. Such are some of the security measures that aims at curbing brute force authentication attacks.
This can be achieve specifically through pam_faillock module. pam_faillock module maintains a list of failed authentication attempts per user during a specified interval and locks the account in case there were more than deny consecutive failed authentications.
“Normally, failed attempts to authenticate root user 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/ssh, etc), this is safe“, pam_faillock man page.
Lock Linux User Account after Multiple Failed Login Attempts
Files to Update
As already stated, pam_faillock
module can be used to limit the number of allowed failed logins within a specified time period on Linux systems before an account is locked.
There are a number of files which you need to edit in order to define the failed authentication policies.
These include;
On CentOS and Similar Derivatives
- /etc/pam.d/system-auth
- /etc/pam.d/password-auth
On Ubuntu/Debian and Similar Derivatives
- /etc/pam.d/common-auth
- /etc/pam.d/common-account
PAM_FAILLOCK Modules Syntax
The pam_faillock module takes the syntax below when being configured.
auth CONTROL_FLAGS pam_faillock.so {preauth|authfail|authsucc} [dir=/path/to/tally-directory] [even_deny_root] [deny=n] [fail_interval=n] [unlock_time=n] [root_unlock_time=n] [audit] [silent] [no_log_info]
account CONTROL_FLAGS pam_faillock.so [dir=/path/to/tally-directory] [no_log_info]
auth
and account
defines different PAM module interfaces:
auth
: requests and validates account passwordaccount
: verifies user account, it checks if a user account has expired or if a user is allowed to log in.
Other module interfaces include;
password
: manages user password including changing user passwords.session
: configures and manages user sessions.
In this particular guide, we are only interested in auth PAM interface.
CONTROL_FLAGS defines what to do with a failed/successful result of a PAM module. They include;
required
: The module result must be successful for authentication to continue. If the test fails at this point, the user is not notified until the results of all module tests that reference that interface are complete.requisite
: The module result must be successful for authentication to continue. However, if a test fails at this point, the user is notified immediately with a message reflecting the first failedrequired
orrequisite
module test.sufficient
: The module result is ignored if it fails. However, if the result of a module flaggedsufficient
is successful and no previous modules flaggedrequired
have failed, then no other results are required and the user is authenticated to the service.optional
: The module result is ignored. A module flagged asoptional
only becomes necessary for successful authentication when no other modules reference the interface.include
: Unlike the other controls, this does not relate to how the module result is handled. This flag pulls in all lines in the configuration file which match the given parameter and appends them as an argument to the module.
The CONTROL_FLAGS can take the format;
[value1=action1 value2=action2 ...]
Thus the above control flags can be represented as;
- required: [success=ok new_authtok_reqd=ok ignore=ignore default=bad]
- requisite: [success=ok new_authtok_reqd=ok ignore=ignore default=die]
- sufficient: [success=done new_authtok_reqd=done default=ignore]
- optional: [success=ok new_authtok_reqd=ok default=ignore]
The valueN can take one of the following forms:
- abort: Critical error (module fail now request)
- acct_expired: User account has expired
- auth_err: Authentication failure
- authinfo_unavail: Underlying authentication service can not retrieve authentication information
- authtok_err: Authentication token manipulation error
- authtok_expired: User’s authentication token has expired
- authtok_disable_aging: Authentication token aging disabled
- authtok_recover_err: Authentication information cannot be recovered
- cred_err: Failure setting user credentials
- cred_expired: User credentials expired
- cred_insufficient: Can not access authentication data due to insufficient credentials
- cred_unavail: Underlying authentication service can not retrieve user credentials unavailable
- default: all not explicitly mentioned values
- ignore: Ignore underlying account module regardless of whether the control flag is required, optional, or sufficient
- maxtries: An authentication service has maintained a retry count which has been reached. No further retries should be attempted
- module_unknown: module is not known
- new_authtok_reqd: New authentication token required. This is normally returned if the machine security policies require that the password should be changed because the password is NULL or it has aged
- perm_denied: Permission denied
- session_err: Can not make or remove an entry for the specified session
- success: Successful function return
- try_again: Preliminary check by password service
- user_unknown: User not known to the underlying authentication module
The actionN can take one of the following forms:
- ignore: when used with a stack of modules, the module’s return status will not contribute to the return code the application obtains.
- bad: this action indicates that the return code should be thought of as indicative of the module failing. If this module is the first in the stack to fail, its status value will be used for that of the whole stack.
- die: equivalent to bad with the side effect of terminating the module stack and PAM immediately returning to the application.
- ok: this tells PAM that the administrator thinks this return code should contribute directly to the return code of the full stack of modules. In other words, if the former state of the stack would lead to a return of PAM_SUCCESS, the module’s return code will override this value. Note, if the former state of the stack holds some value that is indicative of a modules failure, this ‘ok’ value will not be used to override that value.
- done: equivalent to ok with the side effect of terminating the module stack and PAM immediately returning to the application.
- reset: clear all memory of the state of the module stack and start again with the next stacked module.
Read more on man pam.conf
.
Some of the OPTIONS that you can use with auth
PAM interface are:
- {preauth|authfail|authsucc}:
preauth
: must be used when the module is called before the modules which ask for the user credentials such as the password. The module just examines whether the user should be blocked from accessing the service in case there were anomalous number of failed consecutive authentication attempts recently. This call is optional if authsucc is used.authfail
: must be used when the module is called after the modules which determine the authentication outcome, failed. Unless the user is already blocked due to previous authentication failures, the module will record the failure into the appropriate user tally file.authsucc
: must be used when the module is called after the modules which determine the authentication outcome, succeded. Unless the user is already blocked due to previous authentication failures, the module will then clear the record of the failures in the respective user tally file. Otherwise it will return authentication error. If this call is not done, the pam_faillock will not distinguish between consecutive and non-consecutive failed authentication attempts.
dir=/path/to/tally-directory
: The directory where the user files with the failure records are kept. The default is /var/run/faillock.audit
: Will log the user name into the system log if the user is not found.silent
: Don’t print informative messages. This option is mostly associated with authfail and authsucc functions.no_log_info
: Don’t log informative messages via syslog(3).deny=n
: Deny access if the number of consecutive authentication failures for this user during the recent interval exceeds n. The default is 3.fail_interval=n
: The length of the interval during which the consecutive authentication failures must happen for the user account lock out is n seconds. The default is 900 (15 minutes).unlock_time=n
: The access will be re-enabled after n seconds after the lock out. The default is 600 (10 minutes).even_deny_root
: Root account can become locked as well as regular accounts.root_unlock_time=n
: This option implies even_deny_root option. Allow access after n seconds to root account after the account is locked. In case the option is not specified the value is the same as of the unlock_time option.
Enable Account Locking after Multiple Failed Login Attempts
In order to enable account locking after a specific number of failed logins;
- On Debian Based Systems (we tried this specifically on Ubuntu 22.04 and Debian 11)
Open the /etc/pam.d/common-auth file for editing using your preferred editor;
Before that, backup the config.
cp /etc/pam.d/common-auth{,.original}
By default, this is how this file looks like with comment lines removed;
grep -Ev "^$|^#" /etc/pam.d/common-auth
auth [success=1 default=ignore] pam_unix.so nullok
auth requisite pam_deny.so
auth required pam_permit.so
auth optional pam_cap.so
The open it for editing;
vim /etc/pam.d/common-auth
and add these line;
auth required pam_faillock.so preauth audit silent deny=3 fail_interval=60 unlock_time=120
Before the line;
auth [success=1 default=ignore] pam_unix.so nullok
Then, add these lines;
auth [default=die] pam_faillock.so authfail audit deny=3 fail_interval=60 unlock_time=120
auth sufficient pam_faillock.so authsucc audit deny=3 fail_interval=60 unlock_time=120
Before the line;
auth requisite pam_deny.so
- This will cause a user account to locked for 3 consecutive failed login attempts in a period of one minute.
- The account will be temporarily locked for 2 minutes after which a user can again try to authenticate. You can adjust the timings as you wish.
The lines above should be added such that they look exactly like as shown below;
auth required pam_faillock.so preauth audit silent deny=3 fail_interval=60 unlock_time=120
auth [success=1 default=ignore] pam_unix.so nullok
auth [default=die] pam_faillock.so authfail audit deny=3 fail_interval=60 unlock_time=120
auth sufficient pam_faillock.so authsucc audit deny=3 fail_interval=60 unlock_time=120
auth requisite pam_deny.so
auth required pam_permit.so
auth optional pam_cap.so
Save and exit the file when done making changes.
Next, add the line, account required pam_faillock.so
, to the end of the /etc/pam.d/common-account
configuration file.
cp /etc/pam.d/common-account{,.original}
echo "account required pam_faillock.so" >> /etc/pam.d/common-account
- On CentOS and Similar Derivates (we tried this specifically on Rocky Linux)
Make a backup of the configuration files;
cp /etc/pam.d/system-auth{,.original}
cp /etc/pam.d/password-auth{,.original}
The two files have almost same configurations.
Thus, open each file for editing;
vim /etc/pam.d/system-auth
vim /etc/pam.d/password-auth
Under the auth section, add these lines;
auth required pam_faillock.so preauth audit silent deny=3 fail_interval=60 unlock_time=120
auth [default=die] pam_faillock.so authfail audit deny=3 fail_interval=60 unlock_time=120
Under the account section, add these lines;
account required pam_faillock.so
As shown below;
/etc/pam.d/password-auth:
auth required pam_env.so
auth required pam_faildelay.so delay=2000000
auth required pam_faillock.so preauth audit silent deny=3 fail_interval=60 unlock_time=120
auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular
auth [default=1 ignore=ignore success=ok] pam_localuser.so
auth sufficient pam_unix.so nullok try_first_pass
auth [default=die] pam_faillock.so authfail audit deny=3 fail_interval=60 unlock_time=120
auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular
auth sufficient pam_sss.so forward_pass
auth required pam_deny.so
account required pam_unix.so
account sufficient pam_localuser.so
account sufficient pam_usertype.so issystem
account [default=bad success=ok user_unknown=ignore] pam_sss.so
account required pam_permit.so
account required pam_faillock.so
/etc/pam.d/system-auth:
auth required pam_env.so
auth required pam_faildelay.so delay=2000000
auth required pam_faillock.so preauth audit silent deny=3 fail_interval=60 unlock_time=120
auth sufficient pam_fprintd.so
auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular
auth [default=1 ignore=ignore success=ok] pam_localuser.so
auth sufficient pam_unix.so nullok try_first_pass
auth [default=die] pam_faillock.so authfail audit deny=3 fail_interval=60 unlock_time=120
auth [default=1 ignore=ignore success=ok] pam_usertype.so isregular
auth sufficient pam_sss.so forward_pass
auth required pam_deny.so
account required pam_unix.so
account sufficient pam_localuser.so
account sufficient pam_usertype.so issystem
account [default=bad success=ok user_unknown=ignore] pam_sss.so
account required pam_permit.so
account required pam_faillock.so
Save the changes and exit the files.
Test User Account Lock after Multiple Failed Login Attempts
That is all set now.
You can now test failed user account logins.
First of all while logged in as another user, tail the authentication logs;
tail -f /var/log/auth.log
tail -f /var/log/secure
Perform attempted failed login, 3 times within a minute. Sample console logs in my system;
Ubuntu;
Aug 6 08:21:48 jellyfish login[51642]: pam_unix(login:auth): authentication failure; logname=LOGIN uid=0 euid=0 tty=/dev/tty1 ruser= rhost= user=kifarunix
Aug 6 08:21:52 jellyfish login[51642]: FAILED LOGIN (1) on '/dev/tty1' FOR 'kifarunix', Permission denied
Aug 6 08:21:59 jellyfish login[51642]: FAILED LOGIN (2) on '/dev/tty1' FOR 'kifarunix', Permission denied
Aug 6 08:22:03 jellyfish login[51642]: pam_faillock(login:auth): Consecutive login failures for user kifarunix account temporarily locked
Aug 6 08:22:06 jellyfish login[51642]: FAILED LOGIN (3) on '/dev/tty1' FOR 'kifarunix', Permission denied
Aug 6 08:22:11 jellyfish login[51642]: FAILED LOGIN (4) on '/dev/tty1' FOR 'kifarunix', Authentication failure
Rocky;
Aug 6 12:16:07 localhost unix_chkpwd[1333]: password check failed for user (kifarunix)
Aug 6 12:16:07 localhost login[1325]: pam_unix(login:auth): authentication failure; logname= uid=0 euid=0 tty=/dev/tty1 ruser= rhost= user=kifarunix
Aug 6 12:16:10 localhost login[1325]: FAILED LOGIN SESSION FROM tty1 FOR kifarunix, Permission denied
Aug 6 12:16:21 localhost unix_chkpwd[1339]: password check failed for user (kifarunix)
Aug 6 12:16:21 localhost login[1336]: pam_unix(login:auth): authentication failure; logname= uid=0 euid=0 tty=/dev/tty1 ruser= rhost= user=kifarunix
Aug 6 12:16:23 localhost login[1336]: FAILED LOGIN SESSION FROM tty1 FOR kifarunix, Permission denied
Aug 6 12:16:35 localhost unix_chkpwd[1342]: password check failed for user (kifarunix)
Aug 6 12:16:35 localhost login[1340]: pam_unix(login:auth): authentication failure; logname= uid=0 euid=0 tty=/dev/tty1 ruser= rhost= user=kifarunix
Aug 6 12:16:35 localhost login[1340]: pam_faillock(login:auth): Consecutive login failures for user kifarunix account temporarily locked
Aug 6 12:16:37 localhost login[1340]: FAILED LOGIN SESSION FROM tty1 FOR kifarunix, Permission denied
Aug 6 12:16:47 localhost unix_chkpwd[1352]: password check failed for user (kifarunix)
...
As you can see, at the third attempt, user account is temporarily locked.
You can also confirm using the command below;
faillock --user kifarunix
Ubuntu output;
kifarunix:
When Type Source Valid
2022-08-06 08:21:48 RHOST V
2022-08-06 08:21:55 RHOST V
2022-08-06 08:22:03 RHOST V
Rocky output;
kifarunix:
When Type Source Valid
2022-08-06 12:16:07 TTY /dev/tty1 V
2022-08-06 12:16:21 TTY /dev/tty1 V
2022-08-06 12:16:35 TTY /dev/tty1 V
You can view for all users by just running;
faillock
Sample SSH authentication logs Ubuntu;
Aug 6 08:43:05 jellyfish sshd[51672]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.56.1 user=kifarunix
Aug 6 08:43:08 jellyfish sshd[51672]: Failed password for kifarunix from 192.168.56.1 port 39310 ssh2
Aug 6 08:43:12 jellyfish sshd[51672]: Failed password for kifarunix from 192.168.56.1 port 39310 ssh2
Aug 6 08:43:14 jellyfish sshd[51672]: pam_faillock(sshd:auth): Consecutive login failures for user kifarunix account temporarily locked
Aug 6 08:43:16 jellyfish sshd[51672]: Failed password for kifarunix from 192.168.56.1 port 39310 ssh2
Aug 6 08:43:16 jellyfish sshd[51672]: Connection closed by authenticating user kifarunix 192.168.56.1 port 39310 [preauth]
Aug 6 08:43:16 jellyfish sshd[51672]: PAM 2 more authentication failures; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.56.1 user=kifarunix
Aug 6 08:43:36 jellyfish sshd[51674]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.56.1 user=kifarunix
Aug 6 08:43:38 jellyfish sshd[51674]: Failed password for kifarunix from 192.168.56.1 port 39314 ssh2
faillock --user kifarunix
kifarunix:
When Type Source Valid
2022-08-06 08:43:05 RHOST 192.168.56.1 V
2022-08-06 08:43:10 RHOST 192.168.56.1 V
2022-08-06 08:43:14 RHOST 192.168.56.1 V
Sample SSH auth logs Rocky;
Aug 6 12:21:13 localhost unix_chkpwd[1361]: password check failed for user (kifarunix)
Aug 6 12:21:13 localhost sshd[1359]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.56.1 user=kifarunix
Aug 6 12:21:15 localhost sshd[1359]: Failed password for kifarunix from 192.168.56.1 port 37388 ssh2
Aug 6 12:21:20 localhost unix_chkpwd[1362]: password check failed for user (kifarunix)
Aug 6 12:21:22 localhost sshd[1359]: Failed password for kifarunix from 192.168.56.1 port 37388 ssh2
Aug 6 12:21:26 localhost unix_chkpwd[1363]: password check failed for user (kifarunix)
Aug 6 12:21:26 localhost sshd[1359]: pam_faillock(sshd:auth): Consecutive login failures for user kifarunix account temporarily locked
Aug 6 12:21:28 localhost sshd[1359]: Failed password for kifarunix from 192.168.56.1 port 37388 ssh2
Aug 6 12:21:28 localhost sshd[1359]: Connection closed by authenticating user kifarunix 192.168.56.1 port 37388 [preauth]
Aug 6 12:21:28 localhost sshd[1359]: PAM 2 more authentication failures; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.56.1 user=kifarunix
faillock --user kifarunix
kifarunix:
When Type Source Valid
2022-08-06 12:21:13 RHOST 192.168.56.1 V
2022-08-06 12:21:20 RHOST 192.168.56.1 V
2022-08-06 12:21:26 RHOST 192.168.56.1 V
Any subsequent attempted login even with correct password will not go through, until after the time defined by the the unlock_time option, which is 2 minutes in our example.
If you want to enable unlock user account before the defined unlock time, use the command;
faillock --user kifarunix --reset
And you should be able to login again.
And that is how you can enable account locking in Linux after multiple failed login attempts.
Other Tutorials
Enforce Password Complexity Policy On CentOS 7/RHEL Derivatives
With these settings in PAM configs nothing will be listed in the output of “faillock –user username” command. Checked on Ubuntu 22.04.
Maybe you checked after the unlock_time has elapsed.
No, I’ve checked it immediately after trying to log in with wrong password.