Files
adguard-shield/offense-cleanup-worker.sh

269 lines
10 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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-16
# 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"
# ─── Niedrigste Priorität setzen (CPU + I/O) ─────────────────────────────────
# Stellt sicher, dass der Worker auch bei manuellem Start nie andere Dienste
# verdrängt. nice 19 = niedrigste CPU-Priorität, ionice idle = nur bei freier I/O.
renice -n 19 $$ >/dev/null 2>&1 || true
ionice -c 3 -p $$ >/dev/null 2>&1 || true
# ─── 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
local batch_count=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
# Alle 10 Dateien kurz pausieren, um I/O-Bursts zu vermeiden
batch_count=$((batch_count + 1))
if (( batch_count % 10 == 0 )); then
sleep 0.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