Extra Systems Ban Software (ESBANS)

Script system for monitoring

This page fully describes the ESBANS monitoring system. Here is a list of the scripts that ensure its operation:

-rw-r--r-- 1 root   root   1417 Apr 16 16:08 esbans_win.php
-rwxr--r-- 1 root   root    336 Mar 26 23:17 fail2ban_create.sh
-rwxr--r-- 1 root   root   1366 Apr  9 17:25 fail2ban_dbpost.sh
-rw-r--r-- 1 root   root   2222 Apr 10 20:10 fail2ban_page.php
-rw-r--r-- 1 root   root   1586 Apr  9 22:31 fail2ban_stat.php
-rw-r--r-- 1 root   root    158 Apr  9 22:29 linux_db_connect.php
-rw-r--r-- 1 root   root    192 Apr 10 15:44 win_db_connect.php

To operate the monitoring system, two tasks are created in the cron (instead of "???", you need, of course, to put the correct path to the location of your scripts):

9 * * * * /???/fail2ban_dbpost.sh
55 23 * * * /???/fail2ban_create.sh

The fail2ban_create.sh file looks like this:

#!/bin/bash
current_date=$(date +%Y-%m-%d)
for module_name in $(fail2ban-client status | grep "Jail list" | cut -d: -f2 | tr -d ',')
do
        count=$(fail2ban-client status "$module_name" | grep "Currently banned" | cut -d: -f2 | tr -d '[:space:]')
        echo "$current_date $module_name $count" >> /var/log/fail2ban_daily.log
done

This script (at the end of each day) records the number of hosts blocked (at that time) by each fail2ban module in /var/log/fail2ban_daily.log . This is done simply for the sake of order (purely to maintain UNIX traditions) and is not related in any way to the operation of other ESBANS systems and modules.

The fail2ban_dbpost.sh script is called by cron every hour and has the following code (instead of "*******", you need to, of course, put your database access details):

#!/bin/bash
DB_USER=*******
DB_PASS=*******
DB_NAME=fail2ban
for fail2ban_list_item in $(fail2ban-client status | grep "Jail list" | cut -d: -f2 | tr -d '\t,')
do
	count=$(fail2ban-client status $fail2ban_list_item | grep "Currently banned" | cut -d: -f2 | tr -d '\t')
	mysql -u$DB_USER -p$DB_PASS $DB_NAME -e "INSERT IGNORE INTO ban_service (ban_service_name) VALUES ('$fail2ban_list_item');" 2>/dev/null
	mysql -u$DB_USER -p$DB_PASS $DB_NAME -e "INSERT INTO fail_ban_stat (ban_count, ban_service_id) VALUES ($count, (SELECT ban_service_id FROM ban_service WHERE ban_service_name='$fail2ban_list_item'));" 2>/dev/null
done
php /XXX/fail2ban_stat.php
php /XXX/fail2ban_page.php > /var/www/???/esbans_stat.htm

This script first documents the current state of all active fail2ban modules in the fail_ban_stat table, and then calls the fail2ban_stat.php and fail2ban_page.php scripts. In its code, you need to adjust "/var/www/???/esbans_stat.htm" to suit your specific configuration (that is, specify the correct path where it should create the statistics page), and replace "XXX" with the path where you store these scripts.

The fail2ban_stat.php script has the following code:

<?php
	include "linux_db_connect.php";
	$mysql_datetime = date("Y-m-d");
	$max_days   = 3;
	$max_months = 2;
	$main_result = mysqli_query($conn, "select ban_service_id from ban_service");
	foreach($main_result as $main_row)
	{
		$ban_service_id = $main_row["ban_service_id"];
		$step_result = mysqli_query($conn, "select AVG(ban_count) as ban_avg from fail_ban_stat where ban_service_id=$ban_service_id and ban_date > '$mysql_datetime'");
		foreach($step_result as $step_row) $ban_avg = intval($step_row["ban_avg"]);
		$step_result = mysqli_query($conn, "select count(*) as data_count from days_data where ban_service_id=$ban_service_id and ban_date='$mysql_datetime'");
		foreach($step_result as $step_row) $data_count = $step_row["data_count"];
		if ($data_count == 0)
		{
			mysqli_query($conn, "insert into days_data(ban_service_id, ban_date, ban_count) values($ban_service_id, '$mysql_datetime', $ban_avg)");
		} else {
			mysqli_query($conn, "update days_data set ban_count=$ban_avg where ban_service_id=$ban_service_id and ban_date='$mysql_datetime'");
		}
		mysqli_query($conn, "delete from fail_ban_stat where ban_service_id=$ban_service_id and ban_date < CURDATE() - INTERVAL $max_days DAY");
		mysqli_query($conn, "delete from days_data where ban_service_id=$ban_service_id and ban_date < CURDATE() - INTERVAL $max_months MONTH");
	}
?>

The essence of its operation is clear from the text. The fail_ban_stat table is essentially temporary, while the days_data table is used permanently in the ESBANS system to visualize daily statistics. This script transfers information from the first table to the second and simultaneously purges them of outdated records (according to the values ​​of the max_days and max_months parameters).

As can be seen from its code, to connect to the database, this script uses the file linux_db_connect.php, which looks like this (here, instead of "*******", you need, of course, to put your own access details to the database):

<?php
	$dbUser     = "*******";
	$dbPassword = "*******";
	$dbName     = "fail2ban";
	$conn = mysqli_connect("localhost","$dbUser","$dbPassword", "$dbName");
?>

Now let's look at the fail2ban_page.php script, which generates the culmination of all these efforts—the system statistics page esbans_stat.htm. It has the following code:

<!DOCTYPE html>
<html  lang="en"  class="no-js">
<head>
<?php
	$server_name = gethostname();
	echo "<title>$server_name | Extra Systems Ban Software</title>";
?>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>td {font-size:12px;font-family:arial}</style>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<?php
	include "linux_db_connect.php";
	$server_name = gethostname();
	echo "<h3>ESBANS Summary Statistics: Server Hacking Attempts Resisted $server_name</h3>";
	echo "<hr />";
	$mysql_datetime = date("Y-m-d");
	$show_datetime = date("d.m.Y H:i:s");
	$show_days   = 21;
	$table_height = 200;
	$item_width = 24;
	$main_result = mysqli_query($conn, "select ban_service_id, ban_service_name from ban_service");
	foreach($main_result as $main_row)
	{
		$ban_service_id   = $main_row["ban_service_id"];
		$ban_service_name = $main_row["ban_service_name"];
		echo "<h4>Statistics for module $ban_service_name</h4>";
		$max_ban_value = 1;
		$extra_result = mysqli_query($conn, "select * from days_data where ban_service_id=$ban_service_id and ban_date > CURDATE() - INTERVAL $show_days DAY ORDER BY ban_date ASC");
		foreach($extra_result as $extra_row)
		{
			$ban_date  = $extra_row["ban_date"];
			$ban_count = $extra_row["ban_count"];
			$day = date("j", strtotime($ban_date));
			$ban_array[$day] = $ban_count;
			if ($ban_count > $max_ban_value) $max_ban_value = $ban_count;
		}
		echo "<table width=\"100%\"><tr valign=\"bottom\">";
		foreach($ban_array as $key => $value)
		{
			$show_value = (int)(($value * $table_height) / $max_ban_value);
			if ($show_value == 0) $show_value = 1;
			echo "<td align=\"center\" width=\"$item_width\">$value<br><img src=\"/line.gif\" width=\"$item_width\" height=\"$show_value\"><br>$key</td>\n";
		}
		echo "</tr></table>";
		unset($ban_array);
		echo "<hr />";
	}
	include "esbans_win.php";
	echo "<table width=\"100%\"><tr><td>As of $show_datetime</td><td align=\"right\">© Extra Systems, 2026</td></tr></table>";
?>
</body>
</html>

This script's code contains a link to the line.gif file, which you should place in the directory on the server where the esbans_stat.htm statistics file is generated. You can find this file on the esbans_stat.htm page of this website and download it in your browser from any column in the histograms there.

Now let's look at the esbans_win.php file (linked to in this script). It's listed separately because you may or may not have ES-RDP. If you're using ESBANS only for fail2ban and not protecting Windows Remote Desktop, then esbans_win.php will simply be a placeholder:

<?php
?>

If ES-RDP is present on your system, then this file should have the following code:

<?php
	include "win_db_connect.php";
	$win_server_name = gethostbyaddr("$dbHost");
	echo "<h4>ES-RDP module statistics on $win_server_name</h4>";
	$ban_types = ['short' => 1, 'long' => 2, 'net' => 3, 'bot' => 4];
	$ban_names = ['short' => 'prison', 'long' => 'soldier', 'net' => 'camp', 'bot' => 'crematorium'];
	foreach ($ban_types as $key => $ban_type)
	{
		$ban_name = $ban_names[$key];
		echo "<h5>Object condition \"$ban_name\"</h5>";
		$max_ban_value = 1;
		$ban_array = [];
		$extra_result = mysqli_query($win_conn, "select * from days_data where ban_type=$ban_type and ban_date > CURDATE() - INTERVAL $show_days DAY ORDER BY ban_date ASC");
		foreach($extra_result as $extra_row)
		{
			$ban_date  = $extra_row["ban_date"];
			$ban_count = $extra_row["ban_count"];
			$day = date("j", strtotime($ban_date));
			$ban_array[$day] = $ban_count;
			if ($ban_count > $max_ban_value) $max_ban_value = $ban_count;
		}
		echo "<table width=\"100%\"><tr valign=\"bottom\">";
		foreach($ban_array as $key => $value)
		{
			$show_value = (int)(($value * $table_height) / $max_ban_value);
			if ($show_value == 0) $show_value = 1;
			echo "<td align=\"center\" width=\"$item_width\">$value<br><img src=\"/line.gif\" width=\"$item_width\" height=\"$show_value\"><br>$key</td>\n";
		}
		echo "</tr></table>";
		unset($ban_array);
	}
	echo "<hr />";
?>

The win_db_connect.php file mentioned in the code looks like this:

<?php
	$dbHost     = "*******";
	$dbUser     = "*******";
	$dbPassword = "*******";
	$dbName     = "rdp_ban";
	$win_conn   = mysqli_connect("$dbHost","$dbUser","$dbPassword", "$dbName");
?>

In this file, as usual, instead of "*******" you need to enter your own access parameters to the ES-RDP database.

And finally, here is the structure of the fail2ban database, which is necessary for this monitoring system to work:

CREATE DATABASE IF NOT EXISTS fail2ban;
USE fail2ban;

-- Service Directory
CREATE TABLE ban_service (
  ban_service_id int NOT NULL AUTO_INCREMENT,
  ban_service_name varchar(64) UNIQUE,
  PRIMARY KEY (ban_service_id)
);

-- Daily statistics table (for histograms)
CREATE TABLE days_data (
  ban_service_id int,
  ban_date date,
  ban_count int,
  KEY (ban_service_id, ban_date)
);

-- Table of operational statistics (hourly)
CREATE TABLE fail_ban_stat (
  ban_service_id int,
  ban_count int NOT NULL,
  ban_date timestamp DEFAULT CURRENT_TIMESTAMP,
  KEY (ban_service_id, ban_date)
);

The content of this page is also available in Russian.


© Extra Systems, 2026 Extra Web Top