feat: Offense-Cleanup-Worker für automatisches Aufräumen abgelaufener Offense-Zähler
This commit is contained in:
@@ -23,7 +23,7 @@ Wenn ein Client eine bestimmte Domain zu oft anfragt (z.B. >30x pro Minute), wir
|
||||
|
||||
- Automatische Erkennung und Sperre bei Rate-Limit-Verstößen
|
||||
- **Subdomain-Flood-Erkennung** — erkennt Random-Subdomain-Attacken (z.B. `abc123.microsoft.com`, `xyz456.microsoft.com`, ...)
|
||||
- **Progressive Sperren (Recidive)** — Wiederholungstäter werden stufenweise länger gesperrt (wie bei fail2ban)
|
||||
- **Progressive Sperren (Recidive)** — Wiederholungstäter werden stufenweise länger gesperrt (wie bei fail2ban), mit automatischem Cleanup abgelaufener Zähler
|
||||
- Unterstützt **alle DNS-Protokolle**: DNS (53), DoH (443), DoT (853), DoQ (784/853/8853)
|
||||
- **IPv4 + IPv6**
|
||||
- Eigene iptables Chain — greift nicht in bestehende Regeln ein
|
||||
|
||||
@@ -331,6 +331,7 @@ cleanup() {
|
||||
stop_blocklist_worker
|
||||
stop_whitelist_worker
|
||||
stop_geoip_worker
|
||||
stop_offense_cleanup_worker
|
||||
rm -f "$PID_FILE"
|
||||
exit 0
|
||||
}
|
||||
@@ -1257,6 +1258,39 @@ stop_geoip_worker() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Offense-Cleanup-Worker starten ──────────────────────────────────────────
|
||||
start_offense_cleanup_worker() {
|
||||
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" != "true" ]]; then
|
||||
log "DEBUG" "Offense-Cleanup-Worker ist deaktiviert (Progressive Sperren inaktiv)"
|
||||
return
|
||||
fi
|
||||
|
||||
local worker_script="${SCRIPT_DIR}/offense-cleanup-worker.sh"
|
||||
if [[ ! -f "$worker_script" ]]; then
|
||||
log "WARN" "Offense-Cleanup-Worker Script nicht gefunden: $worker_script"
|
||||
return
|
||||
fi
|
||||
|
||||
log "INFO" "Starte Offense-Cleanup-Worker im Hintergrund..."
|
||||
bash "$worker_script" start &
|
||||
OFFENSE_CLEANUP_WORKER_PID=$!
|
||||
log "INFO" "Offense-Cleanup-Worker gestartet (PID: $OFFENSE_CLEANUP_WORKER_PID)"
|
||||
}
|
||||
|
||||
# ─── Offense-Cleanup-Worker stoppen ──────────────────────────────────────────
|
||||
stop_offense_cleanup_worker() {
|
||||
local worker_pid_file="/var/run/adguard-offense-cleanup-worker.pid"
|
||||
if [[ -f "$worker_pid_file" ]]; then
|
||||
local wpid
|
||||
wpid=$(cat "$worker_pid_file")
|
||||
if kill -0 "$wpid" 2>/dev/null; then
|
||||
log "INFO" "Stoppe Offense-Cleanup-Worker (PID: $wpid)..."
|
||||
kill "$wpid" 2>/dev/null || true
|
||||
rm -f "$worker_pid_file"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Hauptschleife ──────────────────────────────────────────────────────────
|
||||
main_loop() {
|
||||
log "INFO" "═══════════════════════════════════════════════════════════"
|
||||
@@ -1288,6 +1322,9 @@ main_loop() {
|
||||
else
|
||||
log "INFO" " GeoIP-Filter: deaktiviert"
|
||||
fi
|
||||
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" ]]; then
|
||||
log "INFO" " Offense-Cleanup: AKTIV (Reset: $(format_duration "${PROGRESSIVE_BAN_RESET_AFTER:-86400}"), Prüfintervall: 1h)"
|
||||
fi
|
||||
log "INFO" "═══════════════════════════════════════════════════════════"
|
||||
|
||||
# Service-Start-Benachrichtigung senden
|
||||
@@ -1304,6 +1341,9 @@ main_loop() {
|
||||
# GeoIP-Worker als Hintergrundprozess starten
|
||||
start_geoip_worker
|
||||
|
||||
# Offense-Cleanup-Worker als Hintergrundprozess starten
|
||||
start_offense_cleanup_worker
|
||||
|
||||
while true; do
|
||||
# Abgelaufene Sperren prüfen
|
||||
check_expired_bans
|
||||
|
||||
@@ -137,6 +137,7 @@ Das ermöglicht:
|
||||
├── external-blocklist-worker.sh # Externer Blocklist-Worker
|
||||
├── external-whitelist-worker.sh # Externer Whitelist-Worker (DNS-Auflösung)
|
||||
├── geoip-worker.sh # GeoIP-Länderfilter-Worker
|
||||
├── offense-cleanup-worker.sh # Automatisches Aufräumen abgelaufener Offense-Zähler
|
||||
├── unban-expired.sh # Cron-basiertes Entsperren
|
||||
└── geoip/ # Auto-Download MaxMind GeoLite2 DB (optional)
|
||||
|
||||
|
||||
@@ -282,6 +282,26 @@ sudo /opt/adguard-shield/geoip-worker.sh flush
|
||||
sudo /opt/adguard-shield/geoip-worker.sh flush-cache
|
||||
```
|
||||
|
||||
## Offense-Cleanup-Worker
|
||||
|
||||
Der Offense-Cleanup-Worker räumt abgelaufene Offense-Zähler (progressive Sperren) automatisch auf. Er startet automatisch mit dem Hauptservice, wenn progressive Sperren aktiviert sind, und prüft stündlich ob Zähler aufgeräumt werden können.
|
||||
|
||||
Der Worker kann auch standalone gesteuert werden:
|
||||
|
||||
```bash
|
||||
# Worker manuell starten (normalerweise automatisch per Hauptscript)
|
||||
sudo /opt/adguard-shield/offense-cleanup-worker.sh start
|
||||
|
||||
# Worker stoppen
|
||||
sudo /opt/adguard-shield/offense-cleanup-worker.sh stop
|
||||
|
||||
# Einmaliger Cleanup-Durchlauf
|
||||
sudo /opt/adguard-shield/offense-cleanup-worker.sh run-once
|
||||
|
||||
# Status anzeigen (aktive/abgelaufene Zähler)
|
||||
sudo /opt/adguard-shield/offense-cleanup-worker.sh status
|
||||
```
|
||||
|
||||
## E-Mail Report
|
||||
|
||||
```bash
|
||||
|
||||
@@ -100,7 +100,7 @@ Wiederholungstäter werden wie bei fail2ban stufenweise länger gesperrt. Wird e
|
||||
| 4. Mal | 4 | 8 Stunden | 3600 × 8 |
|
||||
| 5. Mal | 5 | **PERMANENT** | Max-Stufe erreicht |
|
||||
|
||||
> **Hinweis:** Der Offense-Zähler wird automatisch zurückgesetzt, wenn eine IP für den konfigurierten Zeitraum (`PROGRESSIVE_BAN_RESET_AFTER`) kein erneutes Vergehen begeht. Permanente Sperren werden **nicht** automatisch aufgehoben – sie müssen manuell mit `unban` oder `flush` entfernt werden.
|
||||
> **Hinweis:** Abgelaufene Offense-Zähler werden automatisch vom **Offense-Cleanup-Worker** aufgeräumt, der stündlich prüft, ob das letzte Vergehen einer IP länger als `PROGRESSIVE_BAN_RESET_AFTER` zurückliegt. Der Worker startet automatisch zusammen mit dem Hauptservice, wenn progressive Sperren aktiviert sind. Manuelles Zurücksetzen ist jederzeit mit `reset-offenses` möglich. Permanente Sperren werden **nicht** automatisch aufgehoben – sie müssen manuell mit `unban` oder `flush` entfernt werden.
|
||||
|
||||
### Logging
|
||||
|
||||
|
||||
@@ -265,6 +265,7 @@ install_files() {
|
||||
cp "$SCRIPT_DIR/adguard-shield-watchdog.sh" "$INSTALL_DIR/"
|
||||
cp "$SCRIPT_DIR/uninstall.sh" "$INSTALL_DIR/"
|
||||
cp "$SCRIPT_DIR/geoip-worker.sh" "$INSTALL_DIR/"
|
||||
cp "$SCRIPT_DIR/offense-cleanup-worker.sh" "$INSTALL_DIR/"
|
||||
|
||||
# Templates kopieren
|
||||
mkdir -p "$INSTALL_DIR/templates"
|
||||
@@ -281,6 +282,7 @@ install_files() {
|
||||
chmod +x "$INSTALL_DIR/adguard-shield-watchdog.sh"
|
||||
chmod +x "$INSTALL_DIR/uninstall.sh"
|
||||
chmod +x "$INSTALL_DIR/geoip-worker.sh"
|
||||
chmod +x "$INSTALL_DIR/offense-cleanup-worker.sh"
|
||||
|
||||
echo -e " ✅ Dateien installiert"
|
||||
echo ""
|
||||
@@ -810,8 +812,13 @@ do_uninstall() {
|
||||
rm -f "$INSTALL_DIR/unban-expired.sh"
|
||||
rm -f "$INSTALL_DIR/external-blocklist-worker.sh"
|
||||
rm -f "$INSTALL_DIR/external-whitelist-worker.sh"
|
||||
rm -f "$INSTALL_DIR/offense-cleanup-worker.sh"
|
||||
rm -f "$INSTALL_DIR/geoip-worker.sh"
|
||||
rm -f "$INSTALL_DIR/report-generator.sh"
|
||||
rm -f "$INSTALL_DIR/adguard-shield-watchdog.sh"
|
||||
rm -f "$INSTALL_DIR/uninstall.sh"
|
||||
rm -rf "$INSTALL_DIR/templates"
|
||||
rm -rf "$INSTALL_DIR/geoip"
|
||||
echo " ✅ Scripts entfernt (Konfiguration und Logs behalten)"
|
||||
else
|
||||
rm -rf "$INSTALL_DIR"
|
||||
|
||||
255
offense-cleanup-worker.sh
Normal file
255
offense-cleanup-worker.sh
Normal file
@@ -0,0 +1,255 @@
|
||||
#!/bin/bash
|
||||
###############################################################################
|
||||
# AdGuard Shield - Offense-Cleanup-Worker
|
||||
# Räumt abgelaufene Offense-Zähler (progressive Sperren) automatisch auf.
|
||||
# Entfernt .offenses-Dateien, deren letztes Vergehen länger als
|
||||
# PROGRESSIVE_BAN_RESET_AFTER zurückliegt.
|
||||
# Wird als Hintergrundprozess vom Hauptscript gestartet.
|
||||
#
|
||||
# Autor: Patrick Asmus
|
||||
# E-Mail: support@techniverse.net
|
||||
# Datum: 2026-04-14
|
||||
# Lizenz: MIT
|
||||
###############################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_FILE="${SCRIPT_DIR}/adguard-shield.conf"
|
||||
|
||||
# ─── Konfiguration laden ───────────────────────────────────────────────────────
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "FEHLER: Konfigurationsdatei nicht gefunden: $CONFIG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck source=adguard-shield.conf
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
# ─── Worker PID-File ──────────────────────────────────────────────────────────
|
||||
WORKER_PID_FILE="/var/run/adguard-offense-cleanup-worker.pid"
|
||||
|
||||
# ─── Prüfintervall ───────────────────────────────────────────────────────────
|
||||
# Prüft einmal pro Stunde – das ist völlig ausreichend für diese Aufgabe
|
||||
OFFENSE_CLEANUP_INTERVAL=3600
|
||||
|
||||
# ─── Logging (eigene Funktion, nutzt gleiche Log-Datei) ───────────────────────
|
||||
declare -A LOG_LEVELS=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3)
|
||||
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
local message="$*"
|
||||
local configured_level="${LOG_LEVEL:-INFO}"
|
||||
|
||||
if [[ ${LOG_LEVELS[$level]:-1} -ge ${LOG_LEVELS[$configured_level]:-1} ]]; then
|
||||
local timestamp
|
||||
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
local log_entry="[$timestamp] [$level] [OFFENSE-CLEANUP] $message"
|
||||
echo "$log_entry" | tee -a "$LOG_FILE" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Hilfsfunktionen ─────────────────────────────────────────────────────────
|
||||
format_duration() {
|
||||
local seconds="$1"
|
||||
if [[ "$seconds" -eq 0 ]]; then
|
||||
echo "PERMANENT"
|
||||
return
|
||||
fi
|
||||
if [[ "$seconds" -ge 86400 ]]; then
|
||||
echo "$((seconds / 86400))d $((seconds % 86400 / 3600))h"
|
||||
elif [[ "$seconds" -ge 3600 ]]; then
|
||||
echo "$((seconds / 3600))h $((seconds % 3600 / 60))m"
|
||||
elif [[ "$seconds" -ge 60 ]]; then
|
||||
echo "$((seconds / 60))m $((seconds % 60))s"
|
||||
else
|
||||
echo "${seconds}s"
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Verzeichnisse erstellen ──────────────────────────────────────────────────
|
||||
init_directories() {
|
||||
mkdir -p "${STATE_DIR}"
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
}
|
||||
|
||||
# ─── Abgelaufene Offense-Zähler aufräumen ────────────────────────────────────
|
||||
cleanup_expired_offenses() {
|
||||
local reset_after="${PROGRESSIVE_BAN_RESET_AFTER:-86400}"
|
||||
local now
|
||||
now=$(date '+%s')
|
||||
local cleaned=0
|
||||
|
||||
for offense_file in "${STATE_DIR}"/*.offenses; do
|
||||
[[ -f "$offense_file" ]] || continue
|
||||
|
||||
local last_offense_epoch client_ip offense_level
|
||||
last_offense_epoch=$(grep '^LAST_OFFENSE_EPOCH=' "$offense_file" | cut -d= -f2 || true)
|
||||
client_ip=$(grep '^CLIENT_IP=' "$offense_file" | cut -d= -f2 || true)
|
||||
offense_level=$(grep '^OFFENSE_LEVEL=' "$offense_file" | cut -d= -f2 || true)
|
||||
|
||||
# Kein Zeitstempel vorhanden → überspringen
|
||||
if [[ -z "$last_offense_epoch" ]]; then
|
||||
log "DEBUG" "Offense-Datei ohne Zeitstempel übersprungen: $offense_file"
|
||||
continue
|
||||
fi
|
||||
|
||||
local elapsed=$((now - last_offense_epoch))
|
||||
|
||||
if [[ $elapsed -gt $reset_after ]]; then
|
||||
log "INFO" "Offense-Zähler abgelaufen: $client_ip (Stufe $offense_level, letztes Vergehen vor $(format_duration $elapsed)) → entfernt"
|
||||
rm -f "$offense_file"
|
||||
cleaned=$((cleaned + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $cleaned -gt 0 ]]; then
|
||||
log "INFO" "Offense-Cleanup: $cleaned abgelaufene Zähler entfernt"
|
||||
else
|
||||
log "DEBUG" "Offense-Cleanup: keine abgelaufenen Zähler gefunden"
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── PID-Management ──────────────────────────────────────────────────────────
|
||||
write_pid() {
|
||||
echo $$ > "$WORKER_PID_FILE"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
log "INFO" "Offense-Cleanup-Worker wird beendet..."
|
||||
rm -f "$WORKER_PID_FILE"
|
||||
exit 0
|
||||
}
|
||||
|
||||
check_already_running() {
|
||||
if [[ -f "$WORKER_PID_FILE" ]]; then
|
||||
local old_pid
|
||||
old_pid=$(cat "$WORKER_PID_FILE")
|
||||
if kill -0 "$old_pid" 2>/dev/null; then
|
||||
log "DEBUG" "Offense-Cleanup-Worker läuft bereits (PID: $old_pid)"
|
||||
return 1
|
||||
else
|
||||
rm -f "$WORKER_PID_FILE"
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ─── Status anzeigen ─────────────────────────────────────────────────────────
|
||||
show_status() {
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo " Offense-Cleanup-Worker - Status"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" != "true" ]]; then
|
||||
echo " ⚠️ Progressive Sperren sind deaktiviert"
|
||||
echo " Aktivieren: PROGRESSIVE_BAN_ENABLED=true in $CONFIG_FILE"
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
|
||||
# Worker-Prozess Status
|
||||
if [[ -f "$WORKER_PID_FILE" ]]; then
|
||||
local pid
|
||||
pid=$(cat "$WORKER_PID_FILE")
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
echo " 🟢 Worker läuft (PID: $pid)"
|
||||
else
|
||||
echo " 🔴 Worker nicht aktiv (veraltete PID-Datei)"
|
||||
fi
|
||||
else
|
||||
echo " 🔴 Worker nicht aktiv"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Reset-Zeitraum: $(format_duration "${PROGRESSIVE_BAN_RESET_AFTER:-86400}")"
|
||||
echo " Prüfintervall: $(format_duration "$OFFENSE_CLEANUP_INTERVAL")"
|
||||
|
||||
# Aktuelle Offense-Dateien zählen
|
||||
local total=0
|
||||
local expired=0
|
||||
local now
|
||||
now=$(date '+%s')
|
||||
local reset_after="${PROGRESSIVE_BAN_RESET_AFTER:-86400}"
|
||||
|
||||
for offense_file in "${STATE_DIR}"/*.offenses; do
|
||||
[[ -f "$offense_file" ]] || continue
|
||||
total=$((total + 1))
|
||||
local last_epoch
|
||||
last_epoch=$(grep '^LAST_OFFENSE_EPOCH=' "$offense_file" | cut -d= -f2 || true)
|
||||
if [[ -n "$last_epoch" && $((now - last_epoch)) -gt $reset_after ]]; then
|
||||
expired=$((expired + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo " Offense-Zähler gesamt: $total"
|
||||
echo " Davon abgelaufen: $expired"
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
}
|
||||
|
||||
# ─── Hauptschleife ──────────────────────────────────────────────────────────
|
||||
main_loop() {
|
||||
init_directories
|
||||
|
||||
log "INFO" "═══════════════════════════════════════════════════════════"
|
||||
log "INFO" "Offense-Cleanup-Worker gestartet"
|
||||
log "INFO" " Reset-Zeitraum: $(format_duration "${PROGRESSIVE_BAN_RESET_AFTER:-86400}")"
|
||||
log "INFO" " Prüfintervall: $(format_duration "$OFFENSE_CLEANUP_INTERVAL")"
|
||||
log "INFO" "═══════════════════════════════════════════════════════════"
|
||||
|
||||
while true; do
|
||||
cleanup_expired_offenses
|
||||
sleep "$OFFENSE_CLEANUP_INTERVAL"
|
||||
done
|
||||
}
|
||||
|
||||
# ─── Signal-Handler ──────────────────────────────────────────────────────────
|
||||
trap cleanup SIGTERM SIGINT SIGHUP
|
||||
|
||||
# ─── Kommandozeilen-Argumente ────────────────────────────────────────────────
|
||||
case "${1:-start}" in
|
||||
start)
|
||||
if ! check_already_running; then
|
||||
exit 0
|
||||
fi
|
||||
write_pid
|
||||
main_loop
|
||||
;;
|
||||
stop)
|
||||
if [[ -f "$WORKER_PID_FILE" ]]; then
|
||||
kill "$(cat "$WORKER_PID_FILE")" 2>/dev/null || true
|
||||
rm -f "$WORKER_PID_FILE"
|
||||
echo "Offense-Cleanup-Worker gestoppt"
|
||||
else
|
||||
echo "Offense-Cleanup-Worker läuft nicht"
|
||||
fi
|
||||
;;
|
||||
run-once)
|
||||
init_directories
|
||||
log "INFO" "Einmaliger Offense-Cleanup..."
|
||||
cleanup_expired_offenses
|
||||
log "INFO" "Cleanup abgeschlossen"
|
||||
;;
|
||||
status)
|
||||
init_directories
|
||||
show_status
|
||||
;;
|
||||
*)
|
||||
cat << USAGE
|
||||
AdGuard Shield - Offense-Cleanup-Worker
|
||||
|
||||
Nutzung: $0 {start|stop|run-once|status}
|
||||
|
||||
Befehle:
|
||||
start Startet den Worker (Dauerbetrieb)
|
||||
stop Stoppt den Worker
|
||||
run-once Einmaliger Cleanup-Durchlauf
|
||||
status Zeigt Status und aktuelle Offense-Zähler
|
||||
|
||||
Konfiguration: $CONFIG_FILE
|
||||
USAGE
|
||||
;;
|
||||
esac
|
||||
@@ -123,6 +123,7 @@ do_uninstall() {
|
||||
rm -f "$INSTALL_DIR/unban-expired.sh"
|
||||
rm -f "$INSTALL_DIR/external-blocklist-worker.sh"
|
||||
rm -f "$INSTALL_DIR/external-whitelist-worker.sh"
|
||||
rm -f "$INSTALL_DIR/offense-cleanup-worker.sh"
|
||||
rm -f "$INSTALL_DIR/report-generator.sh"
|
||||
rm -f "$INSTALL_DIR/adguard-shield-watchdog.sh"
|
||||
rm -f "$INSTALL_DIR/geoip-worker.sh"
|
||||
|
||||
Reference in New Issue
Block a user