HEX
Server: Apache
System: Linux pdx1-shared-a4-02 6.6.104-grsec-jammy+ #3 SMP Tue Sep 16 00:28:11 UTC 2025 x86_64
User: niched (5283231)
PHP: 7.4.33
Disabled: NONE
Upload Files
File: //etc/fail2ban/action.d/ndn-fail2ban-central.pl
#!/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();