Set Lockout Time for Failed Password Attempts
Description
This rule configures the system to lock out accounts during a specified time period after a
number of incorrect login attempts using pam_faillock.so
.
pam_faillock.so module requires multiple entries in pam files. These entries must be carefully
defined to work as expected. In order to avoid any errors when manually editing these files,
it is recommended to use the appropriate tools, such as authselect
or authconfig
,
depending on the OS version.
If unlock_time
is set to 0
, manual intervention by an administrator is required
to unlock a user. This should be done using the faillock
tool.
Rationale
By limiting the number of failed logon attempts the risk of unauthorized system
access via user password guessing, otherwise known as brute-forcing, is reduced.
Limits are imposed by locking the account.
Shell script
The following script can be run on the host to remediate the issue.
# Remediation is applicable only in certain platforms
if dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q installed; then
var\_accounts\_passwords\_pam\_faillock\_unlock\_time='600'
if [ -f /usr/bin/authselect ]; then
if ! authselect check; then
echo "
authselect integrity check failed. Remediation aborted!
This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
It is not recommended to manually edit the PAM files when authselect tool is available.
In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
exit 1
fi
authselect enable-feature with-faillock
authselect apply-changes -b
else
pam\_file="/etc/pam.d/common-auth"
if ! grep -qE '^\s\*auth\s+required\s+pam\_faillock\.so\s+preauth.\*$' "$pam\_file" ; then
sed -i --follow-symlinks '/^auth.\*pam\_unix\.so.\*/i auth required pam\_faillock.so preauth' "$pam\_file"
fi
if ! grep -qE '^\s\*auth\s+\[default=die\]\s+pam\_faillock\.so\s+authfail.\*$' "$pam\_file" ; then
sed -i --follow-symlinks '/^auth.\*pam\_unix\.so.\*/a auth [default=die] pam\_faillock.so authfail' "$pam\_file"
fi
if ! grep -qE '^\s\*auth\s+sufficient\s+pam\_faillock\.so\s+authsucc.\*$' "$pam\_file" ; then
sed -i --follow-symlinks '/^auth.\*pam\_faillock\.so.\*authfail.\*/a auth sufficient pam\_faillock.so authsucc' "$pam\_file"
fi
pam\_file="/etc/pam.d/common-account"
if ! grep -qE '^\s\*account\s+required\s+pam\_faillock\.so.\*$' "$pam\_file" ; then
echo 'account required pam\_faillock.so' >> "$pam\_file"
fi
fi
AUTH\_FILES=("/etc/pam.d/common-auth" "/etc/pam.d/password-auth")
FAILLOCK\_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK\_CONF ]; then
regex="^\s\*unlock\_time\s\*="
line="unlock\_time = $var\_accounts\_passwords\_pam\_faillock\_unlock\_time"
if ! grep -q $regex $FAILLOCK\_CONF; then
echo $line >> $FAILLOCK\_CONF
else
sed -i --follow-symlinks 's|^\s\*\(unlock\_time\s\*=\s\*\)\(\S\+\)|\1'"$var\_accounts\_passwords\_pam\_faillock\_unlock\_time"'|g' $FAILLOCK\_CONF
fi
for pam\_file in "${AUTH\_FILES[@]}"
do
if [ -e "$pam\_file" ] ; then
PAM\_FILE\_PATH="$pam\_file"
if [ -f /usr/bin/authselect ]; then
if ! authselect check; then
echo "
authselect integrity check failed. Remediation aborted!
This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
It is not recommended to manually edit the PAM files when authselect tool is available.
In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
exit 1
fi
CURRENT\_PROFILE=$(authselect current -r | awk '{ print $1 }')
# If not already in use, a custom profile is created preserving the enabled features.
if [[ ! $CURRENT\_PROFILE == custom/\* ]]; then
ENABLED\_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
authselect create-profile hardening -b $CURRENT\_PROFILE
CURRENT\_PROFILE="custom/hardening"
authselect apply-changes -b --backup=before-hardening-custom-profile
authselect select $CURRENT\_PROFILE
for feature in $ENABLED\_FEATURES; do
authselect enable-feature $feature;
done
authselect apply-changes -b --backup=after-hardening-custom-profile
fi
PAM\_FILE\_NAME=$(basename "$pam\_file")
PAM\_FILE\_PATH="/etc/authselect/$CURRENT\_PROFILE/$PAM\_FILE\_NAME"
authselect apply-changes -b
fi
if grep -qP '^\s\*auth\s.\*\bpam\_faillock.so\s.\*\bunlock\_time\b' "$PAM\_FILE\_PATH"; then
sed -i -E --follow-symlinks 's/(.\*auth.\*pam\_faillock.so.\*)\bunlock\_time\b=?[[:alnum:]]\*(.\*)/\1\2/g' "$PAM\_FILE\_PATH"
fi
if [ -f /usr/bin/authselect ]; then
authselect apply-changes -b
fi
else
echo "$pam\_file was not found" >&2
fi
done
else
for pam\_file in "${AUTH\_FILES[@]}"
do
if ! grep -qE '^\s\*auth.\*pam\_faillock\.so (preauth|authfail).\*unlock\_time' "$pam\_file"; then
sed -i --follow-symlinks '/^auth.\*required.\*pam\_faillock\.so.\*preauth.\*silent.\*/ s/$/ unlock\_time='"$var\_accounts\_passwords\_pam\_faillock\_unlock\_time"'/' "$pam\_file"
sed -i --follow-symlinks '/^auth.\*required.\*pam\_faillock\.so.\*authfail.\*/ s/$/ unlock\_time='"$var\_accounts\_passwords\_pam\_faillock\_unlock\_time"'/' "$pam\_file"
else
sed -i --follow-symlinks 's/\(^auth.\*required.\*pam\_faillock\.so.\*preauth.\*silent.\*\)\('"unlock\_time"'=\)[0-9]\+\(.\*\)/\1\2'"$var\_accounts\_passwords\_pam\_faillock\_unlock\_time"'\3/' "$pam\_file"
sed -i --follow-symlinks 's/\(^auth.\*required.\*pam\_faillock\.so.\*authfail.\*\)\('"unlock\_time"'=\)[0-9]\+\(.\*\)/\1\2'"$var\_accounts\_passwords\_pam\_faillock\_unlock\_time"'\3/' "$pam\_file"
fi
done
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Warning
If the system supports the new /etc/security/faillock.conf
file but the
pam_faillock.so parameters are defined directly in /etc/pam.d/system-auth
and
/etc/pam.d/password-auth
, the remediation will migrate the unlock_time
parameter
to /etc/security/faillock.conf
to ensure compatibility with authselect
tool.
The parameters deny
and fail_interval
, if used, also have to be migrated
by their respective remediation.