View file File name : ndn-fail2ban-central.pl Content :#!/usr/bin/env perl # #Nightmare Labs central fail2ban database action. # #Writes and Query central fail2ban database. # use strict; use warnings; use DBI; use v5.10; use Getopt::Long qw(GetOptions); #Global Variables my ($sql, $hostname, $jail, $ip, $report, $table); #MySQL related my $db = 'failcentral'; my $host = '69.163.136.9'; my $user = 'f2b_user'; my $password = 'nightmarelabs'; my $dsn = "DBI:mysql:database=$db;host=$host"; my $dbh = DBI->connect($dsn, $user, $password); GetOptions ( "help|h" => sub { _help2() }, "report|r=i" => \$report, ) or _help(); #Usage information sub _help { print "Tool to log fail2ban actions to a database. Usage: ndn-fail2ban-central.pl [--help|-h] [--report| -r <seconds> <cluster>] <cluster> <jail name> <IP address> Options: --help|-h This help page. --report|-r <interval in seconds> <cluster> Examples: ndn-fail2ban-central.pl fail2ban_loadbalancer ssh-bruteforce 1.2.3.4 ndn-fail2ban-central.pl --report|-r 60 (Shows entries from the last 60 seconds) fail2ban_loadbalancer\n"; exit 0; } #Running report if ($report) { ($table) = @ARGV; query_fail2ban($report, $table); exit 0; } sub query_fail2ban{ ($report, $table) = @_; my $sql = "SELECT date, hostname, jail, ip from $table where date>DATE_ADD(NOW(), interval -$report SECOND)"; my $sth = $dbh->prepare($sql); $sth->execute(); while (my @row = $sth->fetchrow_array()){ print "@row\n"; } $sth->finish(); } sub _insert { ($table, $hostname, $jail, $ip) = @_; my $sql = "INSERT INTO $table set hostname='$hostname', jail='$jail',ip='$ip', date=NOW()"; my $fail_data = _get_fail_data(); $sql .= ", fail_data='$fail_data'" if $fail_data; my $sth = $dbh->prepare($sql); $sth->execute(); $sth->finish(); } sub _get_fail_data { my $users_ref = _process_log(); if (!$users_ref) { my $second_attempt = 1; $users_ref = _process_log($second_attempt); return if !$users_ref; } my %users = %$users_ref; my $fail_data; # TODO: for jails like wp and 418, we probably want to do something else.. figure that out later. # user with the highest fail count should be first since the list length is max 15 users. my @sorted_users = sort { $users{$a} <=> $users{$b} } keys %users; my $i = 0; for my $user (reverse @sorted_users) { $fail_data .= "$user:$users{$user},"; $i++; last if $i > 14; # cutting it off here else the list would be truncated in the db. } return $fail_data; } sub _process_log { my ($second_attempt) = @_; # TODO: we're always going to try a second attempt for the other jails, like ssh, ftp, etc. add those later. # TODO: put these regex's somewhere? my $config = { 'dovecot' => { log_file => '/var/log/auth.log', fail_regex => 'dovecot.*fail', user_regex => 'ruser=(.*) rhost', }, 'postfix-sasl' => { log_file => '/var/log/auth.log', fail_regex => 'dovecot.*fail', user_regex => 'ruser=(.*) rhost', }, }; my $log = $config->{$jail}->{log_file}; $log .= '.1' if $second_attempt; my $fail_re = $config->{$jail}->{fail_regex}; my $user_re = $config->{$jail}->{user_regex}; if ($log && $fail_re && $user_re) { open my $fh, '<', $log || return; my %users; while (<$fh>) { my $line = $_; chomp($line); next unless $line =~ /$ip/; next unless $line =~ /$fail_re/; my ($user) = $line =~ /$user_re/; next unless $user; $users{$user}++; } close $fh; return \%users if keys %users > 0; } return; } sub main { ($table, $jail, $ip) = @ARGV; $hostname = `hostname`; #if required values are missing exit if ($table and $jail and $ip) { chomp ($table, $hostname, $jail, $ip); } else { print "Valid options required\n"; _help(); } if ($ip =~ m/^(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)$/) { } else { print "Valid IP address required\n"; _help(); } _insert($table, $hostname, $jail, $ip); } main();