Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions Backend/fail2ban_log2json.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/bin/bash
# This is the Logfile-Reader for the local installation - so you will have to edit the OUTPUT_JSON_DIR to fit your Webserver Installation
# This is the Logfile-Reader for the local installation
# You have to edit the OUTPUT_JSON_DIR to fit your Webserver Installation
#
LOGFILE="/var/log/fail2ban.log"
OUTPUT_JSON_DIR="/var/www/html/Fail2Ban-Report/archive/<SERVERNAME>/fail2ban"
Expand All @@ -9,27 +10,35 @@ TODAY=$(date +"%Y-%m-%d")
OUTPUT_JSON_FILE="$OUTPUT_JSON_DIR/fail2ban-events-$(date +"%Y%m%d").json"
mkdir -p "$OUTPUT_JSON_DIR"

# IPv4 regex: nnn.nnn.nnn.nnn (0-255 simplified to 0-999 for awk)
IPv4='([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'
# IPv6 regex: blocks of hex split by collon, allowed "::" short format
IPv6='(([0-9A-Fa-f]{1,4}:){1,7}[0-9A-Fa-f]{1,4}|([0-9A-Fa-f]{1,4}:){1,7}:|:([0-9A-Fa-f]{1,4}:){1,7}[0-9A-Fa-f]{1,4}|::)'
# complete IPv4 and IPv6 pattern
IP_PATTERN="($IPv4|$IPv6)"

echo "[" > "$OUTPUT_JSON_FILE"

# Grep all relevant Events
grep -E "(Ban|Unban)" "$LOGFILE" | awk -v today="$TODAY" '
grep -E "(Ban|Unban)" "$LOGFILE" | awk -v today="$TODAY" -v ip_pattern="$IP_PATTERN" '
{
timestamp = $1 " " $2;
if (index(timestamp, today) != 1) next;

action = "";
ip = "";

if ($0 ~ /Increase Ban/) {
action = "Increase Ban";
match($0, /Increase Ban ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/, m);
match($0, action " " ip_pattern, m);
if (m[1]) ip = m[1];
} else if ($0 ~ /Ban/) {
action = "Ban";
match($0, /Ban ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/, m);
match($0, action " " ip_pattern, m);
if (m[1]) ip = m[1];
} else if ($0 ~ /Unban/) {
action = "Unban";
match($0, /Unban ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/, m);
match($0, action " " ip_pattern, m);
if (m[1]) ip = m[1];
}

Expand Down
2 changes: 1 addition & 1 deletion Web-UI/assets/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ tr:nth-child(even) {
font-family: monospace;
font-size: 14px;
opacity: 0.95;
animation: fadein 0.3s ease-out, fadeout 0.5s ease-in 4s forwards;
animation: fadein 0.3s ease-out, fadeout 0.5s ease-in 20s forwards;
}

@keyframes fadein {
Expand Down
2 changes: 1 addition & 1 deletion Web-UI/assets/js/action-collector.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function collectAndExecuteActions(ips, action, jails = []) {
const prefix = `[${action.toUpperCase()}] `;
const message = data.message || 'No message returned.';
const type = data.type || (data.success ? 'success' : 'error');
const duration = (action === 'report') ? 7000 : 7000;
const duration = (action === 'report') ? 25000 : 7000;
showNotification(prefix + message, type, duration);
})
.catch(err => {
Expand Down
30 changes: 30 additions & 0 deletions Web-UI/assets/js/ufw-report.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
document.addEventListener('DOMContentLoaded', () => {
async function updateUFWBlocks() {
const div = document.getElementById('ufw-blocks-info');
if (!div) return;

try {
const response = await fetch('includes/ufw-report.php');
if (!response.ok) throw new Error(`Network response was not ok`);
const data = await response.json();

// Gesamt
let output = `Total Matches: ${data.total}`;

// Per IP
if (data.per_ip && Object.keys(data.per_ip).length > 0) {
const ipInfo = Object.entries(data.per_ip).map(([ip, info]) => {
return `${info.blocklist}: ${ip} (${info.count})`;
});
output += ' | ' + ipInfo.join(' | ');
}

div.textContent = output;
} catch (err) {
console.error('Error fetching UFW blocklist data:', err);
div.textContent = '⚠ Error loading UFW blocklist info';
}
}

updateUFWBlocks();
});
2 changes: 1 addition & 1 deletion Web-UI/includes/actions/action_ban-ip.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// includes/actions/action_ban-ip.php

header('Content-Type: application/json; charset=utf-8');
require_once __DIR__ . '/../block-ip.php';
require_once dirname(__DIR__) . '/block-ip.php';

// Check if IP(s) were provided
if (!isset($_POST['ip'])) {
Expand Down
12 changes: 6 additions & 6 deletions Web-UI/includes/actions/action_report-ip.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@

header('Content-Type: application/json; charset=utf-8');

$config = parse_ini_file('/opt/Fail2Ban-Report/Settings/fail2ban-report.config');
// Read config to flat array $infoConfig (to avoid overwrite from included $script)
$infoConfig = parse_ini_file('/opt/Fail2Ban-Report/Settings/fail2ban-report.config');

$ips = $_POST['ip'] ?? null;
if (!$config['report'] || !$config['report_types'] || !$ips) {
if (!$infoConfig['report'] || !$infoConfig['report_types'] || !$ips) {
echo json_encode([
'success' => false,
'message' => 'Reporting not enabled or invalid IP(s).',
Expand All @@ -19,7 +20,7 @@
$ips = [$ips]; // Convert single IP to array
}

$services = array_map('trim', explode(',', $config['report_types']));
$services = array_map('trim', explode(',', $infoConfig['report_types']));
$results = [];
$allMessages = [];
$overallSuccess = true;
Expand All @@ -34,7 +35,7 @@
$script = __DIR__ . "/reports/$service.php";

// Check API keys for services that require them
if ($service === 'abuseipdb' && empty($config['abuseipdb_key'])) {
if ($service === 'abuseipdb' && empty($infoConfig['abuseipdb_key'])) {
$ipSuccess = false;
$reportResults[$service] = [
'success' => false,
Expand All @@ -43,8 +44,7 @@
];
$messages[] = "[$service] API key missing";
continue;
}
if ($service === 'ipinfo' && empty($config['ipinfo_key'])) {
} elseif ($service === 'ipinfo' && empty($infoConfig['ipinfo_key'])) {
$ipSuccess = false;
$reportResults[$service] = [
'success' => false,
Expand Down
2 changes: 1 addition & 1 deletion Web-UI/includes/actions/action_unban-ip.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

header('Content-Type: application/json; charset=utf-8');

require_once __DIR__ . '/../unblock-ip.php';
require_once dirname(__DIR__) . '/unblock-ip.php';

// Validate input
if (!isset($_POST['ip']) || !isset($_POST['jail'])) {
Expand Down
2 changes: 1 addition & 1 deletion Web-UI/includes/actions/reports/abuseipdb.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
// includes/actions/reports/abuseipdb.php

require_once __DIR__ . '/../paths.php';
require_once dirname(__DIR__, 2) . '/paths.php';

// Config laden
$config = parse_ini_file($PATHS['config'] . "fail2ban-report.config", true);
Expand Down
12 changes: 10 additions & 2 deletions Web-UI/includes/actions/reports/ipinfo.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
// includes/actions/reports/ipinfo.php

require_once __DIR__ . '/../paths.php';
require_once dirname(__DIR__, 2) . '/paths.php';

// Config laden
$config = parse_ini_file($PATHS['config'] . "fail2ban-report.config", true);
Expand Down Expand Up @@ -60,7 +60,15 @@
return;
}

$msg = "IPInfo: {$json['ip'] ?? 'unknown'} - Hostname: {$json['hostname'] ?? 'N/A'}, Location: {$json['city'] ?? 'N/A'}, {$json['region'] ?? 'N/A'}, {$json['country'] ?? 'N/A'}, Org: {$json['org'] ?? 'N/A'}";
$msg = sprintf(
"IPInfo: %s - Hostname: %s, Location: %s, %s, %s, Org: %s",
$json['ip'] ?? 'unknown',
$json['hostname'] ?? 'N/A',
$json['city'] ?? 'N/A',
$json['region'] ?? 'N/A',
$json['country'] ?? 'N/A',
$json['org'] ?? 'N/A'
);

echo json_encode([
'success' => true,
Expand Down
7 changes: 4 additions & 3 deletions Web-UI/includes/header.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<title>Fail2Ban Report</title>
<meta name="viewport" content="width=device-width, initial-scale=0.8">
<link rel="stylesheet" href="assets/css/style.css" />
<link rel="icon" href="assets/css/favicon-32x32.png" type="image/png">
<link rel="icon" href="assets/images/favicon1.png" type="image/png">
<script>
const availableFiles = <?php echo $filesJson; ?>;
</script>
Expand Down Expand Up @@ -64,7 +64,7 @@


<!-- Log in/out -->

<?php if (!isset($_SESSION['username'])) : ?>
<div>
<form method="post" action="">
<small>
Expand All @@ -76,12 +76,13 @@
<button class="button-reset" type="submit">Login</button>
</form>
</div>
<?php else : ?>
<div>
<form method="post" action="">
<button class="button-reset" type="submit" name="logout" value="1">Logout</button>
</form>
</div>

<?php endif; ?>
<!-- Log in/out -->


Expand Down
7 changes: 4 additions & 3 deletions Web-UI/includes/list-files.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<?php

// Path to the config file
$configPath = '/opt/Fail2Ban-Report/fail2ban-report.config';
// includes/list-files.php

require_once __DIR__ . "/paths.php";

// Path to the config file
$configPath = $PATHS['config'] . 'fail2ban-report.config';

// Read config file and parse the [Fail2Ban-Daily-List-Settings] section
$config = [];
if (file_exists($configPath)) {
Expand Down
2 changes: 1 addition & 1 deletion Web-UI/includes/paths.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
$CONFIG_ROOT = "/opt/Fail2Ban-Report/Settings/";

// Basispfad
$ARCHIVE_ROOT = __DIR__ . "/../archive/";
$ARCHIVE_ROOT = dirname(__DIR__) . "/archive/";

// Serverliste automatisch aus archive/ generieren
$SERVERS = [];
Expand Down
2 changes: 1 addition & 1 deletion Web-UI/includes/warnings.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
require_once __DIR__ . "/paths.php";

// read Config
$configPath = '/opt/Fail2Ban-Report/fail2ban-report.config';
$configPath = '/opt/Fail2Ban-Report/Settings/fail2ban-report.config';
if (!file_exists($configPath)) {
echo json_encode(['status' => 'disabled', 'reason' => 'No config found']);
exit;
Expand Down