LOGINFAILURE.PL - Block IPs where failed login attempts originate
loginfailure.pl -summary # Show failing IPs
loginfailure.pl -denyip 192.168.1.4 # Block this IP
loginfailure.pl -allowip 192.168.1.4 # Stop blocking this IP
loginfailure.pl /var/log/messages # Check file for offending IPs loginfailure.pl -daemon 60 /var/log/secure
loginfailure.pl -failures 4 -daemon 60 /var/log/secure*
Many times production Unix machines cannot restrict login access by IP address. This allows crackers to attempt to break in by repeated login attempts with various passwords.
This program was written to help identify those cracking attempts and block the intruder. This is accomplished by reading the system log file, collecting a list of the IP addresses from which such login attempts were made and then adding their IP addresses to /etc/hosts.deny. After a period of time the hosts.deny is rewritten, removing the addresses which were blocked.
This program typically runs as a daemon, frequently reading the log files, updating its tables and when necessary, rewriting the hosts.deny file.
Alternatively, the program may be run as a cron entry. This latter mode, of course, only notices attacks when the cron job is run, whereas in daemon mode the program will notice the attack within a much shorter period.
The idea behind this program is that once an IP address is found to have enough login failures, you want to block it for a relatively short period (-denytime) and then later unblock the IP address (-purgetime).
If there are more login failures, the program can notice them quickly (especially in -daemon mode) and block the IP address again. If you don't eventually unblock, you will block hundreds of IP addresses which are no longer a threat, wasting processing time.
If someone tries to login from a multi-user system or a legitimate user's laptop which had a virus and is now cleaned up, you may end up blocking a legitimate user. Blocking an IP permanently just makes it very confusing and difficult to get legitimate IP addresses unblocked. By unblocking them after some period of time, you give legitimate users access again. In general the task is to balance restricting access with staying open for legitimate users.
Some systems will have very little access and only from very few places and so setting a high -denytime value and small -failures value makes sense. Other open multi-user systems will need a less restrictive approach.
The administrator should add IP addresses which should never be blocked to @IPDONOTDISABLE. The local host name IP address will normally be determined automatically, but if your machine has multiple IP addresses, you should add the correct local IP addresses to this array.
You may also specify IP addresses which should be blocked by passing them with the -ip option.
Addresses in @IPDONOTDISABLE may be specified as conventional full IP addresses (e.g. x.x.x.x) or with a pseudo 'mask' character '*'. Some examples should clarify this:
127.0.0.1 - matches only 127.0.0.1 192.168.1.* - matches 192.168.1.0 through 192.168.1.255 192.168.1*.* - matches 192.168.10.0 through 192.168.19.255 OR 192.168.100.0 through 192.168.199.255
/etc/hosts.deny must already exist, it will not be created. You should backup your original /etc/hosts.deny file.
This program keeps a simple database in /var/lib/loginfailure. The database can be removed if it is out of sync somehow. To get back in sync, run this program in non-daemon mode and let it process past security logs so it can rebuild it's database.
The following options should be reviewed and set according to the needs of your installation:
-denytime -purgetime -failures -daemon
Note that the check is not against a particular IP and userid combination, but simple against the IP address. If '-failures 4' is specified and one user attempts to login 3 times and then another fails twice, the IP address will be blocked. The number of total failures is counted within the -denytime interval.
The hosts.deny file will be appended to so you may continue to put fixed entries in this file, as usual. Entries written by this program will be appended to file so you should be careful to not modify the file after the LOGINFAILURES comment.
This program identifies failed login attempts by parsing log entries. Only a few key log entries are checked in the subroutine ReadLog(). You may find it necessary to add your own code there to check cases this program missed. If you do this, please notify the author so others can benefit.
The following is a commented example of running in daemon mode on a system which has access highly restricted:
cp -p /etc/hosts.deny /etc/hosts.deny.master
Make a copy of your original hosts.deny.
cp -p loginfailure.pl /usr/local/sbin/loginfailure.pl
Install this program.
Create a place for the database. Be sure to specify an alternative path with -db pathtofile.
Set up your system to launch this program at boot time. Obviously, you can start this as part of any other system startup script. You will like want to use the same command shown at the end of this section.
perl -MCPAN -e shell install Date::Parse
This program requires your machine have the Perl package Date::Parse installed. The commands shown here would allow you to install the Perl package yourself.
/usr/local/sbin/loginfailure.pl -denytime 4d -failures 1 -daemon 0 /var/log/secure*
The first time you should run this program on all log files of interest (e.g. /var/log/secure*) so that it finds all the past intruders who should be blocked. The database will be purged based on the -denytime TIME option. This will also show other set up errors and could modify /etc/hosts.deny.
/usr/local/sbin/loginfailure.pl -denytime 4d -failures 1 -daemon 60 /var/log/secure
Now start the program in daemon mode. Use this command in your rc.local file. Lines will be appended to the log file. Watch the log file and your security log file for problems to make sure this program finds all the improper login attempts. See also MESSAGE LOGGING.
There are only a few differences between running this program as a daemon versus running it manually or via a crontab entry. The set up is the same for both see EXAMPLE - DAEMON MODE, above with the exception that the value passed on the -daemon option should be 0 (zero) and rather than setting up rc.local, you should invoke this program via crontab.
When running in this mode, the entire security file will be parsed each time, meaning it will run longer than in daemon mode (still taking only a few seconds typically). Of course the program will only detect invalid login attempts each time it runs, so if you run this once an hour, then your system will be slower in blocking intruders. See also MESSAGE LOGGING.
The log file attempts to summarize all relevent activity. The hosts.deny file is only written when there is a change in the status of some IP address.
Each time a new IP address is to be blocked, you see a logfile entry and a new hosts.deny is created. See the option -denytime.
Each time a blocked IP address expires and is no longer to be blocked, you see a logfile entry and a new hosts.deny is created. See the option -purgetime.
A sample log file follows. Notice how IP addresses are added and later removed.
2007/01/09 08:08 loginfailure.pl now in daemon mode 2007/01/09 08:08 Using database '/var/lib/loginfailure/loginfailure.db' 2007/01/09 20:37 Now blocking '126.96.36.199' 2007/01/09 20:37 Rewrote '/etc/hosts.deny' stopping 11 IP addresses 2007/01/09 21:01 No longer blocking '188.8.131.52' 2007/01/09 21:01 Rewrote '/etc/hosts.deny' stopping 10 IP addresses 2007/01/13 18:41 Now blocking '184.108.40.206' 2007/01/13 18:41 Rewrote '/etc/hosts.deny' stopping 6 IP addresses 2007/01/13 20:37 No longer blocking '220.127.116.11' 2007/01/13 20:37 Rewrote '/etc/hosts.deny' stopping 5 IP addresses 2007/01/15 01:29 Now blocking '18.104.22.168' 2007/01/15 01:29 Rewrote '/etc/hosts.deny' stopping 8 IP addresses 2007/01/17 18:43 No longer blocking '22.214.171.124' 2007/01/17 18:43 Rewrote '/etc/hosts.deny' stopping 9 IP addresses
If no fatal errors are detected, the program exits with a return code of 0. Any error will set a non-zero return code. If running in daemon mode, the program will exit, but a child process continues to execute.
Originally written by Terry Gliedt <firstname.lastname@example.org> in 2007 and is made available under terms of the GNU General Public License.