It so happens that someone was having trouble writing a login script to run after failed attempts. And they needed not only a login script but one to run after some number of failed login attempts and to commit some arbitrary actions.
You could do that with fail2ban, sure. But in this case there’s already a native way to handle it without going outside most standard Linux installs. Because the authentication layer is handled by PAM.d
And it proved to be quite a bit harder than I had anticipated. Which, to be fair, is usually what happens when I’m looking at PAM. It’s usually an “oh, sure, PAM does that”. And then it doesn’t… quite… work. Because there’s some feature that’s not exactly obvious.
So here’s the solution I came up with. And it should work on most distributions (any that use PAM) in most instances. But it may require some modification to be secure or for your specific instance. Please keep that in mind.
And here we go! We’re going to create two scripts to call, one to do our thing and one to reset the pam tally count on successful login. Which frustratingly enough I couldn’t find a way to do internally in PAM.
I’ll start from the beginning and list out the order of changes I made for my specific situation. Remember this is Ubuntu 18.04 LTS running PAM, you may need to modify the settings for other uses, distributions, etc.
New Scripts, Changes to Existing PAM Login Scripts
/etc/pam.d/common-myscript (script to execute on failed login)
#!/bin/bash
# /etc/pam.d/common-myscript
mydate=`date`
# echo `pam_tally` >> /tmp/myoutput.txt # DEBUG
if {pam_tally | awk '{if ($5 >= "2") print $5;}'}
then
echo "SUCCEEDED test $mydate" >> /tmp/myoutput.txt
fi
/etc/pam.d/common-pam_tally_reset (script to reset pam_tally on successful login
#!/bin/bash
# /etc/pam.d/common-pam_tally_reset
# script to reset all pam_tally totals on successful
# login attempt
pam_tally --reset
After creating your scripts remember to permission them executable with “chmod +x common-pam_tally_reset common-myscript” (reminder thanks to reddit user up-sky-7)
Originally the /etc/pam.d/common-auth file read
# here are the per-package modules (the "Primary" block)
auth [success=1 default=ignore] pam_unix.so nullok_secure
# 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
# and here are more per-package modules (the "Additional" block)
auth optional pam_ecryptfs.so unwrap
# end of pam-auth-update config
And my changes to get our tally logging correctly in my bog standard install:
# here are the per-package modules (the "Primary" block)
auth [success=3 default=ignore] pam_unix.so nullok_secure
auth optional pam_tally.so per_user
auth optional pam_exec.so /etc/pam.d/common-myscript
# 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 optional pam_exec.so /etc/pam.d/common-pam_tally_reset
auth required pam_permit.so
# and here are more per-package modules (the "Additional" block)
auth optional pam_ecryptfs.so unwrap
# end of pam-auth-update config
And there you are, a simple way to log how many attempts have been made to log in and throw an error if it’s more than two. I leave figuring out doing this on a per-user basis as an exercise for the reader. Check our growing section of other linux articles.
Thanks.
My solution for ubuntu 22.04, (pam_telly is deprecated)
auth [success=3 default=ignore] pam_unix.so nullok
auth [success=2 default=ignore] pam_sss.so use_first_pass
auth optional pam_exec.so /etc/pam.d/login-failed
auth requisite pam_deny.so
auth optional pam_exec.so /etc/pam.d/login-failed-reset
auth required pam_permit.so
#!/bin/bash
FILE=/tmp/login_failed_count_$UID.txt
COUNT=0
if test -f “$FILE”; then
COUNT=$(cat $FILE)
fi
echo $((($COUNT + 1))) > $FILE
if [ $COUNT -ge 3 ]; then
shutdown now
fi
exit 0
#!/bin/bash
FILE=/tmp/login_failed_count_$UID.txt
unlink $FILE
exit 0