Limiting how many times an account can fail to authenticate before being blocked is a core hardening control on Linux servers. Account lockout policies reduce the window for brute‑force password guessing on services like SSH, console login, and remote administration tools, especially when hosts are reachable from untrusted networks.
Linux distributions implement this control through Pluggable Authentication Modules (PAM) using the pam_faillock module and the /etc/security/faillock.conf configuration file. pam_faillock tracks failed authentication attempts per user in a tally directory and denies access once a configurable threshold of consecutive failures is reached, integrating with the auth and account phases of the PAM stack.
Incorrect PAM ordering or thresholds that are too strict can lock legitimate users out of critical systems, including administrative accounts, so configuration changes must be applied cautiously and tested on non‑privileged users first. On Ubuntu and Debian, enabling pam_faillock in /etc/pam.d/common-auth and /etc/pam.d/common-account applies the policy to most login paths, while values in /etc/security/faillock.conf tune how aggressively failures are counted and how long accounts remain locked.
$ whoami user
$ sudo -i whoami root
Misordered PAM entries can block all interactive logins, so a spare root session is essential for restoring backups if authentication fails.
$ sudo cp /etc/pam.d/common-auth /etc/pam.d/common-auth.backup $ sudo cp /etc/pam.d/common-account /etc/pam.d/common-account.backup
Backups allow quick rollback by copying the saved files back into place from the recovery shell if logins stop working.
$ printf '%s\n' '# /etc/security/faillock.conf' 'deny = 5' 'fail_interval = 900' 'unlock_time = 600' | sudo tee /etc/security/faillock.conf # /etc/security/faillock.conf deny = 5 fail_interval = 900 unlock_time = 600
/etc/security/faillock.conf is the preferred location for pam_faillock options on modern Linux systems.
# /etc/security/faillock.conf deny = 5 fail_interval = 900 unlock_time = 600
deny is the number of consecutive failures allowed, fail_interval is the window in seconds in which those failures are counted, and unlock_time is the lock duration in seconds.
$ sudo sed -n '1,30p' /etc/pam.d/common-auth # # /etc/pam.d/common-auth - authentication settings common to all services # # This file is included from other service-specific PAM config files, # and should contain a list of the authentication modules that define # the central authentication scheme for use on the system # (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the # traditional Unix authentication mechanisms. # # As of pam 1.0.1-6, this file is managed by pam-auth-update by default. # To take advantage of this, it is recommended that you configure any # local modules either before or after the default block, and use # pam-auth-update to manage selection of other modules. See # pam-auth-update(8) for details. # here are the per-package modules (the "Primary" block) auth required pam_faillock.so preauth auth [success=3 default=ignore] pam_unix.so nullok auth [default=die] pam_faillock.so authfail # here's the fallback if no module succeeds auth requisite pam_deny.so # prime the stack with a positive return value if there isn't one already; # this avoids us returning an error just because nothing sets a success code # since the modules above will each just jump around auth required pam_permit.so auth sufficient pam_faillock.so authsucc # and here are more per-package modules (the "Additional" block) auth optional pam_cap.so # end of pam-auth-update config
Only change the auth block for local password authentication and leave distribution‑managed comments and include statements intact to avoid breaking other authentication methods.
$ sudo sed -n '1,30p' /etc/pam.d/common-account # # /etc/pam.d/common-account - authorization settings common to all services # # This file is included from other service-specific PAM config files, # and should contain a list of the authorization modules that define # the central access policy for use on the system. The default is to # only deny service to users whose accounts are expired in /etc/shadow. # # As of pam 1.0.1-6, this file is managed by pam-auth-update by default. # To take advantage of this, it is recommended that you configure any # local modules either before or after the default block, and use # pam-auth-update to manage selection of other modules. See # pam-auth-update(8) for details. # # here are the per-package modules (the "Primary" block) account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so # here's the fallback if no module succeeds account requisite pam_deny.so # prime the stack with a positive return value if there isn't one already; # this avoids us returning an error just because nothing sets a success code # since the modules above will each just jump around account required pam_permit.so account required pam_faillock.so # and here are more per-package modules (the "Additional" block) # end of pam-auth-update config
Using the required control flag ensures that account evaluation fails when the tally indicates the user is locked.
Keep the backup file until the configuration has been validated across all login paths used on the system.
$ sudo faillock --user audituser --reset
$ for attempt in $(seq 1 5); do sshpass -p "badpass" ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no -o StrictHostKeyChecking=accept-new audituser@localhost; done Pseudo-terminal will not be allocated because stdin is not a terminal. Permission denied, please try again. Pseudo-terminal will not be allocated because stdin is not a terminal. Permission denied, please try again. Pseudo-terminal will not be allocated because stdin is not a terminal. Permission denied, please try again. Pseudo-terminal will not be allocated because stdin is not a terminal. Permission denied, please try again. Pseudo-terminal will not be allocated because stdin is not a terminal. Permission denied, please try again.
Using a dedicated test account avoids unintentionally locking an administrator out of the system during validation.
$ sudo faillock --user audituser audituser: When Type Source Valid 2026-01-13 01:11:06 RHOST ::1 V 2026-01-13 01:11:09 RHOST ::1 V 2026-01-13 01:11:13 RHOST ::1 V 2026-01-13 01:11:17 RHOST ::1 V 2026-01-13 01:11:20 RHOST ::1 V
faillock reads per‑user tally files from the failure directory and shows recent invalid attempts that contribute to the lockout state.
$ sudo faillock --user audituser --reset $ sudo faillock --user audituser audituser: When Type Source Valid
Use the reset command to clear the failure count after investigating suspicious activity so that legitimate users can log in again.