diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..d18bf85
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Enforce LF line endings for all text files
+* text=auto eol=lf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dbec873
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+# Backup-Dateien
+*.bak
+backups/
+
+# Log-Dateien
+*.log
+*.log.*
+
+# Lock-File
+*.lock
+
+# Temporäre Dateien
+*.tmp
+*.swp
+*.swo
+*~
+
+# OS-Dateien
+.DS_Store
+Thumbs.db
+
+# Editor
+.vscode/
+.idea/
diff --git a/README.md b/README.md
index df7ff0a..fdfa03e 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,293 @@
-# template_repository
+# CrowdSec Manager
+Automatische Verwaltung einer CrowdSec-Allowlist und interaktive CrowdSec-Administration über ein Menü.
+## Übersicht
+Der **CrowdSec Manager** ist ein Bash-Script, das:
+- Domains und Subdomains automatisch zu IPv4/IPv6-Adressen auflöst
+- Die aufgelösten IPs in eine CrowdSec-Allowlist einträgt
+- Veraltete Einträge automatisch entfernt wenn sich IPs ändern
+- Per Cron regelmäßig ausgeführt werden kann (automatischer Sync)
+- Ein **interaktives Menü** für die CrowdSec-Administration bereitstellt
+- Benachrichtigungen über Änderungen senden kann (Desktop, Ntfy, **Gotify**, E-Mail)
-Wichtig: Link für Lizenz anpassen.
+### Features
+- **Interaktives Menü** als Standard-Modus (für manuelle Verwaltung)
+- **CrowdSec Admin-Funktionen**: Decisions, Alerts, Bouncers, Machines, Metriken, IP-Info
+- **Gotify** als zusätzlicher Benachrichtigungskanal
+- **`--run`-Flag** für automatischen Cron-Betrieb
+- **Eigene cscli-Befehle** direkt aus dem Menü ausführen
+## Features
-
-
-
+| Feature | Beschreibung |
+|---------|-------------|
+| **Interaktives Menü** | Grafische Administration von CrowdSec via Menü |
+| **DNS-Auflösung** | Automatische Auflösung von Domains zu IPv4 und IPv6 |
+| **CIDR-Support** | Ganze Subnetze können allowgelistet werden |
+| **Auto-Cleanup** | Entfernt IPs die nicht mehr zu konfigurierten Domains gehören |
+| **Decisions verwalten** | Anzeigen, Suchen, Hinzufügen, Entfernen von Decisions |
+| **Alerts verwalten** | Anzeigen, Inspizieren, Löschen von Alerts |
+| **IP-Info** | Komplett-Check einer IP (rDNS, Decisions, Alerts, Details) |
+| **Flexible CrowdSec-Anbindung** | Unterstützt direkte Installation, Docker, Podman, etc. |
+| **Benachrichtigungen** | Desktop, Ntfy, Gotify, E-Mail |
+| **Logging** | Konfigurierbares Log-Level mit Log-Rotation |
+| **Dry-Run** | Testmodus ohne tatsächliche Änderungen |
+| **Lock-File** | Verhindert parallele Ausführung |
+| **Backup** | Sichert den Zustand vor Änderungen |
+| **Cron-fähig** | Optimiert für automatische Ausführung mit `--run` |
-
-
License |
Matrix |
Mastodon
-
\ No newline at end of file
+## Schnellstart
+
+### 1. Repository klonen
+
+```bash
+git clone https://git.techniverse.net/scriptos/crowdsec-manager.git
+cd crowdsec-manager
+chmod +x crowdsec-manager.sh
+```
+
+### 2. Abhängigkeiten installieren
+
+```bash
+# Debian / Ubuntu
+sudo apt install -y dnsutils curl
+
+# RHEL / CentOS / Fedora
+sudo dnf install -y bind-utils curl
+```
+
+### 3. Konfiguration anpassen
+
+```bash
+nano config/crowdsec-manager.conf
+```
+
+**Mindestens anpassen:**
+- `CSCLI_CMD` – Der Befehl um CrowdSec CLI aufzurufen
+- `ALLOWLIST_ENTRIES` – Die Domains/IPs für die Allowlist
+
+### 4. Testen & Verwenden
+
+```bash
+# Konfiguration prüfen
+./crowdsec-manager.sh --test
+
+# Interaktives Menü starten (Standard)
+./crowdsec-manager.sh
+
+# Allowlist-Sync (für Cron)
+./crowdsec-manager.sh --run
+
+# Testlauf ohne Änderungen
+./crowdsec-manager.sh --run --dry-run
+```
+
+### 5. Cron einrichten
+
+```bash
+crontab -e
+```
+
+```cron
+*/30 * * * * /opt/crowdsec-manager/crowdsec-manager.sh --run >> /var/log/crowdsec-manager-cron.log 2>&1
+```
+
+> **Wichtig:** `--run` muss angegeben werden, da ohne Parameter das interaktive Menü startet.
+
+## Interaktives Menü
+
+```
+╔═══════════════════════════════════════════════════════╗
+║ CrowdSec Manager v0.1.0 ║
+╚═══════════════════════════════════════════════════════╝
+
+── ALLOWLIST MANAGEMENT ──
+ 1) Allowlist synchronisieren 2) Dry-Run
+ 3) Allowlist anzeigen 4) Config testen
+ 5) Flush (alle Einträge entfernen)
+
+── DECISIONS ──
+ 10) Alle anzeigen 11) Suchen 12) Hinzufügen 13) Entfernen
+
+── ALERTS ──
+ 20) Alle anzeigen 21) Inspizieren 22) Löschen
+
+── INFORMATIONEN ──
+ 30) IP-Info (Komplett) 31) Metriken 32) Status
+ 33) Bouncers 34) Machines 35) Parsers
+ 36) Scenarios 37) Collections
+
+── ERWEITERT ──
+ 40) Eigener cscli-Befehl 0) Beenden
+```
+
+## Konfiguration
+
+Die gesamte Konfiguration erfolgt über: [`config/crowdsec-manager.conf`](config/crowdsec-manager.conf)
+
+### CrowdSec Basisbefehl
+
+```bash
+# Direkt installiert
+CSCLI_CMD="cscli"
+
+# Docker
+CSCLI_CMD="docker exec crowdsec cscli"
+
+# Docker Compose
+CSCLI_CMD="docker compose exec crowdsec cscli"
+```
+
+### Domains & IPs
+
+```bash
+ALLOWLIST_ENTRIES=(
+ "example.com" # Domain (wird aufgelöst)
+ "cdn.example.com"
+ "203.0.113.50" # Einzelne IP
+ "10.0.0.0/8" # CIDR-Bereich
+ "2001:db8::1" # IPv6
+)
+```
+
+### Benachrichtigungen
+
+```bash
+NOTIFY_ENABLED=true
+NOTIFY_ON="changes" # always | changes | errors
+
+# Ntfy
+NOTIFY_NTFY_ENABLED=true
+NOTIFY_NTFY_URL="https://ntfy.sh"
+NOTIFY_NTFY_TOPIC="mein-crowdsec"
+
+# Gotify
+NOTIFY_GOTIFY_ENABLED=true
+NOTIFY_GOTIFY_URL="https://gotify.meinedomain.de"
+NOTIFY_GOTIFY_TOKEN="AbCdEf12345"
+NOTIFY_GOTIFY_PRIORITY=5
+
+# E-Mail
+NOTIFY_EMAIL_ENABLED=true
+NOTIFY_EMAIL_TO="admin@example.com"
+NOTIFY_EMAIL_SMTP_SERVER="smtp.example.com"
+```
+
+## Verwendung (CLI)
+
+```
+Verwendung:
+ crowdsec-manager.sh [OPTIONEN]
+
+Modi:
+ (ohne Optionen) Startet das interaktive Menü
+ --run Allowlist synchronisieren (für Cron)
+
+Optionen:
+ -c, --config FILE Konfigurationsdatei angeben
+ -d, --dry-run Testlauf ohne Änderungen
+ -v, --verbose Ausführliche Ausgabe (DEBUG)
+ -q, --quiet Minimale Ausgabe (nur ERROR)
+ -l, --list Aktuelle Allowlist anzeigen
+ -f, --flush Alle verwalteten Einträge entfernen
+ -t, --test Konfiguration testen
+ -i, --interactive Interaktives Menü starten
+ -h, --help Hilfe anzeigen
+ -V, --version Version anzeigen
+```
+
+## Ablaufdiagramm (Allowlist-Sync)
+
+```
+┌─────────────────────────┐
+│ Script: --run │
+└────────────┬────────────┘
+ │
+ ┌───────▼────────┐
+ │ Config laden │
+ │ Lock erwerben │
+ │ Health-Check │
+ └───────┬────────┘
+ │
+ ┌───────▼────────┐
+ │ Aktuelle Liste │
+ │ abrufen + Backup│
+ └───────┬────────┘
+ │
+ ┌───────▼────────┐
+ │ DNS-Auflösung │
+ │ aller Einträge │
+ └───────┬────────┘
+ │
+ ┌───────▼────────┐
+ │ Vergleich & │
+ │ Synchronisation │
+ └───────┬────────┘
+ │
+ ┌───────▼────────┐
+ │ Report & │
+ │ Notifications │
+ └───────┬────────┘
+ │
+ ┌───────▼────────┐
+ │ Lock lösen │
+ │ Aufräumen │
+ └─────────────────┘
+```
+
+## Abhängigkeiten
+
+| Paket | Erforderlich | Zweck |
+|-------|:---:|--------|
+| `bash` (≥ 4.0) | ✅ | Script-Ausführung |
+| `dig` (dnsutils / bind-utils) | ✅ | DNS-Auflösung |
+| `curl` | ⚡ | Ntfy-, Gotify- und E-Mail-Benachrichtigungen |
+| `python3` | ⚡ | JSON-Escaping für Gotify (Fallback: sed) |
+| `notify-send` | ⚡ | Desktop-Benachrichtigungen |
+| `mail` | ⚡ | E-Mail-Fallback |
+
+✅ = Erforderlich | ⚡ = Optional (je nach Konfiguration)
+
+## Dateien & Struktur
+
+```
+crowdsec-manager/
+├── crowdsec-manager.sh # Hauptscript
+├── config/
+│ └── crowdsec-manager.conf # Konfigurationsdatei
+├── examples/
+│ └── crontab.example # Crontab-Beispiele
+├── docs/
+│ ├── DOCUMENTATION.md # Dokumentationsindex
+│ ├── CONFIGURATION.md # Konfigurationsreferenz
+│ ├── ALLOWLIST.md # Allowlist-Verwaltung & Cron
+│ ├── ADMIN.md # Interaktives Menü & Admin
+│ ├── NOTIFICATIONS.md # Benachrichtigungssystem
+│ └── TROUBLESHOOTING.md # Fehlerbehebung & FAQ
+├── README.md # Diese Datei
+├── LICENSE # MIT Lizenz
+└── .gitignore
+```
+
+## Dokumentation
+
+Die detaillierte Dokumentation ist aufgeteilt in:
+
+| Dokument | Beschreibung |
+|----------|-------------|
+| [docs/CONFIGURATION.md](docs/CONFIGURATION.md) | Alle Konfigurationsoptionen |
+| [docs/ALLOWLIST.md](docs/ALLOWLIST.md) | Allowlist-Verwaltung, CLI, Cron |
+| [docs/ADMIN.md](docs/ADMIN.md) | Interaktives Menü & Administration |
+| [docs/NOTIFICATIONS.md](docs/NOTIFICATIONS.md) | Ntfy, Gotify, E-Mail, Desktop |
+| [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) | Fehlerbehebung & FAQ |
+
+## Lizenz
+
+MIT License – siehe [LICENSE](LICENSE) Datei.
+
+## Beitragen
+
+Pull Requests und Issues sind willkommen! Bitte stelle sicher, dass deine Änderungen die bestehende Funktionalität nicht brechen.
diff --git a/config/crowdsec-manager.conf b/config/crowdsec-manager.conf
new file mode 100644
index 0000000..ce3788f
--- /dev/null
+++ b/config/crowdsec-manager.conf
@@ -0,0 +1,177 @@
+#!/bin/bash
+# ============================================================================
+# CrowdSec Manager - Konfigurationsdatei
+# ============================================================================
+# Diese Datei enthält alle konfigurierbaren Parameter für das Script.
+# Passe die Werte an deine Umgebung an.
+# ============================================================================
+
+# ----------------------------------------------------------------------------
+# CrowdSec Basis-Konfiguration
+# ----------------------------------------------------------------------------
+
+# Basisbefehl für CrowdSec CLI (cscli)
+# Beispiele:
+# Direkt installiert: CSCLI_CMD="cscli"
+# Docker: CSCLI_CMD="docker exec crowdsec cscli"
+# Docker Compose: CSCLI_CMD="docker compose exec crowdsec cscli"
+# Podman: CSCLI_CMD="podman exec crowdsec cscli"
+CSCLI_CMD="docker exec crowdsec cscli"
+
+# Name der Allowlist in CrowdSec (wird via 'cscli allowlists' verwaltet)
+# Diese Liste wird vom Script automatisch angelegt und verwaltet
+ALLOWLIST_NAME="crowdsec-manager"
+
+# Beschreibung der Allowlist
+ALLOWLIST_DESCRIPTION="Managed by crowdsec-manager script"
+
+# Beschreibung für einzelne Einträge in der Allowlist
+ALLOWLIST_REASON="allowlist-manager-auto"
+
+# ----------------------------------------------------------------------------
+# Domains / DNS / IPs zum Allowlisten
+# ----------------------------------------------------------------------------
+
+# Liste der Domains, Subdomains und einzelnen IPs
+# Trenne mehrere Einträge mit Leerzeichen
+# Unterstützte Formate:
+# - Domains: example.com
+# - Subdomains: subdomain.example.com
+# - IPv4-Adressen: 192.168.1.100
+# - IPv6-Adressen: 2001:db8::1
+# - CIDR-Notation: 192.168.1.0/24
+# - CIDR IPv6: 2001:db8::/32
+
+ALLOWLIST_ENTRIES=(
+ # === Domains & Subdomains ===
+ "example.com"
+ "subdomain.example.com"
+
+ # === Einzelne IPs ===
+ # "192.168.1.100"
+ # "2001:db8::1"
+
+ # === CIDR-Bereiche ===
+ # "10.0.0.0/8"
+ # "2001:db8::/32"
+)
+
+# ----------------------------------------------------------------------------
+# DNS-Auflösung
+# ----------------------------------------------------------------------------
+
+# Eigenen DNS-Server verwenden (leer lassen für System-DNS)
+# Beispiele: "1.1.1.1", "8.8.8.8", "9.9.9.9"
+DNS_SERVER=""
+
+# IPv6-Adressen ebenfalls auflösen? (true/false)
+RESOLVE_IPV6=true
+
+# DNS-Auflösungs-Timeout in Sekunden
+DNS_TIMEOUT=5
+
+# Anzahl DNS-Auflösungsversuche bei Fehler
+DNS_RETRIES=3
+
+# ----------------------------------------------------------------------------
+# Logging
+# ----------------------------------------------------------------------------
+
+# Log-Level: DEBUG, INFO, WARN, ERROR
+LOG_LEVEL="INFO"
+
+# Pfad zur Log-Datei (leer = nur stdout)
+LOG_FILE="/var/log/crowdsec-manager.log"
+
+# Maximale Größe der Log-Datei in KB bevor rotiert wird (0 = keine Rotation)
+LOG_MAX_SIZE_KB=5120
+
+# Anzahl der aufzubewahrenden rotierten Log-Dateien
+LOG_ROTATE_COUNT=3
+
+# ----------------------------------------------------------------------------
+# Backup
+# ----------------------------------------------------------------------------
+
+# Backup vor Änderungen erstellen? (true/false)
+BACKUP_ENABLED=true
+
+# Verzeichnis für Backups
+BACKUP_DIR="/var/backup/crowdsec-manager"
+
+# Anzahl aufzubewahrende Backups (ältere werden gelöscht)
+BACKUP_RETAIN_COUNT=7
+
+# ----------------------------------------------------------------------------
+# Lock-File (verhindert parallele Ausführung)
+# ----------------------------------------------------------------------------
+
+# Lock-File aktivieren? (true/false)
+LOCK_ENABLED=true
+
+# Pfad zum Lock-File
+LOCK_FILE="/tmp/crowdsec-manager.lock"
+
+# Timeout für Lock in Sekunden (0 = sofort abbrechen wenn gesperrt)
+LOCK_TIMEOUT=300
+
+# ----------------------------------------------------------------------------
+# Benachrichtigungen
+# ----------------------------------------------------------------------------
+
+# Benachrichtigungen aktivieren? (true/false)
+NOTIFY_ENABLED=false
+
+# Wann benachrichtigen?
+# "always" - Immer nach Ausführung
+# "changes" - Nur wenn Änderungen vorgenommen wurden
+# "errors" - Nur bei Fehlern
+NOTIFY_ON="changes"
+
+# --- Notify-Send (Desktop-Benachrichtigungen) ---
+NOTIFY_DESKTOP_ENABLED=false
+
+# --- Ntfy (https://ntfy.sh) ---
+NOTIFY_NTFY_ENABLED=false
+NOTIFY_NTFY_URL="https://ntfy.sh"
+NOTIFY_NTFY_TOPIC="crowdsec-manager"
+NOTIFY_NTFY_PRIORITY="default"
+NOTIFY_NTFY_TOKEN=""
+# Tags/Emojis für Ntfy-Nachrichten
+NOTIFY_NTFY_TAGS="shield,white_check_mark"
+
+# --- Gotify (https://gotify.net) ---
+NOTIFY_GOTIFY_ENABLED=false
+# URL deiner Gotify-Instanz (mit Protokoll, ohne trailing slash)
+NOTIFY_GOTIFY_URL="https://gotify.example.com"
+# App-Token aus Gotify (unter Apps -> Token erstellen)
+NOTIFY_GOTIFY_TOKEN=""
+# Priorität der Nachricht (1-10, höher = wichtiger)
+# 1-3: Niedrig, 4-7: Normal, 8-10: Hoch/Kritisch
+NOTIFY_GOTIFY_PRIORITY=5
+
+# --- E-Mail ---
+NOTIFY_EMAIL_ENABLED=false
+NOTIFY_EMAIL_TO="admin@example.com"
+NOTIFY_EMAIL_FROM="crowdsec@example.com"
+NOTIFY_EMAIL_SUBJECT="CrowdSec Manager Report"
+# SMTP-Server Konfiguration
+NOTIFY_EMAIL_SMTP_SERVER="smtp.example.com"
+NOTIFY_EMAIL_SMTP_PORT="587"
+NOTIFY_EMAIL_SMTP_USER=""
+NOTIFY_EMAIL_SMTP_PASS=""
+NOTIFY_EMAIL_SMTP_TLS=true
+
+# ----------------------------------------------------------------------------
+# Erweiterte Optionen
+# ----------------------------------------------------------------------------
+
+# Automatische Bereinigung: Entfernt IPs aus der Allowlist, die nicht mehr
+# zu den konfigurierten Domains/IPs gehören (true/false)
+AUTO_CLEANUP=true
+
+# Health-Check vor Ausführung (true/false)
+HEALTH_CHECK=true
+
+# Dry-Run Modus als Standard (kann mit --dry-run überschrieben werden)
+DRY_RUN=false
diff --git a/crowdsec-manager.sh b/crowdsec-manager.sh
new file mode 100644
index 0000000..a1c6888
--- /dev/null
+++ b/crowdsec-manager.sh
@@ -0,0 +1,1743 @@
+#!/bin/bash
+# ============================================================================
+# CrowdSec Manager v0.1.0
+# ============================================================================
+# Verwaltet automatisch eine CrowdSec-Allowlist und bietet interaktive
+# CrowdSec-Administration über ein Menü.
+#
+# Autor: Patrick Asmus
+# www.patrick-asmus.de
+# Lizenz: MIT
+# ============================================================================
+
+set -euo pipefail
+
+# ----------------------------------------------------------------------------
+# Globale Variablen
+# ----------------------------------------------------------------------------
+SCRIPT_NAME="$(basename "$0")"
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SCRIPT_VERSION="0.1.0"
+CONFIG_FILE="${SCRIPT_DIR}/config/crowdsec-manager.conf"
+
+# Farben (werden ggf. deaktiviert wenn nicht im Terminal)
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[0;33m'
+BLUE='\033[0;34m'
+CYAN='\033[0;36m'
+MAGENTA='\033[0;35m'
+BOLD='\033[1m'
+DIM='\033[2m'
+NC='\033[0m'
+
+# Statistiken
+STAT_ADDED=0
+STAT_REMOVED=0
+STAT_UNCHANGED=0
+STAT_ERRORS=0
+STAT_RESOLVED_DOMAINS=0
+STAT_TOTAL_IPS=0
+REPORT_MESSAGES=()
+
+# Temporäre Dateien
+TEMP_DIR=""
+CURRENT_IPS_FILE=""
+NEW_IPS_FILE=""
+
+# ============================================================================
+# TEIL 1: HILFSFUNKTIONEN
+# ============================================================================
+
+# Farben deaktivieren wenn nicht im Terminal oder wenn als Cron ausgeführt
+disable_colors_if_needed() {
+ if [[ ! -t 1 ]] || [[ -n "${CRON:-}" ]]; then
+ RED=""
+ GREEN=""
+ YELLOW=""
+ BLUE=""
+ CYAN=""
+ MAGENTA=""
+ BOLD=""
+ DIM=""
+ NC=""
+ fi
+}
+
+# Trennlinie zeichnen
+draw_line() {
+ local char="${1:--}"
+ local width="${2:-60}"
+ printf '%*s\n' "$width" '' | tr ' ' "$char"
+}
+
+# Logging-Funktion
+log() {
+ local level="$1"
+ shift
+ local message="$*"
+ local timestamp
+ timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
+
+ local -A levels=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3)
+ local current_level="${levels[${LOG_LEVEL:-INFO}]:-1}"
+ local msg_level="${levels[$level]:-1}"
+
+ if [[ $msg_level -lt $current_level ]]; then
+ return 0
+ fi
+
+ local color=""
+ case "$level" in
+ DEBUG) color="$CYAN" ;;
+ INFO) color="$GREEN" ;;
+ WARN) color="$YELLOW" ;;
+ ERROR) color="$RED" ;;
+ esac
+
+ local log_line="[${timestamp}] [${level}] ${message}"
+ local colored_line="${color}[${timestamp}] [${BOLD}${level}${NC}${color}] ${message}${NC}"
+
+ echo -e "$colored_line"
+
+ if [[ -n "${LOG_FILE:-}" ]]; then
+ echo "$log_line" >> "$LOG_FILE" 2>/dev/null || true
+ fi
+}
+
+# Log-Rotation
+rotate_logs() {
+ if [[ -z "${LOG_FILE:-}" ]] || [[ "${LOG_MAX_SIZE_KB:-0}" -eq 0 ]]; then
+ return 0
+ fi
+
+ if [[ ! -f "$LOG_FILE" ]]; then
+ return 0
+ fi
+
+ local size_kb
+ size_kb=$(du -k "$LOG_FILE" 2>/dev/null | cut -f1)
+
+ if [[ "$size_kb" -ge "$LOG_MAX_SIZE_KB" ]]; then
+ log "INFO" "Log-Rotation wird durchgeführt (${size_kb}KB >= ${LOG_MAX_SIZE_KB}KB)"
+
+ for ((i = LOG_ROTATE_COUNT - 1; i >= 1; i--)); do
+ local prev=$((i - 1))
+ if [[ -f "${LOG_FILE}.${prev}" ]]; then
+ mv "${LOG_FILE}.${prev}" "${LOG_FILE}.${i}"
+ fi
+ done
+
+ if [[ -f "$LOG_FILE" ]]; then
+ mv "$LOG_FILE" "${LOG_FILE}.0"
+ fi
+
+ touch "$LOG_FILE"
+ fi
+}
+
+# Temporäre Dateien
+setup_temp() {
+ TEMP_DIR="$(mktemp -d /tmp/crowdsec-manager-XXXXXX)"
+ CURRENT_IPS_FILE="${TEMP_DIR}/current_ips.txt"
+ NEW_IPS_FILE="${TEMP_DIR}/new_ips.txt"
+ touch "$CURRENT_IPS_FILE" "$NEW_IPS_FILE"
+}
+
+cleanup_temp() {
+ if [[ -n "${TEMP_DIR:-}" ]] && [[ -d "$TEMP_DIR" ]]; then
+ rm -rf "$TEMP_DIR"
+ fi
+}
+
+# Lock-File Management
+acquire_lock() {
+ if [[ "${LOCK_ENABLED:-false}" != "true" ]]; then
+ return 0
+ fi
+
+ local lock_file="${LOCK_FILE:-/tmp/crowdsec-manager.lock}"
+ local timeout="${LOCK_TIMEOUT:-300}"
+ local waited=0
+
+ while [[ -f "$lock_file" ]]; do
+ local lock_pid
+ lock_pid=$(cat "$lock_file" 2>/dev/null || echo "")
+
+ if [[ -n "$lock_pid" ]] && ! kill -0 "$lock_pid" 2>/dev/null; then
+ log "WARN" "Verwaistes Lock-File gefunden (PID: $lock_pid). Wird entfernt."
+ rm -f "$lock_file"
+ break
+ fi
+
+ if [[ "$timeout" -eq 0 ]] || [[ "$waited" -ge "$timeout" ]]; then
+ log "ERROR" "Lock-File existiert bereits (PID: $lock_pid). Abbruch."
+ return 1
+ fi
+
+ log "DEBUG" "Warte auf Lock-Freigabe... (${waited}s/${timeout}s)"
+ sleep 5
+ waited=$((waited + 5))
+ done
+
+ echo $$ > "$lock_file"
+ log "DEBUG" "Lock erworben (PID: $$)"
+}
+
+release_lock() {
+ if [[ "${LOCK_ENABLED:-false}" != "true" ]]; then
+ return 0
+ fi
+
+ local lock_file="${LOCK_FILE:-/tmp/crowdsec-manager.lock}"
+ if [[ -f "$lock_file" ]]; then
+ local lock_pid
+ lock_pid=$(cat "$lock_file" 2>/dev/null || echo "")
+ if [[ "$lock_pid" == "$$" ]]; then
+ rm -f "$lock_file"
+ log "DEBUG" "Lock freigegeben (PID: $$)"
+ fi
+ fi
+}
+
+# ============================================================================
+# TEIL 2: VALIDIERUNG
+# ============================================================================
+
+is_valid_ipv4() {
+ local ip="$1"
+ local regex='^([0-9]{1,3}\.){3}[0-9]{1,3}$'
+ if [[ "$ip" =~ $regex ]]; then
+ local IFS='.'
+ read -ra octets <<< "$ip"
+ for octet in "${octets[@]}"; do
+ if [[ "$octet" -gt 255 ]]; then
+ return 1
+ fi
+ done
+ return 0
+ fi
+ return 1
+}
+
+is_valid_ipv6() {
+ local ip="$1"
+ if [[ "$ip" =~ ^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$ ]] || \
+ [[ "$ip" =~ ^([0-9a-fA-F]{0,4}:){1,7}:$ ]] || \
+ [[ "$ip" =~ ^::([0-9a-fA-F]{0,4}:){0,6}[0-9a-fA-F]{0,4}$ ]] || \
+ [[ "$ip" =~ ^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$ ]]; then
+ return 0
+ fi
+ return 1
+}
+
+is_cidr() {
+ local entry="$1"
+ [[ "$entry" =~ / ]]
+}
+
+is_ip_or_cidr() {
+ local entry="$1"
+
+ if is_cidr "$entry"; then
+ local ip="${entry%/*}"
+ local mask="${entry#*/}"
+ if is_valid_ipv4 "$ip"; then
+ [[ "$mask" -ge 0 && "$mask" -le 32 ]] 2>/dev/null && return 0
+ elif is_valid_ipv6 "$ip"; then
+ [[ "$mask" -ge 0 && "$mask" -le 128 ]] 2>/dev/null && return 0
+ fi
+ return 1
+ fi
+
+ is_valid_ipv4 "$entry" || is_valid_ipv6 "$entry"
+}
+
+is_domain() {
+ local entry="$1"
+ if ! is_valid_ipv4 "$entry" && ! is_valid_ipv6 "$entry" && ! is_cidr "$entry"; then
+ if [[ "$entry" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$ ]]; then
+ return 0
+ fi
+ fi
+ return 1
+}
+
+# ============================================================================
+# TEIL 3: DNS-AUFLÖSUNG
+# ============================================================================
+
+resolve_domain() {
+ local domain="$1"
+ local resolved_ips=()
+
+ log "DEBUG" "Löse Domain auf: $domain"
+
+ local dig_opts="+short +timeout=${DNS_TIMEOUT:-5} +retry=${DNS_RETRIES:-3}"
+ if [[ -n "${DNS_SERVER:-}" ]]; then
+ dig_opts+=" @${DNS_SERVER}"
+ fi
+
+ # IPv4 (A Records)
+ local ipv4_results
+ ipv4_results=$(dig $dig_opts A "$domain" 2>/dev/null | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' || true)
+
+ if [[ -n "$ipv4_results" ]]; then
+ while IFS= read -r ip; do
+ if is_valid_ipv4 "$ip"; then
+ resolved_ips+=("$ip")
+ log "DEBUG" " IPv4 aufgelöst: $domain -> $ip"
+ fi
+ done <<< "$ipv4_results"
+ fi
+
+ # IPv6 (AAAA Records)
+ if [[ "${RESOLVE_IPV6:-true}" == "true" ]]; then
+ local ipv6_results
+ ipv6_results=$(dig $dig_opts AAAA "$domain" 2>/dev/null | grep -E '^[0-9a-fA-F:]+$' | grep ':' || true)
+
+ if [[ -n "$ipv6_results" ]]; then
+ while IFS= read -r ip; do
+ if is_valid_ipv6 "$ip"; then
+ resolved_ips+=("$ip")
+ log "DEBUG" " IPv6 aufgelöst: $domain -> $ip"
+ fi
+ done <<< "$ipv6_results"
+ fi
+ fi
+
+ if [[ ${#resolved_ips[@]} -eq 0 ]]; then
+ log "WARN" "Keine IPs für Domain '$domain' aufgelöst"
+ STAT_ERRORS=$((STAT_ERRORS + 1))
+ REPORT_MESSAGES+=("WARN: Keine Auflösung für $domain")
+ return 1
+ fi
+
+ STAT_RESOLVED_DOMAINS=$((STAT_RESOLVED_DOMAINS + 1))
+ printf '%s\n' "${resolved_ips[@]}"
+}
+
+resolve_all_entries() {
+ log "INFO" "Starte DNS-Auflösung für ${#ALLOWLIST_ENTRIES[@]} Einträge..."
+
+ for entry in "${ALLOWLIST_ENTRIES[@]}"; do
+ entry=$(echo "$entry" | sed 's/#.*$//' | xargs)
+ [[ -z "$entry" ]] && continue
+
+ if is_ip_or_cidr "$entry"; then
+ log "DEBUG" "Direkter Eintrag: $entry"
+ echo "$entry" >> "$NEW_IPS_FILE"
+ STAT_TOTAL_IPS=$((STAT_TOTAL_IPS + 1))
+ elif is_domain "$entry"; then
+ local resolved
+ if resolved=$(resolve_domain "$entry"); then
+ while IFS= read -r ip; do
+ echo "$ip" >> "$NEW_IPS_FILE"
+ STAT_TOTAL_IPS=$((STAT_TOTAL_IPS + 1))
+ done <<< "$resolved"
+ fi
+ else
+ log "WARN" "Unbekannter Eintragstyp: '$entry' - wird übersprungen"
+ STAT_ERRORS=$((STAT_ERRORS + 1))
+ fi
+ done
+
+ if [[ -f "$NEW_IPS_FILE" ]]; then
+ sort -u "$NEW_IPS_FILE" -o "$NEW_IPS_FILE"
+ STAT_TOTAL_IPS=$(wc -l < "$NEW_IPS_FILE")
+ fi
+
+ log "INFO" "${STAT_TOTAL_IPS} eindeutige IPs/CIDRs aus ${STAT_RESOLVED_DOMAINS} aufgelösten Domains ermittelt"
+}
+
+# ============================================================================
+# TEIL 4: CROWDSEC INTERAKTION (Kern-Funktionen)
+# ============================================================================
+
+# CrowdSec CLI Wrapper
+cscli_exec() {
+ eval "$CSCLI_CMD $*"
+}
+
+crowdsec_health_check() {
+ if [[ "${HEALTH_CHECK:-true}" != "true" ]]; then
+ return 0
+ fi
+
+ log "INFO" "Prüfe CrowdSec-Erreichbarkeit..."
+
+ if ! cscli_exec "version" &>/dev/null; then
+ log "ERROR" "CrowdSec ist nicht erreichbar! Befehl: '$CSCLI_CMD version'"
+ REPORT_MESSAGES+=("ERROR: CrowdSec nicht erreichbar")
+ return 1
+ fi
+
+ log "INFO" "CrowdSec ist erreichbar"
+ return 0
+}
+
+ensure_allowlist_exists() {
+ # Prüfe ob die Allowlist bereits existiert
+ if cscli_exec "allowlists inspect ${ALLOWLIST_NAME}" &>/dev/null; then
+ log "DEBUG" "Allowlist '${ALLOWLIST_NAME}' existiert bereits"
+ return 0
+ fi
+
+ # Allowlist erstellen
+ log "INFO" "Erstelle Allowlist '${ALLOWLIST_NAME}'..."
+ if cscli_exec "allowlists create ${ALLOWLIST_NAME} -d '${ALLOWLIST_DESCRIPTION}'" 2>&1; then
+ log "INFO" "Allowlist '${ALLOWLIST_NAME}' erfolgreich erstellt"
+ return 0
+ else
+ log "ERROR" "Fehler beim Erstellen der Allowlist '${ALLOWLIST_NAME}'"
+ return 1
+ fi
+}
+
+get_current_allowlist() {
+ log "INFO" "Rufe aktuelle Allowlist '${ALLOWLIST_NAME}' ab..."
+
+ if ! ensure_allowlist_exists; then
+ return 1
+ fi
+
+ # Aktuelle IPs aus der Allowlist auslesen
+ local inspect_output
+ inspect_output=$(cscli_exec "allowlists inspect ${ALLOWLIST_NAME} -o json" 2>/dev/null || echo "null")
+
+ if [[ "$inspect_output" == "null" ]] || [[ -z "$inspect_output" ]]; then
+ log "DEBUG" "Keine bestehenden Einträge in der Allowlist gefunden"
+ return 0
+ fi
+
+ # IPs/CIDRs aus dem JSON extrahieren (Feld "items" -> "ip_or_range")
+ echo "$inspect_output" | grep -oP '"ip_or_range"\s*:\s*"\K[^"]+' | sort -u > "$CURRENT_IPS_FILE" 2>/dev/null || true
+
+ local count
+ count=$(wc -l < "$CURRENT_IPS_FILE" 2>/dev/null || echo "0")
+ log "INFO" "${count} bestehende Einträge in der Allowlist gefunden"
+}
+
+add_to_allowlist() {
+ local ip="$1"
+
+ log "DEBUG" "Füge hinzu: $ip"
+
+ if [[ "${DRY_RUN:-false}" == "true" ]]; then
+ log "INFO" "[DRY-RUN] Würde hinzufügen: $ip"
+ return 0
+ fi
+
+ local result
+ if result=$(cscli_exec "allowlists add ${ALLOWLIST_NAME} $ip -d '${ALLOWLIST_REASON}'" 2>&1); then
+ log "DEBUG" "Erfolgreich hinzugefügt: $ip"
+ STAT_ADDED=$((STAT_ADDED + 1))
+ return 0
+ else
+ log "ERROR" "Fehler beim Hinzufügen von $ip: $result"
+ STAT_ERRORS=$((STAT_ERRORS + 1))
+ REPORT_MESSAGES+=("ERROR: Konnte $ip nicht hinzufügen: $result")
+ return 1
+ fi
+}
+
+remove_from_allowlist() {
+ local ip="$1"
+
+ log "DEBUG" "Entferne: $ip"
+
+ if [[ "${DRY_RUN:-false}" == "true" ]]; then
+ log "INFO" "[DRY-RUN] Würde entfernen: $ip"
+ return 0
+ fi
+
+ local result
+ if result=$(cscli_exec "allowlists remove ${ALLOWLIST_NAME} $ip" 2>&1); then
+ log "DEBUG" "Erfolgreich entfernt: $ip"
+ STAT_REMOVED=$((STAT_REMOVED + 1))
+ return 0
+ else
+ log "ERROR" "Fehler beim Entfernen von $ip: $result"
+ STAT_ERRORS=$((STAT_ERRORS + 1))
+ REPORT_MESSAGES+=("ERROR: Konnte $ip nicht entfernen: $result")
+ return 1
+ fi
+}
+
+sync_allowlist() {
+ log "INFO" "Synchronisiere Allowlist..."
+
+ local to_add
+ to_add=$(comm -23 "$NEW_IPS_FILE" "$CURRENT_IPS_FILE" 2>/dev/null || true)
+
+ local to_remove=""
+ if [[ "${AUTO_CLEANUP:-true}" == "true" ]]; then
+ to_remove=$(comm -13 "$NEW_IPS_FILE" "$CURRENT_IPS_FILE" 2>/dev/null || true)
+ fi
+
+ local unchanged
+ unchanged=$(comm -12 "$NEW_IPS_FILE" "$CURRENT_IPS_FILE" 2>/dev/null || true)
+ if [[ -n "$unchanged" ]]; then
+ STAT_UNCHANGED=$(echo "$unchanged" | wc -l)
+ fi
+
+ if [[ -n "$to_add" ]]; then
+ log "INFO" "Neue IPs zum Hinzufügen gefunden:"
+ while IFS= read -r ip; do
+ [[ -z "$ip" ]] && continue
+ log "INFO" " + $ip"
+ add_to_allowlist "$ip"
+ done <<< "$to_add"
+ else
+ log "INFO" "Keine neuen IPs zum Hinzufügen"
+ fi
+
+ if [[ -n "$to_remove" ]]; then
+ log "INFO" "Veraltete IPs zum Entfernen gefunden:"
+ while IFS= read -r ip; do
+ [[ -z "$ip" ]] && continue
+ log "INFO" " - $ip"
+ remove_from_allowlist "$ip"
+ done <<< "$to_remove"
+ else
+ log "INFO" "Keine veralteten IPs zum Entfernen"
+ fi
+}
+
+# ============================================================================
+# TEIL 5: BACKUP
+# ============================================================================
+
+create_backup() {
+ if [[ "${BACKUP_ENABLED:-false}" != "true" ]]; then
+ return 0
+ fi
+
+ log "INFO" "Erstelle Backup..."
+
+ local backup_dir="${BACKUP_DIR:-/var/backup/crowdsec-manager}"
+ mkdir -p "$backup_dir"
+
+ local backup_file="${backup_dir}/allowlist_$(date '+%Y%m%d_%H%M%S').bak"
+
+ if [[ -f "$CURRENT_IPS_FILE" ]]; then
+ cp "$CURRENT_IPS_FILE" "$backup_file"
+ log "INFO" "Backup erstellt: $backup_file"
+ fi
+
+ local retain=${BACKUP_RETAIN_COUNT:-7}
+ local backups
+ backups=$(find "$backup_dir" -name "allowlist_*.bak" -type f 2>/dev/null | sort -r)
+ local count=0
+
+ while IFS= read -r file; do
+ count=$((count + 1))
+ if [[ $count -gt $retain ]]; then
+ rm -f "$file"
+ log "DEBUG" "Altes Backup gelöscht: $file"
+ fi
+ done <<< "$backups"
+}
+
+# ============================================================================
+# TEIL 6: BENACHRICHTIGUNGEN (Ntfy, Gotify, E-Mail, Desktop)
+# ============================================================================
+
+build_notification_message() {
+ local status="OK"
+ if [[ $STAT_ERRORS -gt 0 ]]; then
+ status="FEHLER"
+ fi
+
+ local msg=""
+ msg+="=== CrowdSec Manager Report ===\n"
+ msg+="Status: $status\n"
+ msg+="Zeitpunkt: $(date '+%Y-%m-%d %H:%M:%S')\n"
+ msg+="---\n"
+ msg+="Domains aufgelöst: $STAT_RESOLVED_DOMAINS\n"
+ msg+="IPs gesamt: $STAT_TOTAL_IPS\n"
+ msg+="Hinzugefügt: $STAT_ADDED\n"
+ msg+="Entfernt: $STAT_REMOVED\n"
+ msg+="Unverändert: $STAT_UNCHANGED\n"
+ msg+="Fehler: $STAT_ERRORS\n"
+
+ if [[ ${#REPORT_MESSAGES[@]} -gt 0 ]]; then
+ msg+="---\n"
+ msg+="Details:\n"
+ for m in "${REPORT_MESSAGES[@]}"; do
+ msg+=" $m\n"
+ done
+ fi
+
+ if [[ "${DRY_RUN:-false}" == "true" ]]; then
+ msg+="---\n"
+ msg+="HINWEIS: Dry-Run Modus - Keine Änderungen durchgeführt\n"
+ fi
+
+ echo -e "$msg"
+}
+
+should_notify() {
+ local notify_on="${NOTIFY_ON:-changes}"
+ case "$notify_on" in
+ always) return 0 ;;
+ changes)
+ if [[ $STAT_ADDED -gt 0 ]] || [[ $STAT_REMOVED -gt 0 ]] || [[ $STAT_ERRORS -gt 0 ]]; then
+ return 0
+ fi
+ return 1
+ ;;
+ errors)
+ [[ $STAT_ERRORS -gt 0 ]] && return 0
+ return 1
+ ;;
+ esac
+ return 1
+}
+
+send_notify_desktop() {
+ if [[ "${NOTIFY_DESKTOP_ENABLED:-false}" != "true" ]]; then
+ return 0
+ fi
+
+ if ! command -v notify-send &>/dev/null; then
+ log "WARN" "notify-send nicht gefunden. Desktop-Benachrichtigung übersprungen."
+ return 1
+ fi
+
+ local urgency="normal"
+ [[ $STAT_ERRORS -gt 0 ]] && urgency="critical"
+
+ local summary="CrowdSec Allowlist: +${STAT_ADDED} -${STAT_REMOVED} (${STAT_ERRORS} Fehler)"
+ notify-send -u "$urgency" "CrowdSec Manager" "$summary"
+ log "DEBUG" "Desktop-Benachrichtigung gesendet"
+}
+
+send_notify_ntfy() {
+ if [[ "${NOTIFY_NTFY_ENABLED:-false}" != "true" ]]; then
+ return 0
+ fi
+
+ if ! command -v curl &>/dev/null; then
+ log "WARN" "curl nicht gefunden. Ntfy-Benachrichtigung übersprungen."
+ return 1
+ fi
+
+ local message
+ message=$(build_notification_message)
+
+ local title="CrowdSec Manager"
+ local priority="${NOTIFY_NTFY_PRIORITY:-default}"
+ local tags="${NOTIFY_NTFY_TAGS:-shield}"
+
+ if [[ $STAT_ERRORS -gt 0 ]]; then
+ priority="high"
+ tags="warning,shield"
+ fi
+
+ local curl_opts=(
+ -s
+ -H "Title: $title"
+ -H "Priority: $priority"
+ -H "Tags: $tags"
+ )
+
+ if [[ -n "${NOTIFY_NTFY_TOKEN:-}" ]]; then
+ curl_opts+=(-H "Authorization: Bearer ${NOTIFY_NTFY_TOKEN}")
+ fi
+
+ local url="${NOTIFY_NTFY_URL:-https://ntfy.sh}/${NOTIFY_NTFY_TOPIC:-crowdsec-manager}"
+
+ if curl "${curl_opts[@]}" -d "$message" "$url" &>/dev/null; then
+ log "DEBUG" "Ntfy-Benachrichtigung gesendet"
+ else
+ log "WARN" "Ntfy-Benachrichtigung fehlgeschlagen"
+ fi
+}
+
+send_notify_gotify() {
+ if [[ "${NOTIFY_GOTIFY_ENABLED:-false}" != "true" ]]; then
+ return 0
+ fi
+
+ if ! command -v curl &>/dev/null; then
+ log "WARN" "curl nicht gefunden. Gotify-Benachrichtigung übersprungen."
+ return 1
+ fi
+
+ local gotify_url="${NOTIFY_GOTIFY_URL:-}"
+ local gotify_token="${NOTIFY_GOTIFY_TOKEN:-}"
+
+ if [[ -z "$gotify_url" ]] || [[ -z "$gotify_token" ]]; then
+ log "WARN" "Gotify URL oder Token nicht konfiguriert."
+ return 1
+ fi
+
+ local message
+ message=$(build_notification_message)
+
+ local title="CrowdSec Manager"
+ local priority="${NOTIFY_GOTIFY_PRIORITY:-5}"
+
+ # Bei Fehlern höhere Priorität
+ if [[ $STAT_ERRORS -gt 0 ]]; then
+ priority=8
+ fi
+
+ local api_url="${gotify_url}/message?token=${gotify_token}"
+
+ # JSON-Payload erstellen – Nachricht für JSON escapen
+ local escaped_message
+ escaped_message=$(echo "$message" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))" 2>/dev/null || \
+ echo "$message" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g' | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/^/"/;s/$/"/')
+
+ local escaped_title
+ escaped_title=$(echo "$title" | sed 's/"/\\"/g')
+
+ if curl -s -X POST "$api_url" \
+ -H "Content-Type: application/json" \
+ -d "{\"title\":\"${escaped_title}\",\"message\":${escaped_message},\"priority\":${priority}}" \
+ &>/dev/null; then
+ log "DEBUG" "Gotify-Benachrichtigung gesendet an ${gotify_url}"
+ else
+ log "WARN" "Gotify-Benachrichtigung fehlgeschlagen"
+ fi
+}
+
+send_notify_email() {
+ if [[ "${NOTIFY_EMAIL_ENABLED:-false}" != "true" ]]; then
+ return 0
+ fi
+
+ local message
+ message=$(build_notification_message)
+
+ local to="${NOTIFY_EMAIL_TO:-}"
+ local from="${NOTIFY_EMAIL_FROM:-crowdsec@$(hostname)}"
+ local subject="${NOTIFY_EMAIL_SUBJECT:-CrowdSec Manager Report}"
+
+ if command -v curl &>/dev/null && [[ -n "${NOTIFY_EMAIL_SMTP_SERVER:-}" ]]; then
+ local smtp_url="smtp://${NOTIFY_EMAIL_SMTP_SERVER}:${NOTIFY_EMAIL_SMTP_PORT:-587}"
+
+ if [[ "${NOTIFY_EMAIL_SMTP_TLS:-true}" == "true" ]]; then
+ smtp_url="smtps://${NOTIFY_EMAIL_SMTP_SERVER}:${NOTIFY_EMAIL_SMTP_PORT:-465}"
+ fi
+
+ local email_body
+ email_body="From: ${from}\r\nTo: ${to}\r\nSubject: ${subject}\r\n\r\n${message}"
+
+ local curl_opts=(
+ -s
+ --url "$smtp_url"
+ --mail-from "$from"
+ --mail-rcpt "$to"
+ )
+
+ if [[ -n "${NOTIFY_EMAIL_SMTP_USER:-}" ]]; then
+ curl_opts+=(--user "${NOTIFY_EMAIL_SMTP_USER}:${NOTIFY_EMAIL_SMTP_PASS:-}")
+ fi
+
+ if echo -e "$email_body" | curl "${curl_opts[@]}" -T - 2>/dev/null; then
+ log "DEBUG" "E-Mail-Benachrichtigung gesendet an $to"
+ else
+ log "WARN" "E-Mail-Benachrichtigung fehlgeschlagen"
+ fi
+ elif command -v mail &>/dev/null; then
+ echo -e "$message" | mail -s "$subject" -r "$from" "$to"
+ log "DEBUG" "E-Mail-Benachrichtigung gesendet an $to (via mail)"
+ else
+ log "WARN" "Weder curl noch mail verfügbar. E-Mail-Benachrichtigung übersprungen."
+ fi
+}
+
+send_notifications() {
+ if [[ "${NOTIFY_ENABLED:-false}" != "true" ]]; then
+ return 0
+ fi
+
+ if ! should_notify; then
+ log "DEBUG" "Keine Benachrichtigung erforderlich (Bedingung: ${NOTIFY_ON:-changes})"
+ return 0
+ fi
+
+ log "INFO" "Sende Benachrichtigungen..."
+ send_notify_desktop
+ send_notify_ntfy
+ send_notify_gotify
+ send_notify_email
+}
+
+# ============================================================================
+# TEIL 7: CROWDSEC ADMIN-FUNKTIONEN (Interaktiv)
+# ============================================================================
+
+# ---[ Metriken anzeigen ]---
+admin_show_metrics() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== CrowdSec Metriken ===${NC}"
+ draw_line "=" 60
+ echo ""
+ cscli_exec "metrics" 2>/dev/null || {
+ echo -e "${RED}Fehler beim Abrufen der Metriken.${NC}"
+ }
+ echo ""
+}
+
+# ---[ Alle Decisions anzeigen ]---
+admin_list_decisions() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Aktuelle Decisions ===${NC}"
+ draw_line "=" 60
+ echo ""
+ cscli_exec "decisions list" 2>/dev/null || {
+ echo -e "${YELLOW}Keine Decisions vorhanden oder Fehler beim Abrufen.${NC}"
+ }
+ echo ""
+}
+
+# ---[ Decision für eine IP suchen ]---
+admin_search_decision() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Decision suchen ===${NC}"
+ echo -n -e "${CYAN}IP-Adresse eingeben: ${NC}"
+ read -r search_ip
+
+ if [[ -z "$search_ip" ]]; then
+ echo -e "${YELLOW}Keine IP eingegeben.${NC}"
+ return
+ fi
+
+ echo ""
+ echo -e "${DIM}Suche nach Decisions für: $search_ip${NC}"
+ draw_line "-" 60
+ cscli_exec "decisions list -i '$search_ip'" 2>/dev/null || {
+ echo -e "${YELLOW}Keine Decisions für $search_ip gefunden.${NC}"
+ }
+ echo ""
+}
+
+# ---[ Decision manuell hinzufügen ]---
+admin_add_decision() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Decision manuell hinzufügen ===${NC}"
+ echo ""
+
+ # IP eingeben
+ echo -n -e "${CYAN}IP-Adresse oder CIDR: ${NC}"
+ read -r add_ip
+
+ if [[ -z "$add_ip" ]]; then
+ echo -e "${YELLOW}Keine IP eingegeben. Abbruch.${NC}"
+ return
+ fi
+
+ # Typ wählen
+ echo ""
+ echo -e "${BOLD}Typ auswählen:${NC}"
+ echo " 1) ban - IP sperren"
+ echo " 2) allow - IP erlauben (Allowlist)"
+ echo " 3) captcha - Captcha anzeigen"
+ echo " 4) throttle - Drosselung"
+ echo -n -e "${CYAN}Auswahl [1-4]: ${NC}"
+ read -r type_choice
+
+ local decision_type
+ case "$type_choice" in
+ 1) decision_type="ban" ;;
+ 2) decision_type="allow" ;;
+ 3) decision_type="captcha" ;;
+ 4) decision_type="throttle" ;;
+ *)
+ echo -e "${YELLOW}Ungültige Auswahl. Verwende 'ban'.${NC}"
+ decision_type="ban"
+ ;;
+ esac
+
+ # Dauer eingeben
+ echo -n -e "${CYAN}Dauer (z.B. 10m, 1h, 24h, 7d) [Standard: 4h]: ${NC}"
+ read -r duration
+ duration="${duration:-4h}"
+
+ # Grund eingeben
+ echo -n -e "${CYAN}Grund (optional) [Standard: manual via script]: ${NC}"
+ read -r reason
+ reason="${reason:-manual via script}"
+
+ # Scope bestimmen
+ local scope="ip"
+ if is_cidr "$add_ip"; then
+ scope="range"
+ fi
+
+ # Zusammenfassung
+ echo ""
+ draw_line "-" 60
+ echo -e "${BOLD}Zusammenfassung:${NC}"
+ echo -e " IP/Range: ${CYAN}$add_ip${NC}"
+ echo -e " Typ: ${CYAN}$decision_type${NC}"
+ echo -e " Scope: ${CYAN}$scope${NC}"
+ echo -e " Dauer: ${CYAN}$duration${NC}"
+ echo -e " Grund: ${CYAN}$reason${NC}"
+ draw_line "-" 60
+ echo ""
+
+ echo -n -e "${YELLOW}Decision hinzufügen? [j/N]: ${NC}"
+ read -r confirm
+ if [[ "$confirm" =~ ^[jJyY]$ ]]; then
+ echo ""
+ if cscli_exec "decisions add --type '$decision_type' --scope '$scope' --value '$add_ip' --duration '$duration' --reason '$reason'" 2>&1; then
+ echo -e "${GREEN}Decision erfolgreich hinzugefügt!${NC}"
+ else
+ echo -e "${RED}Fehler beim Hinzufügen der Decision.${NC}"
+ fi
+ else
+ echo -e "${YELLOW}Abgebrochen.${NC}"
+ fi
+ echo ""
+}
+
+# ---[ Decision entfernen ]---
+admin_remove_decision() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Decision entfernen ===${NC}"
+ echo ""
+ echo -e "${BOLD}Entfernen nach:${NC}"
+ echo " 1) IP-Adresse"
+ echo " 2) Decision-ID"
+ echo -n -e "${CYAN}Auswahl [1-2]: ${NC}"
+ read -r remove_choice
+
+ case "$remove_choice" in
+ 1)
+ echo -n -e "${CYAN}IP-Adresse: ${NC}"
+ read -r remove_ip
+ if [[ -z "$remove_ip" ]]; then
+ echo -e "${YELLOW}Keine IP eingegeben.${NC}"
+ return
+ fi
+ echo -n -e "${YELLOW}Decision für $remove_ip wirklich entfernen? [j/N]: ${NC}"
+ read -r confirm
+ if [[ "$confirm" =~ ^[jJyY]$ ]]; then
+ if cscli_exec "decisions delete --ip '$remove_ip'" 2>&1; then
+ echo -e "${GREEN}Decision entfernt!${NC}"
+ else
+ echo -e "${RED}Fehler beim Entfernen.${NC}"
+ fi
+ else
+ echo -e "${YELLOW}Abgebrochen.${NC}"
+ fi
+ ;;
+ 2)
+ echo -n -e "${CYAN}Decision-ID: ${NC}"
+ read -r remove_id
+ if [[ -z "$remove_id" ]]; then
+ echo -e "${YELLOW}Keine ID eingegeben.${NC}"
+ return
+ fi
+ if ! [[ "$remove_id" =~ ^[0-9]+$ ]]; then
+ echo -e "${RED}Ungültige Decision-ID (nur Zahlen erlaubt).${NC}"
+ return
+ fi
+ echo -n -e "${YELLOW}Decision #$remove_id wirklich entfernen? [j/N]: ${NC}"
+ read -r confirm
+ if [[ "$confirm" =~ ^[jJyY]$ ]]; then
+ if cscli_exec "decisions delete --id '$remove_id'" 2>&1; then
+ echo -e "${GREEN}Decision #$remove_id entfernt!${NC}"
+ else
+ echo -e "${RED}Fehler beim Entfernen.${NC}"
+ fi
+ else
+ echo -e "${YELLOW}Abgebrochen.${NC}"
+ fi
+ ;;
+ *)
+ echo -e "${YELLOW}Ungültige Auswahl.${NC}"
+ ;;
+ esac
+ echo ""
+}
+
+# ---[ Alerts anzeigen ]---
+admin_list_alerts() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Aktuelle Alerts ===${NC}"
+ draw_line "=" 60
+ echo ""
+ cscli_exec "alerts list" 2>/dev/null || {
+ echo -e "${YELLOW}Keine Alerts vorhanden oder Fehler beim Abrufen.${NC}"
+ }
+ echo ""
+}
+
+# ---[ Alert Detail-Inspektion ]---
+admin_inspect_alert() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Alert inspizieren ===${NC}"
+ echo -n -e "${CYAN}Alert-ID eingeben: ${NC}"
+ read -r alert_id
+
+ if [[ -z "$alert_id" ]]; then
+ echo -e "${YELLOW}Keine Alert-ID eingegeben.${NC}"
+ return
+ fi
+
+ if ! [[ "$alert_id" =~ ^[0-9]+$ ]]; then
+ echo -e "${RED}Ungültige Alert-ID (nur Zahlen erlaubt).${NC}"
+ return
+ fi
+
+ echo ""
+ echo -e "${DIM}Inspiziere Alert #$alert_id...${NC}"
+ draw_line "-" 60
+ cscli_exec "alerts inspect '$alert_id' -d" 2>/dev/null || {
+ echo -e "${RED}Fehler beim Inspizieren von Alert #$alert_id.${NC}"
+ }
+ echo ""
+}
+
+# ---[ Alert löschen ]---
+admin_delete_alert() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Alert löschen ===${NC}"
+ echo ""
+ echo -e "${BOLD}Löschen nach:${NC}"
+ echo " 1) Einzelne Alert-ID"
+ echo " 2) Alle Alerts für eine IP"
+ echo " 3) Alle Alerts für einen Zeitraum"
+ echo -n -e "${CYAN}Auswahl [1-3]: ${NC}"
+ read -r delete_choice
+
+ case "$delete_choice" in
+ 1)
+ echo -n -e "${CYAN}Alert-ID: ${NC}"
+ read -r del_id
+ if [[ -z "$del_id" ]] || ! [[ "$del_id" =~ ^[0-9]+$ ]]; then
+ echo -e "${RED}Ungültige Alert-ID.${NC}"
+ return
+ fi
+ echo -n -e "${YELLOW}Alert #$del_id wirklich löschen? [j/N]: ${NC}"
+ read -r confirm
+ if [[ "$confirm" =~ ^[jJyY]$ ]]; then
+ cscli_exec "alerts delete --id '$del_id'" 2>&1
+ else
+ echo -e "${YELLOW}Abgebrochen.${NC}"
+ fi
+ ;;
+ 2)
+ echo -n -e "${CYAN}IP-Adresse: ${NC}"
+ read -r del_ip
+ if [[ -z "$del_ip" ]]; then
+ echo -e "${YELLOW}Keine IP eingegeben.${NC}"
+ return
+ fi
+ echo -n -e "${YELLOW}Alle Alerts für $del_ip löschen? [j/N]: ${NC}"
+ read -r confirm
+ if [[ "$confirm" =~ ^[jJyY]$ ]]; then
+ cscli_exec "alerts delete --ip '$del_ip'" 2>&1
+ else
+ echo -e "${YELLOW}Abgebrochen.${NC}"
+ fi
+ ;;
+ 3)
+ echo -n -e "${CYAN}Zeitraum (z.B. 24h, 7d): ${NC}"
+ read -r del_range
+ if [[ -z "$del_range" ]]; then
+ echo -e "${YELLOW}Kein Zeitraum eingegeben.${NC}"
+ return
+ fi
+ echo -n -e "${YELLOW}Alle Alerts älter als $del_range löschen? [j/N]: ${NC}"
+ read -r confirm
+ if [[ "$confirm" =~ ^[jJyY]$ ]]; then
+ cscli_exec "alerts delete --range '$del_range'" 2>&1
+ else
+ echo -e "${YELLOW}Abgebrochen.${NC}"
+ fi
+ ;;
+ *)
+ echo -e "${YELLOW}Ungültige Auswahl.${NC}"
+ ;;
+ esac
+ echo ""
+}
+
+# ---[ Bouncers verwalten ]---
+admin_list_bouncers() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Registrierte Bouncers ===${NC}"
+ draw_line "=" 60
+ echo ""
+ cscli_exec "bouncers list" 2>/dev/null || {
+ echo -e "${RED}Fehler beim Abrufen der Bouncers.${NC}"
+ }
+ echo ""
+}
+
+# ---[ Machines verwalten ]---
+admin_list_machines() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Registrierte Machines ===${NC}"
+ draw_line "=" 60
+ echo ""
+ cscli_exec "machines list" 2>/dev/null || {
+ echo -e "${RED}Fehler beim Abrufen der Machines.${NC}"
+ }
+ echo ""
+}
+
+# ---[ Parsers anzeigen ]---
+admin_list_parsers() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Installierte Parsers ===${NC}"
+ draw_line "=" 60
+ echo ""
+ cscli_exec "parsers list" 2>/dev/null || {
+ echo -e "${RED}Fehler beim Abrufen der Parsers.${NC}"
+ }
+ echo ""
+}
+
+# ---[ Scenarios anzeigen ]---
+admin_list_scenarios() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Installierte Scenarios ===${NC}"
+ draw_line "=" 60
+ echo ""
+ cscli_exec "scenarios list" 2>/dev/null || {
+ echo -e "${RED}Fehler beim Abrufen der Scenarios.${NC}"
+ }
+ echo ""
+}
+
+# ---[ Collections anzeigen ]---
+admin_list_collections() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Installierte Collections ===${NC}"
+ draw_line "=" 60
+ echo ""
+ cscli_exec "collections list" 2>/dev/null || {
+ echo -e "${RED}Fehler beim Abrufen der Collections.${NC}"
+ }
+ echo ""
+}
+
+# ---[ CrowdSec Version & Hub Status ]---
+admin_show_status() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== CrowdSec Status ===${NC}"
+ draw_line "=" 60
+ echo ""
+
+ echo -e "${BOLD}Version:${NC}"
+ cscli_exec "version" 2>/dev/null || echo -e "${RED}Nicht erreichbar${NC}"
+
+ echo ""
+ echo -e "${BOLD}Hub Status:${NC}"
+ cscli_exec "hub list" 2>/dev/null || echo -e "${RED}Fehler${NC}"
+ echo ""
+}
+
+# ---[ Eigenen cscli-Befehl ausführen ]---
+admin_custom_command() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== Eigenen cscli-Befehl ausführen ===${NC}"
+ echo -e "${DIM}Basis: ${CSCLI_CMD}${NC}"
+ echo -e "${DIM}Gib nur die Argumente nach 'cscli' ein.${NC}"
+ echo -e "${DIM}Beispiel: decisions list -o json${NC}"
+ echo ""
+ echo -n -e "${CYAN}cscli> ${NC}"
+ read -r custom_args
+
+ if [[ -z "$custom_args" ]]; then
+ echo -e "${YELLOW}Kein Befehl eingegeben.${NC}"
+ return
+ fi
+
+ echo ""
+ draw_line "-" 60
+ cscli_exec "$custom_args" 2>&1 || true
+ draw_line "-" 60
+ echo ""
+}
+
+# ---[ IP-Info: Alle Infos zu einer IP sammeln ]---
+admin_ip_info() {
+ echo ""
+ echo -e "${BOLD}${BLUE}=== IP-Informationen ===${NC}"
+ echo -n -e "${CYAN}IP-Adresse eingeben: ${NC}"
+ read -r info_ip
+
+ if [[ -z "$info_ip" ]]; then
+ echo -e "${YELLOW}Keine IP eingegeben.${NC}"
+ return
+ fi
+
+ echo ""
+ draw_line "=" 60
+ echo -e "${BOLD}Informationen für: ${CYAN}$info_ip${NC}"
+ draw_line "=" 60
+
+ # DNS Reverse-Lookup
+ echo ""
+ echo -e "${BOLD}[1/4] Reverse-DNS:${NC}"
+ local rdns
+ rdns=$(dig +short -x "$info_ip" 2>/dev/null || true)
+ if [[ -n "$rdns" ]]; then
+ echo -e " ${GREEN}$rdns${NC}"
+ else
+ echo -e " ${DIM}Kein PTR-Record gefunden${NC}"
+ fi
+
+ # Decisions für diese IP
+ echo ""
+ echo -e "${BOLD}[2/4] Aktive Decisions:${NC}"
+ cscli_exec "decisions list -i '$info_ip'" 2>/dev/null || {
+ echo -e " ${DIM}Keine Decisions gefunden${NC}"
+ }
+
+ # Alerts für diese IP
+ echo ""
+ echo -e "${BOLD}[3/4] Alerts:${NC}"
+ local alerts_output
+ alerts_output=$(cscli_exec "alerts list -i '$info_ip'" 2>/dev/null || true)
+ if [[ -n "$alerts_output" ]]; then
+ echo "$alerts_output"
+
+ # Alert-IDs extrahieren für Detail-Inspektion
+ echo ""
+ echo -e "${BOLD}[4/4] Alert-Details:${NC}"
+ local alert_ids
+ alert_ids=$(echo "$alerts_output" | grep -oP '^\s*\K[0-9]+' | head -5)
+
+ if [[ -n "$alert_ids" ]]; then
+ echo -e "${DIM}Zeige Details für die letzten Alerts (max. 5):${NC}"
+ while IFS= read -r aid; do
+ [[ -z "$aid" ]] && continue
+ echo ""
+ echo -e "${MAGENTA}--- Alert #$aid ---${NC}"
+ cscli_exec "alerts inspect '$aid' -d" 2>/dev/null || true
+ done <<< "$alert_ids"
+ fi
+ else
+ echo -e " ${DIM}Keine Alerts gefunden${NC}"
+ echo ""
+ echo -e "${BOLD}[4/4] Alert-Details:${NC}"
+ echo -e " ${DIM}Übersprungen (keine Alerts)${NC}"
+ fi
+
+ draw_line "=" 60
+ echo ""
+}
+
+# ============================================================================
+# TEIL 8: INTERAKTIVES MENÜ
+# ============================================================================
+
+show_menu_banner() {
+ clear 2>/dev/null || true
+ echo ""
+ echo -e "${BOLD}${BLUE}"
+ echo " ╔═══════════════════════════════════════════════════════╗"
+ echo " ║ ║"
+ echo " ║ CrowdSec Manager v${SCRIPT_VERSION} ║"
+ echo " ║ ║"
+ echo " ║ Patrick Asmus ║"
+ echo " ║ www.patrick-asmus.de ║"
+ echo " ║ ║"
+ echo " ╚═══════════════════════════════════════════════════════╝"
+ echo -e "${NC}"
+ echo -e " ${DIM}Basisbefehl: ${CSCLI_CMD}${NC}"
+ echo ""
+}
+
+show_main_menu() {
+ echo -e "${BOLD}${GREEN} ── ALLOWLIST MANAGEMENT ──────────────────────────────${NC}"
+ echo -e " ${CYAN} 1)${NC} Allowlist synchronisieren (DNS auflösen & updaten)"
+ echo -e " ${CYAN} 2)${NC} Allowlist synchronisieren (Dry-Run)"
+ echo -e " ${CYAN} 3)${NC} Aktuelle Allowlist anzeigen"
+ echo -e " ${CYAN} 4)${NC} Konfiguration testen"
+ echo -e " ${CYAN} 5)${NC} Alle verwalteten Einträge entfernen (Flush)"
+ echo ""
+ echo -e "${BOLD}${GREEN} ── DECISIONS ─────────────────────────────────────────${NC}"
+ echo -e " ${CYAN}10)${NC} Alle Decisions anzeigen"
+ echo -e " ${CYAN}11)${NC} Decision suchen (nach IP)"
+ echo -e " ${CYAN}12)${NC} Decision manuell hinzufügen"
+ echo -e " ${CYAN}13)${NC} Decision entfernen"
+ echo ""
+ echo -e "${BOLD}${GREEN} ── ALERTS ────────────────────────────────────────────${NC}"
+ echo -e " ${CYAN}20)${NC} Alle Alerts anzeigen"
+ echo -e " ${CYAN}21)${NC} Alert inspizieren (Detail-Ansicht)"
+ echo -e " ${CYAN}22)${NC} Alerts löschen"
+ echo ""
+ echo -e "${BOLD}${GREEN} ── INFORMATIONEN ─────────────────────────────────────${NC}"
+ echo -e " ${CYAN}30)${NC} IP-Informationen (Komplett-Check)"
+ echo -e " ${CYAN}31)${NC} CrowdSec Metriken"
+ echo -e " ${CYAN}32)${NC} CrowdSec Status & Version"
+ echo -e " ${CYAN}33)${NC} Bouncers anzeigen"
+ echo -e " ${CYAN}34)${NC} Machines anzeigen"
+ echo -e " ${CYAN}35)${NC} Parsers anzeigen"
+ echo -e " ${CYAN}36)${NC} Scenarios anzeigen"
+ echo -e " ${CYAN}37)${NC} Collections anzeigen"
+ echo ""
+ echo -e "${BOLD}${GREEN} ── ERWEITERT ─────────────────────────────────────────${NC}"
+ echo -e " ${CYAN}40)${NC} Eigenen cscli-Befehl ausführen"
+ echo ""
+ echo -e " ${CYAN} 0)${NC} Beenden"
+ echo ""
+ draw_line "─" 60
+}
+
+interactive_menu() {
+ load_config
+ disable_colors_if_needed
+
+ while true; do
+ show_menu_banner
+ show_main_menu
+
+ echo -n -e " ${BOLD}Auswahl: ${NC}"
+ read -r choice
+
+ case "$choice" in
+ # Allowlist Management
+ 1) run_allowlist_sync false ;;
+ 2) run_allowlist_sync true ;;
+ 3) show_current_list ;;
+ 4) test_config ;;
+ 5)
+ echo -n -e " ${YELLOW}Wirklich ALLE verwalteten Einträge entfernen? [j/N]: ${NC}"
+ read -r confirm
+ if [[ "$confirm" =~ ^[jJyY]$ ]]; then
+ flush_allowlist
+ else
+ echo -e " ${YELLOW}Abgebrochen.${NC}"
+ fi
+ ;;
+
+ # Decisions
+ 10) admin_list_decisions ;;
+ 11) admin_search_decision ;;
+ 12) admin_add_decision ;;
+ 13) admin_remove_decision ;;
+
+ # Alerts
+ 20) admin_list_alerts ;;
+ 21) admin_inspect_alert ;;
+ 22) admin_delete_alert ;;
+
+ # Informationen
+ 30) admin_ip_info ;;
+ 31) admin_show_metrics ;;
+ 32) admin_show_status ;;
+ 33) admin_list_bouncers ;;
+ 34) admin_list_machines ;;
+ 35) admin_list_parsers ;;
+ 36) admin_list_scenarios ;;
+ 37) admin_list_collections ;;
+
+ # Erweitert
+ 40) admin_custom_command ;;
+
+ # Beenden
+ 0|q|Q|exit)
+ echo ""
+ echo -e " ${GREEN}Auf Wiedersehen!${NC}"
+ echo ""
+ exit 0
+ ;;
+
+ *)
+ echo -e " ${RED}Ungültige Auswahl: $choice${NC}"
+ ;;
+ esac
+
+ echo ""
+ echo -n -e " ${DIM}Drücke Enter um fortzufahren...${NC}"
+ read -r
+ done
+}
+
+# Allowlist Sync als Funktion (wird aus Menü und CLI aufgerufen)
+run_allowlist_sync() {
+ local dry_run_override="${1:-false}"
+ local orig_dry_run="${DRY_RUN:-false}"
+
+ if [[ "$dry_run_override" == "true" ]]; then
+ DRY_RUN=true
+ fi
+
+ # Statistiken zurücksetzen
+ STAT_ADDED=0
+ STAT_REMOVED=0
+ STAT_UNCHANGED=0
+ STAT_ERRORS=0
+ STAT_RESOLVED_DOMAINS=0
+ STAT_TOTAL_IPS=0
+ REPORT_MESSAGES=()
+
+ echo ""
+ log "INFO" "============================================"
+ log "INFO" " CrowdSec Manager v${SCRIPT_VERSION}"
+ log "INFO" " Patrick Asmus - www.patrick-asmus.de"
+ log "INFO" "============================================"
+
+ if [[ "${DRY_RUN}" == "true" ]]; then
+ log "WARN" "*** DRY-RUN MODUS AKTIV ***"
+ fi
+
+ rotate_logs
+
+ if ! check_dependencies; then
+ DRY_RUN="$orig_dry_run"
+ return 1
+ fi
+
+ if ! acquire_lock; then
+ DRY_RUN="$orig_dry_run"
+ return 1
+ fi
+
+ trap 'release_lock; cleanup_temp' EXIT INT TERM
+
+ setup_temp
+
+ if ! crowdsec_health_check; then
+ cleanup_temp
+ release_lock
+ DRY_RUN="$orig_dry_run"
+ return 1
+ fi
+
+ get_current_allowlist
+ create_backup
+ resolve_all_entries
+ sync_allowlist
+ print_summary
+ send_notifications
+
+ cleanup_temp
+ release_lock
+
+ trap - EXIT INT TERM
+
+ DRY_RUN="$orig_dry_run"
+
+ if [[ $STAT_ERRORS -gt 0 ]]; then
+ return 2
+ fi
+ return 0
+}
+
+# ============================================================================
+# TEIL 9: Report / Zusammenfassung
+# ============================================================================
+
+print_summary() {
+ echo ""
+ log "INFO" "============================================"
+ log "INFO" " ZUSAMMENFASSUNG"
+ log "INFO" "============================================"
+ log "INFO" "Domains aufgelöst: $STAT_RESOLVED_DOMAINS"
+ log "INFO" "IPs/CIDRs gesamt: $STAT_TOTAL_IPS"
+ log "INFO" "Hinzugefügt: $STAT_ADDED"
+ log "INFO" "Entfernt: $STAT_REMOVED"
+ log "INFO" "Unverändert: $STAT_UNCHANGED"
+
+ if [[ $STAT_ERRORS -gt 0 ]]; then
+ log "WARN" "Fehler: $STAT_ERRORS"
+ else
+ log "INFO" "Fehler: $STAT_ERRORS"
+ fi
+
+ if [[ "${DRY_RUN:-false}" == "true" ]]; then
+ log "WARN" "DRY-RUN MODUS - Keine Änderungen vorgenommen"
+ fi
+
+ log "INFO" "============================================"
+ echo ""
+}
+
+# ============================================================================
+# TEIL 10: CLI-FUNKTIONEN (nicht-interaktiv)
+# ============================================================================
+
+show_help() {
+ cat << EOF
+${BOLD}CrowdSec Manager v${SCRIPT_VERSION}${NC}
+
+Verwaltet automatisch eine CrowdSec-Allowlist und bietet interaktive
+CrowdSec-Administration.
+
+${BOLD}Verwendung:${NC}
+ $SCRIPT_NAME [OPTIONEN]
+
+${BOLD}Modi:${NC}
+ (ohne Optionen) Startet das interaktive Menü
+ --run Allowlist synchronisieren (für Cron)
+
+${BOLD}Optionen:${NC}
+ -c, --config FILE Konfigurationsdatei angeben
+ -d, --dry-run Dry-Run: Zeigt was passieren würde
+ -v, --verbose Ausführliche Ausgabe (DEBUG)
+ -q, --quiet Minimale Ausgabe (ERROR)
+ -l, --list Aktuelle Allowlist anzeigen
+ -f, --flush Alle verwalteten Einträge entfernen
+ -t, --test Konfiguration testen
+ -i, --interactive Interaktives Menü starten
+ --run Allowlist-Sync ausführen (nicht-interaktiv)
+ -h, --help Diese Hilfe anzeigen
+ -V, --version Version anzeigen
+
+${BOLD}Beispiele:${NC}
+ $SCRIPT_NAME # Interaktives Menü
+ $SCRIPT_NAME --run # Allowlist-Sync (Cron-Modus)
+ $SCRIPT_NAME --run --dry-run # Testlauf
+ $SCRIPT_NAME --interactive # Interaktives Menü
+ $SCRIPT_NAME --list # Allowlist anzeigen
+ $SCRIPT_NAME --config /etc/conf.conf # Eigene Config
+
+${BOLD}Cron-Beispiel:${NC}
+ */30 * * * * ${SCRIPT_DIR}/${SCRIPT_NAME} --run 2>&1
+
+${BOLD}Konfiguration:${NC}
+ Standard: config/crowdsec-manager.conf
+
+EOF
+}
+
+show_version() {
+ echo "CrowdSec Manager v${SCRIPT_VERSION}"
+}
+
+show_current_list() {
+ echo ""
+ echo -e "${BOLD}Aktuelle CrowdSec Allowlist (${ALLOWLIST_NAME}):${NC}"
+ draw_line "-" 60
+ cscli_exec "allowlists inspect ${ALLOWLIST_NAME}" 2>/dev/null || echo "Keine Einträge oder Allowlist nicht gefunden."
+ draw_line "-" 60
+}
+
+flush_allowlist() {
+ log "WARN" "Entferne ALLE verwalteten Einträge aus der Allowlist..."
+
+ if [[ "${DRY_RUN:-false}" == "true" ]]; then
+ log "INFO" "[DRY-RUN] Würde Allowlist '${ALLOWLIST_NAME}' löschen und neu erstellen"
+ return 0
+ fi
+
+ # Allowlist komplett löschen und leer neu erstellen
+ if cscli_exec "allowlists delete ${ALLOWLIST_NAME}" 2>/dev/null; then
+ log "INFO" "Allowlist '${ALLOWLIST_NAME}' gelöscht."
+ else
+ log "WARN" "Allowlist '${ALLOWLIST_NAME}' konnte nicht gelöscht werden (existiert möglicherweise nicht)."
+ fi
+
+ # Leer neu erstellen
+ if cscli_exec "allowlists create ${ALLOWLIST_NAME} -d '${ALLOWLIST_DESCRIPTION}'" 2>&1; then
+ log "INFO" "Leere Allowlist '${ALLOWLIST_NAME}' neu erstellt."
+ else
+ log "ERROR" "Fehler beim Neuerstellen der Allowlist."
+ return 1
+ fi
+}
+
+test_config() {
+ echo -e "\n${BOLD}Konfigurationstest:${NC}\n"
+
+ echo -e "${GREEN}[OK]${NC} Konfigurationsdatei geladen: $CONFIG_FILE"
+
+ echo -n "CrowdSec-Erreichbarkeit... "
+ if cscli_exec "version" &>/dev/null; then
+ echo -e "${GREEN}[OK]${NC}"
+ else
+ echo -e "${RED}[FEHLER]${NC} Befehl: '$CSCLI_CMD version'"
+ fi
+
+ echo -n "DNS-Auflösung... "
+ if command -v dig &>/dev/null; then
+ echo -e "${GREEN}[OK]${NC} dig gefunden"
+ else
+ echo -e "${RED}[FEHLER]${NC} dig nicht gefunden (dnsutils installieren)"
+ fi
+
+ echo ""
+ echo -e "${BOLD}Konfigurierte Einträge (${#ALLOWLIST_ENTRIES[@]}):${NC}"
+ for entry in "${ALLOWLIST_ENTRIES[@]}"; do
+ entry=$(echo "$entry" | sed 's/#.*$//' | xargs)
+ [[ -z "$entry" ]] && continue
+
+ if is_ip_or_cidr "$entry"; then
+ echo -e " ${CYAN}[IP/CIDR]${NC} $entry"
+ elif is_domain "$entry"; then
+ echo -n " [Domain] $entry -> "
+ local ips
+ ips=$(resolve_domain "$entry" 2>/dev/null)
+ if [[ -n "$ips" ]]; then
+ echo -e "${GREEN}$(echo "$ips" | tr '\n' ', ' | sed 's/,$//')${NC}"
+ else
+ echo -e "${RED}Nicht auflösbar${NC}"
+ fi
+ else
+ echo -e " ${RED}[UNBEKANNT]${NC} $entry"
+ fi
+ done
+
+ echo ""
+ echo -e "${BOLD}Benachrichtigungen:${NC}"
+ echo " Aktiviert: ${NOTIFY_ENABLED}"
+ echo " Desktop: ${NOTIFY_DESKTOP_ENABLED:-false}"
+ echo " Ntfy: ${NOTIFY_NTFY_ENABLED:-false}"
+ echo " Gotify: ${NOTIFY_GOTIFY_ENABLED:-false}"
+ echo " E-Mail: ${NOTIFY_EMAIL_ENABLED:-false}"
+ echo ""
+}
+
+# ============================================================================
+# TEIL 11: KONFIGURATION & ABHÄNGIGKEITEN
+# ============================================================================
+
+load_config() {
+ if [[ ! -f "$CONFIG_FILE" ]]; then
+ echo "FEHLER: Konfigurationsdatei nicht gefunden: $CONFIG_FILE" >&2
+ echo "Erstelle eine Konfigurationsdatei basierend auf der Vorlage:" >&2
+ echo " cp config/crowdsec-manager.conf.example config/crowdsec-manager.conf" >&2
+ exit 1
+ fi
+
+ # shellcheck source=/dev/null
+ source "$CONFIG_FILE"
+
+ if [[ -n "${LOG_FILE:-}" ]]; then
+ local log_dir
+ log_dir="$(dirname "$LOG_FILE")"
+ if [[ ! -d "$log_dir" ]]; then
+ mkdir -p "$log_dir" 2>/dev/null || true
+ fi
+ fi
+}
+
+check_dependencies() {
+ local missing=()
+
+ if ! command -v dig &>/dev/null; then
+ missing+=("dig (dnsutils/bind-utils)")
+ fi
+
+ if ! command -v curl &>/dev/null; then
+ if [[ "${NOTIFY_NTFY_ENABLED:-false}" == "true" ]] || \
+ [[ "${NOTIFY_GOTIFY_ENABLED:-false}" == "true" ]] || \
+ [[ "${NOTIFY_EMAIL_ENABLED:-false}" == "true" ]]; then
+ missing+=("curl (für Benachrichtigungen)")
+ fi
+ fi
+
+ if [[ ${#missing[@]} -gt 0 ]]; then
+ log "ERROR" "Fehlende Abhängigkeiten:"
+ for dep in "${missing[@]}"; do
+ log "ERROR" " - $dep"
+ done
+ return 1
+ fi
+
+ return 0
+}
+
+# ============================================================================
+# TEIL 12: HAUPTPROGRAMM
+# ============================================================================
+
+main() {
+ local action="interactive"
+
+ # Argumente parsen
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -c|--config)
+ CONFIG_FILE="$2"
+ shift 2
+ ;;
+ -d|--dry-run)
+ DRY_RUN=true
+ shift
+ ;;
+ -v|--verbose)
+ LOG_LEVEL="DEBUG"
+ shift
+ ;;
+ -q|--quiet)
+ LOG_LEVEL="ERROR"
+ shift
+ ;;
+ -l|--list)
+ action="list"
+ shift
+ ;;
+ -f|--flush)
+ action="flush"
+ shift
+ ;;
+ -t|--test)
+ action="test"
+ shift
+ ;;
+ -i|--interactive)
+ action="interactive"
+ shift
+ ;;
+ --run)
+ action="run"
+ shift
+ ;;
+ -h|--help)
+ disable_colors_if_needed
+ show_help
+ exit 0
+ ;;
+ -V|--version)
+ show_version
+ exit 0
+ ;;
+ *)
+ echo "Unbekannte Option: $1" >&2
+ echo "Verwende '$SCRIPT_NAME --help' für Hilfe" >&2
+ exit 1
+ ;;
+ esac
+ done
+
+ disable_colors_if_needed
+
+ # Spezielle Aktionen
+ case "$action" in
+ list)
+ load_config
+ show_current_list
+ exit 0
+ ;;
+ test)
+ load_config
+ test_config
+ exit 0
+ ;;
+ flush)
+ load_config
+ flush_allowlist
+ exit 0
+ ;;
+ interactive)
+ interactive_menu
+ exit 0
+ ;;
+ esac
+
+ # === Automatischer Sync (--run, für Cron) ===
+ load_config
+ [[ -n "${DRY_RUN:-}" ]] || DRY_RUN=false
+
+ run_allowlist_sync false
+ local rc=$?
+
+ exit $rc
+}
+
+# Script ausführen
+main "$@"
diff --git a/docs/ADMIN.md b/docs/ADMIN.md
new file mode 100644
index 0000000..436298b
--- /dev/null
+++ b/docs/ADMIN.md
@@ -0,0 +1,198 @@
+# Interaktive Administration
+
+Dokumentation der interaktiven CrowdSec-Administrationsfunktionen im CrowdSec Manager v0.1.0.
+
+---
+
+## Übersicht
+
+Ab Version 0.1.0 startet das Script standardmäßig ein **interaktives Menü**, über das CrowdSec-Funktionen direkt verwaltet werden können – ohne manuell `cscli`-Befehle absetzen zu müssen.
+
+```bash
+# Interaktives Menü starten (Standard)
+./crowdsec-manager.sh
+
+# Oder explizit:
+./crowdsec-manager.sh --interactive
+```
+
+---
+
+## Menü-Struktur
+
+```
+╔═══════════════════════════════════════════════════════╗
+║ CrowdSec Manager v0.1.0 ║
+╚═══════════════════════════════════════════════════════╝
+
+── ALLOWLIST MANAGEMENT ──────────────────────────────
+ 1) Allowlist synchronisieren (DNS auflösen & updaten)
+ 2) Allowlist synchronisieren (Dry-Run)
+ 3) Aktuelle Allowlist anzeigen
+ 4) Konfiguration testen
+ 5) Alle verwalteten Einträge entfernen (Flush)
+
+── DECISIONS ─────────────────────────────────────────
+ 10) Alle Decisions anzeigen
+ 11) Decision suchen (nach IP)
+ 12) Decision manuell hinzufügen
+ 13) Decision entfernen
+
+── ALERTS ────────────────────────────────────────────
+ 20) Alle Alerts anzeigen
+ 21) Alert inspizieren (Detail-Ansicht)
+ 22) Alerts löschen
+
+── INFORMATIONEN ─────────────────────────────────────
+ 30) IP-Informationen (Komplett-Check)
+ 31) CrowdSec Metriken
+ 32) CrowdSec Status & Version
+ 33) Bouncers anzeigen
+ 34) Machines anzeigen
+ 35) Parsers anzeigen
+ 36) Scenarios anzeigen
+ 37) Collections anzeigen
+
+── ERWEITERT ─────────────────────────────────────────
+ 40) Eigenen cscli-Befehl ausführen
+
+ 0) Beenden
+```
+
+---
+
+## Funktionen im Detail
+
+### Allowlist Management (1-5)
+
+| Nr. | Funktion | Beschreibung |
+|-----|----------|-------------|
+| 1 | Sync | Führt die komplette Allowlist-Synchronisation aus (DNS auflösen, Vergleich, Update) |
+| 2 | Dry-Run | Wie 1, aber ohne tatsächliche Änderungen an CrowdSec |
+| 3 | Anzeigen | Zeigt alle aktuellen Allowlist-Einträge |
+| 4 | Test | Prüft Config, DNS-Auflösung und CrowdSec-Verbindung |
+| 5 | Flush | Entfernt alle vom Script verwalteten Einträge (mit Bestätigung) |
+
+### Decisions (10-13)
+
+#### 10) Alle Decisions anzeigen
+
+Zeigt alle aktiven CrowdSec Decisions an (Ban, Allow, Captcha, Throttle).
+
+Entspricht: `cscli decisions list`
+
+#### 11) Decision suchen
+
+Sucht alle Decisions für eine bestimmte IP-Adresse.
+
+Entspricht: `cscli decisions list -i `
+
+#### 12) Decision manuell hinzufügen
+
+Interaktiver Assistent zum Hinzufügen einer Decision:
+
+1. **IP/CIDR eingeben** – Die zu behandelnde IP-Adresse oder CIDR-Range
+2. **Typ wählen:**
+ - `ban` – IP sperren
+ - `allow` – IP erlauben (Allowlist)
+ - `captcha` – Captcha anzeigen
+ - `throttle` – Traffic drosseln
+3. **Dauer** – z.B. `10m`, `1h`, `24h`, `7d` (Standard: 4h)
+4. **Grund** – Optionaler Kommentar
+5. **Bestätigung** – Zusammenfassung mit Ja/Nein-Abfrage
+
+#### 13) Decision entfernen
+
+Entfernt eine Decision entweder nach:
+- **IP-Adresse** – Alle Decisions für diese IP
+- **Decision-ID** – Spezifische Decision
+
+---
+
+### Alerts (20-22)
+
+#### 20) Alle Alerts anzeigen
+
+Zeigt alle aktuellen Alerts an.
+
+Entspricht: `cscli alerts list`
+
+#### 21) Alert inspizieren
+
+Zeigt Detail-Informationen zu einem spezifischen Alert (nach Alert-ID).
+
+Entspricht: `cscli alerts inspect -d`
+
+#### 22) Alerts löschen
+
+Löscht Alerts nach:
+- **Einzelne Alert-ID** – Spezifischen Alert löschen
+- **IP-Adresse** – Alle Alerts für eine IP
+- **Zeitraum** – Alle Alerts älter als z.B. `24h`, `7d`
+
+Jede Löschung erfordert eine Bestätigung.
+
+---
+
+### Informationen (30-37)
+
+#### 30) IP-Informationen (Komplett-Check)
+
+Sammelt umfassende Informationen zu einer IP-Adresse in 4 Schritten:
+
+```
+[1/4] Reverse-DNS: PTR-Record Lookup
+[2/4] Aktive Decisions: Alle Decisions für diese IP
+[3/4] Alerts: Alle Alerts für diese IP
+[4/4] Alert-Details: Detail-Inspektion der letzten 5 Alerts
+```
+
+Nützlich für schnelle Untersuchung verdächtiger IPs.
+
+#### 31) CrowdSec Metriken
+
+Zeigt aktuelle CrowdSec-Metriken (Acquisition, Parser, Scenarios, etc.).
+
+Entspricht: `cscli metrics`
+
+#### 32) CrowdSec Status & Version
+
+Zeigt die CrowdSec-Version und den Hub-Status.
+
+#### 33-37) Hub-Komponenten
+
+| Nr. | Komponente | cscli-Befehl |
+|-----|-----------|-------------|
+| 33 | Bouncers | `cscli bouncers list` |
+| 34 | Machines | `cscli machines list` |
+| 35 | Parsers | `cscli parsers list` |
+| 36 | Scenarios | `cscli scenarios list` |
+| 37 | Collections | `cscli collections list` |
+
+---
+
+### Erweitert (40)
+
+#### 40) Eigener cscli-Befehl
+
+Ermöglicht die Ausführung beliebiger `cscli`-Befehle über den konfigurierten `CSCLI_CMD`.
+
+```
+Basis: docker exec crowdsec cscli
+Gib nur die Argumente nach 'cscli' ein.
+Beispiel: decisions list -o json
+
+cscli> decisions list -o json
+```
+
+Der eingegebene Befehl wird an den konfigurierten Basisbefehl angehängt und ausgeführt.
+
+---
+
+## Tipps
+
+- Das Menü merkt sich den Kontext nicht zwischen Aufrufen. Jede Funktion ist eigenständig.
+- Bei allen destruktiven Aktionen (Flush, Decisions/Alerts löschen) wird eine Bestätigung abgefragt.
+- Die Navigation erfolgt rein über Nummern-Eingabe.
+- Nach jeder Funktion wird auf Enter gewartet, bevor das Menü neu angezeigt wird.
+- Mit `0`, `q` oder `exit` wird das Programm beendet.
diff --git a/docs/ALLOWLIST.md b/docs/ALLOWLIST.md
new file mode 100644
index 0000000..33f8c10
--- /dev/null
+++ b/docs/ALLOWLIST.md
@@ -0,0 +1,192 @@
+# Allowlist Management
+
+Dokumentation der automatischen Allowlist-Verwaltung im CrowdSec Manager v0.1.0.
+
+---
+
+## Funktionsweise
+
+Das Script arbeitet nach dem **Desired-State-Prinzip**: Du definierst WAS allowgelistet sein soll (Domains/IPs), und das Script sorgt dafür, dass der IST-Zustand in CrowdSec dem SOLL-Zustand entspricht.
+
+### Ablauf einer Synchronisation
+
+```
+1. Vorbereitung
+ ├── Abhängigkeiten prüfen (dig, curl)
+ ├── Lock-File erwerben
+ ├── Temporäre Dateien erstellen
+ └── CrowdSec Health-Check
+
+2. Datenerfassung
+ ├── Aktuelle CrowdSec-Allowlist abrufen → current_ips.txt
+ ├── Backup der aktuellen Liste erstellen
+ └── DNS-Auflösung aller Einträge → new_ips.txt
+
+3. Synchronisation
+ ├── Vergleich: current_ips.txt vs. new_ips.txt
+ ├── Neue IPs hinzufügen (in new, nicht in current)
+ ├── Veraltete IPs entfernen (in current, nicht in new)
+ └── Statistiken aktualisieren
+
+4. Abschluss
+ ├── Zusammenfassung ausgeben
+ ├── Benachrichtigungen senden
+ ├── Lock-File freigeben
+ └── Temporäre Dateien aufräumen
+```
+
+---
+
+## Kommandozeilen-Optionen
+
+### Startmodi (v0.1.0)
+
+| Aufruf | Beschreibung |
+|--------|-------------|
+| `./script.sh` | Startet das **interaktive Menü** (Standard) |
+| `./script.sh --run` | Führt Allowlist-Sync aus (für **Cron**) |
+| `./script.sh --interactive` | Startet das interaktive Menü (explizit) |
+
+> **Wichtig für Cron:** Ab v0.1.0 muss `--run` angegeben werden, da ohne Parameter das interaktive Menü startet.
+
+### Alle Optionen
+
+| Option | Kurz | Beschreibung |
+|--------|------|-------------|
+| `--run` | | Allowlist-Sync ausführen (nicht-interaktiv, für Cron) |
+| `--interactive` | `-i` | Interaktives Menü starten |
+| `--config FILE` | `-c` | Eigene Konfigurationsdatei laden |
+| `--dry-run` | `-d` | Simuliert alle Aktionen ohne CrowdSec zu ändern |
+| `--verbose` | `-v` | Setzt Log-Level auf DEBUG |
+| `--quiet` | `-q` | Setzt Log-Level auf ERROR |
+| `--list` | `-l` | Zeigt aktuelle CrowdSec-Allowlist an |
+| `--flush` | `-f` | Entfernt ALLE vom Script verwalteten Einträge |
+| `--test` | `-t` | Testet Config, DNS und CrowdSec-Verbindung |
+| `--help` | `-h` | Zeigt Hilfe an |
+| `--version` | `-V` | Zeigt Version an |
+
+---
+
+## Dry-Run Modus
+
+Im Dry-Run Modus werden alle Schritte normal durchgeführt (DNS-Auflösung, Vergleich), aber die CrowdSec-Befehle werden **nicht** ausgeführt:
+
+```bash
+./crowdsec-manager.sh --run --dry-run
+```
+
+Ausgabe:
+```
+[INFO] [DRY-RUN] Würde hinzufügen: 203.0.113.50
+[INFO] [DRY-RUN] Würde entfernen: 198.51.100.20
+```
+
+Auch im interaktiven Menü (Punkt 2) kann ein Dry-Run gestartet werden.
+
+---
+
+## Test-Modus
+
+Prüft die gesamte Konfiguration ohne etwas zu ändern:
+
+```bash
+./crowdsec-manager.sh --test
+```
+
+Ausgabe:
+```
+Konfigurationstest:
+
+[OK] Konfigurationsdatei geladen
+CrowdSec-Erreichbarkeit... [OK]
+DNS-Auflösung... [OK] dig gefunden
+
+Konfigurierte Einträge (3):
+ [Domain] example.com -> 93.184.216.34, 2606:2800:220:1:...
+ [Domain] cdn.example.com -> 104.16.132.229
+ [IP/CIDR] 192.168.1.0/24
+
+Benachrichtigungen:
+ Aktiviert: true
+ Desktop: false
+ Ntfy: true
+ Gotify: false
+ E-Mail: false
+```
+
+---
+
+## Flush-Modus
+
+Entfernt **alle** vom Script verwalteten Einträge aus CrowdSec (identifiziert über `ALLOWLIST_REASON`):
+
+```bash
+./crowdsec-manager.sh --flush
+```
+
+> **Achtung:** Manuell mit `cscli decisions add` erstellte Einträge werden NICHT entfernt.
+
+---
+
+## Cron-Integration
+
+### Empfohlene Intervalle
+
+| Anwendungsfall | Intervall | Cron-Ausdruck |
+|:---:|:---:|:---:|
+| Dynamische DNS (DynDNS) | 15 Minuten | `*/15 * * * *` |
+| Cloud-Dienste / CDN | 30 Minuten | `*/30 * * * *` |
+| Statische Infrastruktur | Stündlich | `0 * * * *` |
+| Feste IPs | Täglich | `0 3 * * *` |
+
+### Einrichtung
+
+```bash
+crontab -e
+```
+
+Empfohlener Eintrag:
+```cron
+# CrowdSec Manager - alle 30 Minuten
+*/30 * * * * /opt/crowdsec-manager/crowdsec-manager.sh --run >> /var/log/crowdsec-manager-cron.log 2>&1
+```
+
+### Cron-spezifisches Verhalten
+
+Das Script erkennt automatisch ob es im Terminal oder per Cron läuft:
+- **Terminal:** Farbige Ausgabe
+- **Cron:** Plain-Text Ausgabe (keine Escape-Codes)
+
+---
+
+## Exit-Codes
+
+| Exit-Code | Bedeutung |
+|:---:|---------|
+| `0` | Erfolgreich |
+| `1` | Kritischer Fehler (Config fehlt, CrowdSec nicht erreichbar) |
+| `2` | Teilweise erfolgreich (mit nicht-kritischen Fehlern) |
+
+---
+
+## Mehrere Konfigurationen
+
+Du kannst mehrere Konfigurationsdateien für verschiedene Allowlists verwenden:
+
+```bash
+# Webserver-Allowlist
+./crowdsec-manager.sh --run --config config/webserver.conf
+
+# Monitoring-Allowlist
+./crowdsec-manager.sh --run --config config/monitoring.conf
+```
+
+Jede Konfiguration sollte einen eigenen `ALLOWLIST_REASON` verwenden:
+
+```bash
+# webserver.conf
+ALLOWLIST_REASON="allowlist-webserver"
+
+# monitoring.conf
+ALLOWLIST_REASON="allowlist-monitoring"
+```
diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md
new file mode 100644
index 0000000..e4032b9
--- /dev/null
+++ b/docs/CONFIGURATION.md
@@ -0,0 +1,260 @@
+# Konfigurationsreferenz
+
+Dokumentation aller Konfigurationsoptionen für den CrowdSec Manager v0.1.0.
+
+> **Datei:** `config/crowdsec-manager.conf`
+> Die Konfigurationsdatei ist ein Bash-Script und wird per `source` eingebunden.
+
+---
+
+## Inhaltsverzeichnis
+
+1. [CrowdSec Basis-Konfiguration](#crowdsec-basis-konfiguration)
+2. [Allowlist-Einträge](#allowlist-einträge)
+3. [DNS-Konfiguration](#dns-konfiguration)
+4. [Logging](#logging)
+5. [Backup](#backup)
+6. [Lock-File](#lock-file)
+7. [Auto-Cleanup](#auto-cleanup)
+
+---
+
+## CrowdSec Basis-Konfiguration
+
+### `CSCLI_CMD`
+
+Der Befehl zum Aufrufen von CrowdSec CLI. Ermöglicht die Unterstützung verschiedener Installationsarten:
+
+```bash
+# Native Installation
+CSCLI_CMD="cscli"
+
+# Docker (Container-Name: crowdsec)
+CSCLI_CMD="docker exec crowdsec cscli"
+
+# Docker Compose (Service-Name: crowdsec)
+CSCLI_CMD="docker compose exec crowdsec cscli"
+
+# Docker Compose mit eigenem Compose-File
+CSCLI_CMD="docker compose -f /opt/crowdsec/docker-compose.yml exec crowdsec cscli"
+
+# Podman
+CSCLI_CMD="podman exec crowdsec cscli"
+
+# SSH auf Remote-Server
+CSCLI_CMD="ssh user@server cscli"
+```
+
+**Tipp:** Teste den Befehl manuell, bevor du ihn in die Config einträgst:
+```bash
+docker exec crowdsec cscli version
+```
+
+### `ALLOWLIST_NAME`
+
+Name der verwalteten Allowlist. Wird via `cscli allowlists` in CrowdSec verwaltet.
+
+```bash
+ALLOWLIST_NAME="crowdsec-manager"
+```
+
+### `ALLOWLIST_REASON`
+
+Beschreibung für die einzelnen Einträge in der Allowlist. Dient zur Nachverfolgbarkeit.
+
+```bash
+ALLOWLIST_REASON="allowlist-manager-auto"
+```
+
+### `HEALTH_CHECK`
+
+Ob vor dem Sync ein CrowdSec-Health-Check durchgeführt werden soll.
+
+```bash
+HEALTH_CHECK=true
+```
+
+### `DRY_RUN`
+
+Global aktivierbarer Dry-Run Modus. Kann auch per CLI mit `--dry-run` aktiviert werden.
+
+```bash
+DRY_RUN=false
+```
+
+---
+
+## Allowlist-Einträge
+
+### `ALLOWLIST_ENTRIES`
+
+Bash-Array mit allen zu verwaltenden Einträgen:
+
+```bash
+ALLOWLIST_ENTRIES=(
+ # === Webserver & CDN ===
+ "meinedomain.de"
+ "www.meinedomain.de"
+ "cdn.meinedomain.de"
+
+ # === Mail-Server ===
+ "mail.meinedomain.de"
+
+ # === Monitoring ===
+ "monitoring.example.com"
+
+ # === Feste IPs ===
+ "203.0.113.10"
+ "203.0.113.20"
+
+ # === Büro-Netzwerk (CIDR) ===
+ "192.168.1.0/24"
+
+ # === IPv6 ===
+ "2001:db8::1"
+ "2001:db8:1::/48"
+)
+```
+
+**Regeln für Einträge:**
+- Ein Eintrag pro Zeile (in Anführungszeichen)
+- Kommentare mit `#` sind direkt in der Zeile möglich
+- Leere Zeilen werden ignoriert
+- Domains werden automatisch per DNS aufgelöst
+- IPs und CIDRs werden direkt eingetragen
+- IPv4 und IPv6 werden unterstützt
+
+**Erkennung von Eintragstypen:**
+
+| Eintragstyp | Beispiel | Aktion |
+|-------------|----------|--------|
+| IPv4 | `192.168.1.1` | Direkt eintragen |
+| IPv6 | `2001:db8::1` | Direkt eintragen |
+| IPv4-CIDR | `10.0.0.0/8` | Als `range` eintragen |
+| IPv6-CIDR | `2001:db8::/32` | Als `range` eintragen |
+| Domain | `example.com` | DNS auflösen → IPs eintragen |
+
+---
+
+## DNS-Konfiguration
+
+### `DNS_SERVER`
+
+Eigenen DNS-Server für die Auflösung verwenden. Leer lassen für den System-DNS (aus `/etc/resolv.conf`).
+
+```bash
+# System-DNS (Standard)
+DNS_SERVER=""
+
+# Cloudflare
+DNS_SERVER="1.1.1.1"
+
+# Google
+DNS_SERVER="8.8.8.8"
+
+# Quad9
+DNS_SERVER="9.9.9.9"
+
+# Eigener DNS
+DNS_SERVER="10.0.0.53"
+```
+
+### `RESOLVE_IPV6`
+
+Ob auch AAAA-Records (IPv6) aufgelöst werden sollen:
+
+```bash
+RESOLVE_IPV6=true # IPv4 + IPv6
+RESOLVE_IPV6=false # Nur IPv4
+```
+
+### `DNS_TIMEOUT` und `DNS_RETRIES`
+
+```bash
+DNS_TIMEOUT=5 # Timeout in Sekunden pro DNS-Anfrage
+DNS_RETRIES=3 # Wiederholungsversuche bei Fehler
+```
+
+---
+
+## Logging
+
+### `LOG_LEVEL`
+
+Steuert die Ausführlichkeit der Ausgabe:
+
+| Level | Beschreibung |
+|-------|-------------|
+| `DEBUG` | Alles loggen (jede DNS-Anfrage, jeder Vergleich) |
+| `INFO` | Standard: Wichtige Schritte und Ergebnisse |
+| `WARN` | Nur Warnungen und Fehler |
+| `ERROR` | Nur Fehler |
+
+```bash
+LOG_LEVEL="INFO"
+```
+
+**Tipp:** Verwende `--verbose` beim manuellen Aufruf und `--quiet` für Cron.
+
+### `LOG_FILE`
+
+Pfad zur Log-Datei. Leer lassen wenn nur auf stdout ausgegeben werden soll.
+
+```bash
+LOG_FILE="/var/log/crowdsec-manager.log"
+```
+
+### Log-Rotation
+
+```bash
+LOG_MAX_SIZE_KB=5120 # Rotation bei 5 MB
+LOG_ROTATE_COUNT=3 # 3 alte Dateien aufbewahren
+```
+
+Rotierte Dateien:
+```
+crowdsec-manager.log ← Aktuell
+crowdsec-manager.log.0 ← Vorherige
+crowdsec-manager.log.1
+crowdsec-manager.log.2 ← Älteste
+```
+
+---
+
+## Backup
+
+```bash
+BACKUP_ENABLED=true
+BACKUP_DIR="/var/backup/crowdsec-manager"
+BACKUP_RETAIN_COUNT=7 # 7 Backups aufbewahren
+```
+
+Backup-Dateien werden im Format `allowlist_YYYYMMDD_HHMMSS.bak` gespeichert. Ältere Backups werden automatisch gelöscht.
+
+---
+
+## Lock-File
+
+Das Lock-File verhindert parallele Ausführung (z.B. wenn ein Cron-Lauf länger dauert als das Intervall).
+
+```bash
+LOCK_ENABLED=true
+LOCK_FILE="/tmp/crowdsec-manager.lock"
+LOCK_TIMEOUT=300 # 5 Minuten warten, dann abbrechen
+```
+
+**Verwaiste Lock-Files:** Das Script erkennt automatisch, ob der im Lock-File referenzierte Prozess noch läuft. Wenn nicht, wird das Lock-File entfernt.
+
+---
+
+## Auto-Cleanup
+
+```bash
+AUTO_CLEANUP=true
+```
+
+Wenn aktiviert, entfernt das Script IPs aus der CrowdSec-Allowlist, die:
+- In der aktuellen Allowlist vorhanden sind (mit dem Origin `ALLOWLIST_REASON`)
+- NICHT mehr in der aufgelösten IP-Liste enthalten sind
+
+Wenn also eine Domain ihre IP ändert, wird die alte IP automatisch aus der Allowlist entfernt.
diff --git a/docs/DOCUMENTATION.md b/docs/DOCUMENTATION.md
new file mode 100644
index 0000000..21e5ce0
--- /dev/null
+++ b/docs/DOCUMENTATION.md
@@ -0,0 +1,182 @@
+# CrowdSec Manager – Dokumentation
+
+## Version 0.1.0
+
+Umfassende Dokumentation für den CrowdSec Manager.
+
+---
+
+## Dokumentationsübersicht
+
+| Dokument | Beschreibung |
+|----------|-------------|
+| [CONFIGURATION.md](CONFIGURATION.md) | Alle Konfigurationsoptionen im Detail |
+| [ALLOWLIST.md](ALLOWLIST.md) | Allowlist-Verwaltung, CLI-Optionen, Cron-Integration |
+| [ADMIN.md](ADMIN.md) | Interaktives Menü & CrowdSec-Administration |
+| [NOTIFICATIONS.md](NOTIFICATIONS.md) | Benachrichtigungen (Ntfy, Gotify, E-Mail, Desktop) |
+| [TROUBLESHOOTING.md](TROUBLESHOOTING.md) | Fehlerbehebung & FAQ |
+
+---
+
+## Schnellstart
+
+### 1. Installation
+
+```bash
+cd /opt
+git clone https://github.com/dein-user/crowdsec-manager.git
+cd crowdsec-manager
+chmod +x crowdsec-manager.sh
+```
+
+### 2. Abhängigkeiten
+
+```bash
+# Debian / Ubuntu
+sudo apt update && sudo apt install -y dnsutils curl
+
+# RHEL / CentOS / Fedora
+sudo dnf install -y bind-utils curl
+
+# Alpine
+apk add --no-cache bind-tools curl
+```
+
+### 3. Konfiguration
+
+```bash
+nano config/crowdsec-manager.conf
+```
+
+Mindestens setzen:
+- `CSCLI_CMD` – CrowdSec CLI Befehl
+- `ALLOWLIST_ENTRIES` – Domains/IPs für die Allowlist
+
+### 4. Test
+
+```bash
+./crowdsec-manager.sh --test
+```
+
+### 5. Verwendung
+
+```bash
+# Interaktives Menü (Standard)
+./crowdsec-manager.sh
+
+# Allowlist-Sync (für Cron)
+./crowdsec-manager.sh --run
+```
+
+### 6. Cron einrichten
+
+```bash
+crontab -e
+```
+
+```cron
+*/30 * * * * /opt/crowdsec-manager/crowdsec-manager.sh --run >> /var/log/crowdsec-manager-cron.log 2>&1
+```
+
+---
+
+## Architektur
+
+### Dateien
+
+| Datei | Aufgabe |
+|-------|---------|
+| `crowdsec-manager.sh` | Hauptscript (Allowlist-Sync + Interaktives Admin-Menü) |
+| `config/crowdsec-manager.conf` | Zentrale Konfigurationsdatei |
+| `examples/crontab.example` | Vorlagen für Cron-Einträge |
+| `docs/` | Dokumentation (aufgeteilt nach Themen) |
+
+### Voraussetzungen
+
+- **Bash 4.0+** (für assoziative Arrays)
+- **dig** (DNS-Auflösung) – Teil von `dnsutils` / `bind-utils`
+- **CrowdSec** mit CLI-Zugriff
+- Optional: **curl** (für Ntfy/Gotify/E-Mail-Benachrichtigungen)
+- Optional: **python3** (für JSON-Escaping bei Gotify)
+
+### Sicherheitsaspekte
+
+- **Lock-File** verhindert Race Conditions bei paralleler Ausführung
+- **Temporäre Dateien** werden nach Ausführung automatisch gelöscht (auch bei Abbruch)
+- **Keine sensiblen Daten** in der Log-Ausgabe
+- Config-Datei sollte nur vom Script-Benutzer lesbar sein: `chmod 600 config/crowdsec-manager.conf`
+- Für DNS-Spoofing Schutz: DNSSEC-validierende DNS-Server verwenden
+
+---
+
+## Logging & Monitoring
+
+### Log-Datei analysieren
+
+```bash
+# Letzte Ausführung anzeigen
+grep "$(date '+%Y-%m-%d')" /var/log/crowdsec-manager.log
+
+# Nur Fehler
+grep "\[ERROR\]" /var/log/crowdsec-manager.log
+
+# Nur Änderungen
+grep -E "Hinzugefügt|Entfernt" /var/log/crowdsec-manager.log
+```
+
+### Exit-Codes für Monitoring
+
+| Exit-Code | Bedeutung |
+|:---:|---------|
+| `0` | Erfolgreich |
+| `1` | Kritischer Fehler |
+| `2` | Teilweise erfolgreich (mit Fehlern) |
+
+---
+
+## Integration
+
+### Ansible
+
+```yaml
+- name: Deploy CrowdSec Manager
+ hosts: crowdsec_servers
+ tasks:
+ - name: Clone repository
+ git:
+ repo: https://github.com/user/crowdsec-manager.git
+ dest: /opt/crowdsec-manager
+
+ - name: Deploy config
+ template:
+ src: crowdsec-manager.conf.j2
+ dest: /opt/crowdsec-manager/config/crowdsec-manager.conf
+ mode: '0600'
+
+ - name: Setup cron
+ cron:
+ name: "CrowdSec Manager"
+ minute: "*/30"
+ job: "/opt/crowdsec-manager/crowdsec-manager.sh --run >> /var/log/crowdsec-manager-cron.log 2>&1"
+```
+
+### Docker Compose Sidecar
+
+```yaml
+services:
+ crowdsec:
+ image: crowdsecurity/crowdsec
+ container_name: crowdsec
+
+ allowlist-manager:
+ image: alpine
+ volumes:
+ - ./crowdsec-manager:/app
+ - /var/run/docker.sock:/var/run/docker.sock
+ command: >
+ sh -c "apk add --no-cache bash bind-tools curl docker-cli &&
+ while true; do
+ /app/crowdsec-manager.sh --run;
+ sleep 1800;
+ done"
+```
diff --git a/docs/NOTIFICATIONS.md b/docs/NOTIFICATIONS.md
new file mode 100644
index 0000000..452609b
--- /dev/null
+++ b/docs/NOTIFICATIONS.md
@@ -0,0 +1,205 @@
+# Benachrichtigungen
+
+Dokumentation des Benachrichtigungssystems im CrowdSec Manager v0.1.0.
+
+---
+
+## Übersicht
+
+Das Benachrichtigungssystem ist modular aufgebaut. Vier Kanäle können gleichzeitig aktiv sein:
+
+```
+ ┌── Desktop (notify-send)
+ ├── Ntfy (HTTP POST)
+should_notify? ─────┤
+ ├── Gotify (HTTP POST / JSON)
+ └── E-Mail (SMTP via curl / mail)
+```
+
+---
+
+## Allgemeine Konfiguration
+
+### Benachrichtigungen aktivieren
+
+```bash
+NOTIFY_ENABLED=true
+```
+
+### Benachrichtigungs-Bedingungen
+
+```bash
+NOTIFY_ON="changes"
+```
+
+| Wert | Bedingung |
+|------|-----------|
+| `always` | Nach jeder Ausführung |
+| `changes` | Wenn IPs hinzugefügt, entfernt oder Fehler aufgetreten sind |
+| `errors` | Nur bei Fehlern |
+
+### Benachrichtigungs-Inhalt
+
+Jede Benachrichtigung enthält:
+
+```
+=== CrowdSec Manager Report ===
+Status: OK
+Zeitpunkt: 2026-03-11 14:30:00
+---
+Domains aufgelöst: 5
+IPs gesamt: 12
+Hinzugefügt: 3
+Entfernt: 1
+Unverändert: 8
+Fehler: 0
+```
+
+Bei Fehlern werden zusätzliche Details angehängt:
+```
+Details:
+ WARN: Keine Auflösung für broken-domain.com
+ ERROR: Konnte 203.0.113.50 nicht hinzufügen: ...
+```
+
+---
+
+## Desktop-Benachrichtigung (notify-send)
+
+Verwendet `notify-send` für Desktop-Notifications auf Linux.
+
+```bash
+NOTIFY_DESKTOP_ENABLED=true
+```
+
+**Voraussetzungen:**
+- `notify-send` muss installiert sein (Teil von `libnotify-bin` auf Debian/Ubuntu)
+- Desktop-Session muss aktiv sein
+
+**Verhalten:**
+- Bei Fehlern: Urgency wird auf `critical` gesetzt
+- Ansonsten: `normal`
+
+---
+
+## Ntfy
+
+[Ntfy](https://ntfy.sh) ist ein Open-Source Benachrichtigungsdienst. Du kannst die öffentliche Instanz oder eine eigene nutzen.
+
+### Konfiguration
+
+```bash
+NOTIFY_NTFY_ENABLED=true
+NOTIFY_NTFY_URL="https://ntfy.sh" # oder eigene Instanz
+NOTIFY_NTFY_TOPIC="mein-crowdsec" # Dein Topic
+NOTIFY_NTFY_PRIORITY="default" # low, default, high, urgent
+NOTIFY_NTFY_TOKEN="tk_xxxxxxxx" # Access Token (optional)
+NOTIFY_NTFY_TAGS="shield,white_check_mark" # Emoji-Tags
+```
+
+### Prioritäten
+
+- Bei Fehlern wird automatisch `high` verwendet
+- Ansonsten der konfigurierte Wert
+
+### Tags
+
+- Bei Fehlern werden automatisch `warning,shield` verwendet
+- Ansonsten der konfigurierte Wert
+
+### Voraussetzungen
+
+- `curl` muss installiert sein
+- Ntfy-Server muss erreichbar sein
+
+### Ntfy-App
+
+Du kannst die [Ntfy-App](https://ntfy.sh) auf Android/iOS installieren und das Topic abonnieren, um Push-Benachrichtigungen zu erhalten.
+
+---
+
+## Gotify
+
+[Gotify](https://gotify.net) ist ein Self-Hosted Push-Benachrichtigungsdienst.
+
+### Konfiguration
+
+```bash
+NOTIFY_GOTIFY_ENABLED=true
+NOTIFY_GOTIFY_URL="https://gotify.meinedomain.de" # Gotify-Server URL
+NOTIFY_GOTIFY_TOKEN="AbCdEf12345" # App-Token
+NOTIFY_GOTIFY_PRIORITY=5 # Priorität (0-10)
+```
+
+### Einrichtung in Gotify
+
+1. Melde dich in deinem Gotify-Webinterface an
+2. Erstelle eine neue **Application** (z.B. "CrowdSec Manager")
+3. Kopiere den generierten **Token**
+4. Trage URL und Token in die Config ein
+
+### Prioritäten
+
+| Bereich | Bedeutung |
+|---------|-----------|
+| 0 | Minimale Priorität |
+| 1-3 | Niedrig |
+| 4-7 | Normal |
+| 8-10 | Hoch |
+
+- Konfigurierte Priorität wird als Standard verwendet
+- Bei Fehlern wird automatisch Priorität `8` (hoch) gesetzt
+
+### Voraussetzungen
+
+- `curl` muss installiert sein
+- Gotify-Server muss erreichbar sein
+- Für JSON-Escaping wird `python3` bevorzugt (Fallback: `sed`)
+
+---
+
+## E-Mail
+
+E-Mails werden bevorzugt über SMTP mit `curl` gesendet. Fallback ist der `mail`-Befehl.
+
+### Konfiguration
+
+```bash
+NOTIFY_EMAIL_ENABLED=true
+NOTIFY_EMAIL_TO="admin@example.com"
+NOTIFY_EMAIL_FROM="crowdsec@meinserver.de"
+NOTIFY_EMAIL_SUBJECT="CrowdSec Manager Report"
+NOTIFY_EMAIL_SMTP_SERVER="smtp.example.com"
+NOTIFY_EMAIL_SMTP_PORT="587" # 587 für STARTTLS, 465 für SSL
+NOTIFY_EMAIL_SMTP_USER="mailuser"
+NOTIFY_EMAIL_SMTP_PASS="mailpass"
+NOTIFY_EMAIL_SMTP_TLS=true
+```
+
+### SMTP-Ports
+
+| Port | Protokoll | `SMTP_TLS` |
+|------|-----------|-----------|
+| 587 | STARTTLS | `true` (verwendet `smtps://`) |
+| 465 | Implicit TLS/SSL | `true` |
+| 25 | Unverschlüsselt | `false` |
+
+### Fallback
+
+Wenn `curl` nicht verfügbar ist, wird versucht den `mail`-Befehl zu verwenden. Dieser muss auf dem System konfiguriert sein (z.B. mit Postfix/Sendmail).
+
+### Voraussetzungen
+
+- `curl` (bevorzugt) oder `mail`
+- SMTP-Server muss erreichbar sein
+
+---
+
+## Sicherheitshinweise
+
+- **Tokens und Passwörter** werden in der Konfigurationsdatei gespeichert. Stelle sicher, dass die Datei nur von berechtigten Benutzern lesbar ist:
+ ```bash
+ chmod 600 config/crowdsec-manager.conf
+ ```
+- Keine sensiblen Daten (Passwörter, Tokens) werden in der Log-Ausgabe angezeigt
+- Verwende eigene/selbst gehostete Instanzen (Ntfy, Gotify) für erhöhte Sicherheit
diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md
new file mode 100644
index 0000000..c0aa2cb
--- /dev/null
+++ b/docs/TROUBLESHOOTING.md
@@ -0,0 +1,135 @@
+# Troubleshooting & FAQ
+
+Hilfe bei häufigen Problemen und Antworten auf oft gestellte Fragen.
+
+---
+
+## Häufige Probleme
+
+### "dig: command not found"
+
+```bash
+# Debian/Ubuntu
+sudo apt install dnsutils
+
+# RHEL/CentOS/Fedora
+sudo dnf install bind-utils
+
+# Alpine
+apk add --no-cache bind-tools
+
+# Arch Linux
+sudo pacman -S bind
+```
+
+### "CrowdSec ist nicht erreichbar"
+
+1. Prüfe ob CrowdSec läuft:
+ ```bash
+ docker ps | grep crowdsec
+ # oder
+ systemctl status crowdsec
+ ```
+
+2. Teste den Befehl manuell:
+ ```bash
+ docker exec crowdsec cscli version
+ ```
+
+3. Prüfe Docker-Berechtigungen:
+ ```bash
+ groups $(whoami) | grep docker
+ ```
+
+### "Lock-File existiert bereits"
+
+1. Prüfe ob noch ein Prozess läuft:
+ ```bash
+ cat /tmp/crowdsec-manager.lock
+ ps aux | grep crowdsec-manager
+ ```
+
+2. Wenn kein Prozess läuft, Lock manuell entfernen:
+ ```bash
+ rm /tmp/crowdsec-manager.lock
+ ```
+
+### Domain wird nicht aufgelöst
+
+1. Manuell testen:
+ ```bash
+ dig +short A example.com
+ dig +short @8.8.8.8 A example.com
+ ```
+
+2. DNS-Server in der Config setzen:
+ ```bash
+ DNS_SERVER="8.8.8.8"
+ ```
+
+3. Timeout erhöhen:
+ ```bash
+ DNS_TIMEOUT=10
+ DNS_RETRIES=5
+ ```
+
+### Berechtigungs-Fehler
+
+```bash
+# Script ausführbar machen
+chmod +x crowdsec-manager.sh
+
+# Log-Datei
+sudo touch /var/log/crowdsec-manager.log
+sudo chown $(whoami) /var/log/crowdsec-manager.log
+
+# Backup-Verzeichnis
+sudo mkdir -p /var/backup/crowdsec-manager
+sudo chown $(whoami) /var/backup/crowdsec-manager
+```
+
+### Interaktives Menü startet statt Cron-Sync
+
+Ab v0.1.0 startet das Script standardmäßig das interaktive Menü. Für den Cron-Betrieb **muss** `--run` angegeben werden:
+
+```bash
+# Falsch (startet interaktives Menü):
+*/30 * * * * /opt/.../crowdsec-manager.sh
+
+# Richtig:
+*/30 * * * * /opt/.../crowdsec-manager.sh --run
+```
+
+---
+
+## FAQ
+
+**F: Werden manuell erstellte CrowdSec-Decisions beeinflusst?**
+A: Nein. Das Script verwaltet nur Decisions mit dem konfigurierten `ALLOWLIST_REASON`. Alle manuell erstellten Einträge bleiben unberührt.
+
+**F: Was passiert wenn CrowdSec nicht erreichbar ist?**
+A: Das Script bricht mit Exit-Code 1 ab. Beim nächsten Cron-Lauf wird erneut versucht. Die bestehende Allowlist bleibt unverändert.
+
+**F: Was passiert wenn eine Domain nicht aufgelöst werden kann?**
+A: Die Domain wird übersprungen und eine Warnung geloggt. Alle anderen Domains werden normal verarbeitet. Bestehende Einträge in der Allowlist bleiben erhalten, da sie persistent sind.
+
+**F: Kann ich das Script auf einem anderen Server als CrowdSec laufen lassen?**
+A: Ja, über `CSCLI_CMD` z.B. via SSH:
+```bash
+CSCLI_CMD="ssh user@crowdsec-server cscli"
+```
+
+**F: Unterstützt das Script Wildcard-Domains?**
+A: Nein, Wildcard-Domains (z.B. `*.example.com`) werden nicht unterstützt. Jede Subdomain muss einzeln eingetragen werden.
+
+**F: Wie viele Einträge kann das Script verwalten?**
+A: Kein festes Limit. Die DNS-Auflösung wird linear pro Eintrag durchgeführt. Bei hunderten Domains kann die Laufzeit einige Minuten betragen.
+
+**F: Kann ich das Script unter macOS verwenden?**
+A: Ja, solange `dig` und Bash 4+ verfügbar sind. macOS liefert standardmäßig Bash 3.2 – installiere Bash 4+ z.B. via Homebrew.
+
+**F: Was ist der Unterschied zwischen dem interaktiven Menü und dem CLI?**
+A: Das interaktive Menü (Standard) bietet eine grafische Auswahl aller Funktionen. Der CLI-Modus (`--run`, `--list`, etc.) ist für automatisierten/Cron-Betrieb gedacht.
+
+**F: Können im interaktiven Menü auch direkte cscli-Befehle ausgeführt werden?**
+A: Ja, Menüpunkt 40 ermöglicht die Ausführung beliebiger cscli-Befehle über den konfigurierten Basisbefehl.
diff --git a/examples/crontab.example b/examples/crontab.example
new file mode 100644
index 0000000..2017dca
--- /dev/null
+++ b/examples/crontab.example
@@ -0,0 +1,32 @@
+# ============================================================================
+# CrowdSec Manager v0.1.0 - Crontab Beispiele
+# ============================================================================
+# Installation: crontab -e (und gewünschte Zeile einfügen)
+#
+# WICHTIG: Das Script startet ohne Parameter das interaktive Menü.
+# Für den Cron-Betrieb MUSS der Parameter --run angegeben werden!
+# ============================================================================
+
+# --- Standard: Alle 30 Minuten ausführen ---
+*/30 * * * * /opt/crowdsec-manager/crowdsec-manager.sh --run >> /var/log/crowdsec-manager-cron.log 2>&1
+
+# --- Alle 15 Minuten ---
+# */15 * * * * /opt/crowdsec-manager/crowdsec-manager.sh --run >> /var/log/crowdsec-manager-cron.log 2>&1
+
+# --- Stündlich ---
+# 0 * * * * /opt/crowdsec-manager/crowdsec-manager.sh --run >> /var/log/crowdsec-manager-cron.log 2>&1
+
+# --- Alle 6 Stunden ---
+# 0 */6 * * * /opt/crowdsec-manager/crowdsec-manager.sh --run >> /var/log/crowdsec-manager-cron.log 2>&1
+
+# --- Täglich um 03:00 Uhr ---
+# 0 3 * * * /opt/crowdsec-manager/crowdsec-manager.sh --run >> /var/log/crowdsec-manager-cron.log 2>&1
+
+# --- Mit eigenem Config-Pfad ---
+# */30 * * * * /opt/crowdsec-manager/crowdsec-manager.sh --run --config /etc/crowdsec-manager/meine-config.conf >> /var/log/crowdsec-manager-cron.log 2>&1
+
+# --- Im Quiet-Modus (nur Fehler loggen) ---
+# */30 * * * * /opt/crowdsec-manager/crowdsec-manager.sh --run --quiet >> /var/log/crowdsec-manager-cron.log 2>&1
+
+# --- Dry-Run zum Testen ---
+# */30 * * * * /opt/crowdsec-manager/crowdsec-manager.sh --run --dry-run >> /var/log/crowdsec-manager-cron.log 2>&1