Skip to content

Commit 56539d1

Browse files
authored
V 0.3.4
🆕 Fix V 0.3.4 Json Files should not loose Data anymore when several write processes trying to write the json file at the same time. firewall-update.sh: Added FLOCK to lock json when writing block-ip.php: Added FLOCK to lock json when writing unblock-ip.php: Added FLOCK to lock json when writing
2 parents dffa5bd + 10ae74f commit 56539d1

File tree

6 files changed

+177
-100
lines changed

6 files changed

+177
-100
lines changed

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Fail2Ban-Report
2-
> Beta 3.3 | Version 0.3.3
2+
> Beta 3.4 | Version 0.3.4
33
44
> A simple and clean web-based dashboard to turn your daily Fail2Ban logs into searchable and filterable JSON reports — with optional IP blocklist management for UFW.
55
@@ -78,6 +78,16 @@ It provides optional tools to:
7878
7979
---
8080

81+
## 🆕 Fix V 0.3.4
82+
Json Files should not loose Data anymore when several write processes trying to write the json file at the same time.
83+
84+
- `firewall-update.sh`: Added FLOCK to lock json when writing
85+
- `block-ip.php`: Added FLOCK to lock json when writing
86+
- `unblock-ip.php`: Added FLOCK to lock json when writing
87+
88+
just copy those 3 Files to apply the hotfix.
89+
90+
8191
## 🆕 What's New in V 0.3.3 (QoL Update)
8292
### ⚠️ Warning System and Pending Status Indicators
8393
- 🚨 New [Warnings] section in .config to configure warning & critical thresholds (events per minute per jail) in format warning:critical (e.g: 20:50).
@@ -138,6 +148,7 @@ This is especially useful if you want to manually patch or update individual fil
138148
-**Blocklist Path on unblocking** fixed a possible bug that could lead to not finding the blocklist.json when unblocking from the Blocklist view.
139149
→ Hotfixed on 05.08.2025 at 13:10 (UTC+2) directly in latest
140150
-**Installer** should now ask if you want to delete and reclone repo when allready existing
151+
-**Added FLOCK** to lock json files to not loose data when several write processes write at the same time
141152

142153
---
143154

changelog.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# changelog
22

3+
## Changes made for V 0.3.4 (Fix)
4+
5+
Json Files should not loose Data anymore when several write processes trying to write the json file at the same time.
6+
7+
- `firewall-update.sh`: Added FLOCK to lock json when writing
8+
- `block-ip.php`: Added FLOCK to lock json when writing
9+
- `unblock-ip.php`: Added FLOCK to lock json when writing
10+
11+
12+
313
## Changes made for V 0.3.3 (QoL Update)
414

515
- **Warning System and Pending Status Indicators**

firewall-update.sh

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
set -euo pipefail
44

55
# --- Configuration ---
6-
BLOCKLIST_DIR="/var/www/vhosts/suble.org/xbkupx/Fail2Ban-Report/archive"
7-
LOGFILE="/opt/Fail2Ban-Report/fail2ban_blocklist.log"
8-
LOGGING=false # Set to true to enable logging
6+
BLOCKLIST_DIR="/path/to/web/archive"
7+
LOGFILE="/var/log/Fail2Ban-Report.log"
8+
LOGGING=true # Set to true to enable logging
99

1010
# --- Set PATH ---
1111
export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
@@ -36,8 +36,18 @@ ufw status numbered | grep "DENY IN" | awk '{print $3}' > "$TMP_BLOCKED" || true
3636
for FILE in "$BLOCKLIST_DIR"/*.blocklist.json; do
3737
[ -e "$FILE" ] || continue # skip if no files match
3838

39+
JAIL_NAME=$(basename "$FILE" .blocklist.json)
40+
LOCKFILE="/tmp/${JAIL_NAME}.blocklist.lock"
41+
3942
log "Processing blocklist: $FILE"
4043

44+
# === Acquire lock ===
45+
exec {lock_fd}>"$LOCKFILE"
46+
if ! flock -x "$lock_fd"; then
47+
log "ERROR: Could not acquire lock for $JAIL_NAME"
48+
continue
49+
fi
50+
4151
# Extract active and inactive IPs
4252
active_ips=$(jq -r '.[] | select(.active != false) | .ip' "$FILE")
4353
inactive_ips=$(jq -r '.[] | select(.active == false) | .ip' "$FILE")
@@ -48,9 +58,9 @@ for FILE in "$BLOCKLIST_DIR"/*.blocklist.json; do
4858
log "Blocking IP: $ip"
4959
if ufw deny from "$ip"; then
5060
log "Blocked $ip successfully, updating pending flag"
51-
# Update pending to false for this IP in JSON
5261
tmp_file=$(mktemp)
53-
jq --arg ip "$ip" 'map(if .ip == $ip then .pending = false else . end)' "$FILE" > "$tmp_file" && mv "$tmp_file" "$FILE"
62+
jq --arg ip "$ip" 'map(if .ip == $ip then .pending = false else . end)' "$FILE" > "$tmp_file" \
63+
&& mv "$tmp_file" "$FILE"
5464
else
5565
log "Failed to block $ip via ufw"
5666
fi
@@ -59,7 +69,6 @@ for FILE in "$BLOCKLIST_DIR"/*.blocklist.json; do
5969

6070
# Remove UFW rules for inactive IPs
6171
for ip in $inactive_ips; do
62-
# Reverse order to avoid shifting rule numbers
6372
mapfile -t rules < <(ufw status numbered | grep "$ip" | grep "DENY IN" | tac)
6473
for rule in "${rules[@]}"; do
6574
rule_number=$(echo "$rule" | awk -F'[][]' '{print $2}')
@@ -75,6 +84,10 @@ for FILE in "$BLOCKLIST_DIR"/*.blocklist.json; do
7584
# Set ownership and permissions
7685
chown www-data:www-data "$FILE"
7786
chmod 644 "$FILE"
87+
88+
# === Release lock ===
89+
flock -u "$lock_fd"
90+
exec {lock_fd}>&-
7891
done
7992

8093
log "All blocklists processed successfully."

includes/block-ip.php

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,43 @@ function blockIp($ips, $jail = 'unknown', $source = 'manual') {
3737
}
3838

3939
$jsonFile = dirname(__DIR__, 1) . "/archive/{$safeJail}.blocklist.json";
40+
$lockFile = "/tmp/{$safeJail}.blocklist.lock"; // same lock file name as shell script
4041

41-
$data = [];
42+
// Open lock file
43+
$lockHandle = fopen($lockFile, 'c');
44+
if (!$lockHandle) {
45+
$results[] = [
46+
'ip' => $ip,
47+
'success' => false,
48+
'message' => "Unable to open lock file for {$safeJail}.",
49+
'type' => 'error'
50+
];
51+
continue;
52+
}
53+
54+
// Acquire exclusive lock
55+
if (!flock($lockHandle, LOCK_EX)) {
56+
fclose($lockHandle);
57+
$results[] = [
58+
'ip' => $ip,
59+
'success' => false,
60+
'message' => "Could not acquire lock for {$safeJail}.",
61+
'type' => 'error'
62+
];
63+
continue;
64+
}
4265

66+
// Load existing JSON
67+
$data = [];
4368
if (file_exists($jsonFile)) {
4469
$existing = file_get_contents($jsonFile);
4570
$data = json_decode($existing, true);
4671
if (!is_array($data)) {
47-
$data = []; // fallback bei Dateibeschädigung
72+
$data = []; // fallback if file is corrupted
4873
}
4974
}
5075

51-
// Prüfen, ob IP schon vorhanden und aktiv
76+
// Check if IP already exists
5277
$found = false;
5378
foreach ($data as &$item) {
5479
if ($item['ip'] === $ip) {
@@ -57,6 +82,8 @@ function blockIp($ips, $jail = 'unknown', $source = 'manual') {
5782
$item['active'] = true;
5883
$item['lastModified'] = date('c');
5984
if (file_put_contents($jsonFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) === false) {
85+
flock($lockHandle, LOCK_UN);
86+
fclose($lockHandle);
6087
$results[] = [
6188
'ip' => $ip,
6289
'success' => false,
@@ -65,6 +92,8 @@ function blockIp($ips, $jail = 'unknown', $source = 'manual') {
6592
];
6693
continue 2;
6794
}
95+
flock($lockHandle, LOCK_UN);
96+
fclose($lockHandle);
6897
$results[] = [
6998
'ip' => $ip,
7099
'success' => true,
@@ -73,6 +102,8 @@ function blockIp($ips, $jail = 'unknown', $source = 'manual') {
73102
];
74103
continue 2;
75104
} else {
105+
flock($lockHandle, LOCK_UN);
106+
fclose($lockHandle);
76107
$results[] = [
77108
'ip' => $ip,
78109
'success' => true,
@@ -85,7 +116,7 @@ function blockIp($ips, $jail = 'unknown', $source = 'manual') {
85116
}
86117
unset($item);
87118

88-
// Neu hinzufügen
119+
// Add new entry if not found
89120
if (!$found) {
90121
$entry = [
91122
'ip' => $ip,
@@ -102,6 +133,8 @@ function blockIp($ips, $jail = 'unknown', $source = 'manual') {
102133
$data[] = $entry;
103134

104135
if (file_put_contents($jsonFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) === false) {
136+
flock($lockHandle, LOCK_UN);
137+
fclose($lockHandle);
105138
$results[] = [
106139
'ip' => $ip,
107140
'success' => false,
@@ -110,6 +143,9 @@ function blockIp($ips, $jail = 'unknown', $source = 'manual') {
110143
];
111144
continue;
112145
}
146+
147+
flock($lockHandle, LOCK_UN);
148+
fclose($lockHandle);
113149
$results[] = [
114150
'ip' => $ip,
115151
'success' => true,
@@ -119,7 +155,7 @@ function blockIp($ips, $jail = 'unknown', $source = 'manual') {
119155
}
120156
}
121157

122-
// Bei nur einem Ergebnis flach zurückgeben
158+
// Flatten result if only one entry
123159
if (count($results) === 1) {
124160
return $results[0];
125161
}

includes/header.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<div>
3030
<h1>Fail2Ban-Report</h1>
3131
<h2>Let's catch the bad guys!</h2>
32-
<div><span title="Beta 3.3"><small>Version : 0.3.3</small></span></div>
32+
<div><span title="Beta 3.4"><small>Version : 0.3.4</small></span></div>
3333
</div>
3434

3535

0 commit comments

Comments
 (0)