From efb11a0bfa88537c046c1c8793570b6271115a07 Mon Sep 17 00:00:00 2001 From: scriptos Date: Wed, 11 Mar 2026 16:49:45 +0100 Subject: [PATCH 1/2] Initialer Import --- .gitattributes | 2 + .gitignore | 24 + README.md | 293 +++++- config/crowdsec-manager.conf | 183 ++++ crowdsec-manager.sh | 1716 ++++++++++++++++++++++++++++++++++ docs/ADMIN.md | 198 ++++ docs/ALLOWLIST.md | 202 ++++ docs/CONFIGURATION.md | 278 ++++++ docs/DOCUMENTATION.md | 182 ++++ docs/NOTIFICATIONS.md | 205 ++++ docs/TROUBLESHOOTING.md | 135 +++ examples/crontab.example | 32 + 12 files changed, 3442 insertions(+), 8 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 config/crowdsec-manager.conf create mode 100644 crowdsec-manager.sh create mode 100644 docs/ADMIN.md create mode 100644 docs/ALLOWLIST.md create mode 100644 docs/CONFIGURATION.md create mode 100644 docs/DOCUMENTATION.md create mode 100644 docs/NOTIFICATIONS.md create mode 100644 docs/TROUBLESHOOTING.md create mode 100644 examples/crontab.example 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 License | Matrix Matrix | Mastodon 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..3fe307d --- /dev/null +++ b/config/crowdsec-manager.conf @@ -0,0 +1,183 @@ +#!/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-Decision-Liste in CrowdSec +# Diese Liste wird vom Script angelegt und verwaltet +ALLOWLIST_NAME="crowdsec-manager" + +# Beschreibung der Allowlist +ALLOWLIST_DESCRIPTION="Managed by crowdsec-manager script" + +# Dauer für die Allowlist-Einträge (wie lange ein Eintrag gültig ist) +# Format: s=Sekunden, m=Minuten, h=Stunden +# Empfehlung: Etwas länger als das Cron-Intervall setzen +ALLOWLIST_DURATION="25h" + +# Typ der Allowlist-Einträge +# Mögliche Werte: "allow" (Whitelist) +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..b1e4cc6 --- /dev/null +++ b/crowdsec-manager.sh @@ -0,0 +1,1716 @@ +#!/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 +} + +get_current_allowlist() { + log "INFO" "Rufe aktuelle Allowlist '${ALLOWLIST_NAME}' ab..." + + local decisions + decisions=$(cscli_exec "decisions list -o json --origin $ALLOWLIST_REASON" 2>/dev/null || echo "null") + + if [[ "$decisions" == "null" ]] || [[ -z "$decisions" ]]; then + log "DEBUG" "Keine bestehenden Einträge in der Allowlist gefunden" + return 0 + fi + + echo "$decisions" | grep -oP '"value"\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" + local ip_type="ip" + + if is_cidr "$ip"; then + ip_type="range" + fi + + log "DEBUG" "Füge hinzu: $ip (Typ: $ip_type)" + + if [[ "${DRY_RUN:-false}" == "true" ]]; then + log "INFO" "[DRY-RUN] Würde hinzufügen: $ip" + return 0 + fi + + local result + if result=$(cscli_exec "decisions add --type allow --scope $ip_type --value '$ip' --duration '${ALLOWLIST_DURATION}' --reason '${ALLOWLIST_REASON}' --origin '${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 "decisions delete --value '$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 "decisions list --origin $ALLOWLIST_REASON" 2>/dev/null || echo "Keine Einträge oder Fehler beim Abrufen." + 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 alle Einträge mit Origin '$ALLOWLIST_REASON' entfernen" + return 0 + fi + + if cscli_exec "decisions delete --origin $ALLOWLIST_REASON" 2>/dev/null; then + log "INFO" "Alle verwalteten Einträge wurden entfernt." + else + log "ERROR" "Fehler beim Entfernen der Einträge." + 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..724b80f --- /dev/null +++ b/docs/ALLOWLIST.md @@ -0,0 +1,202 @@ +# 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 +``` + +### ALLOWLIST_DURATION vs. Cron-Intervall + +Die `ALLOWLIST_DURATION` muss **immer größer** als das Cron-Intervall sein: + +``` +Cron: */30 * * * * → ALLOWLIST_DURATION="1h" ✅ +Cron: 0 * * * * → ALLOWLIST_DURATION="2h" ✅ +Cron: 0 * * * * → ALLOWLIST_DURATION="30m" ❌ Zu kurz! +``` + +### 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..b7a8cbe --- /dev/null +++ b/docs/CONFIGURATION.md @@ -0,0 +1,278 @@ +# 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 zur Identifizierung in CrowdSec verwendet. + +```bash +ALLOWLIST_NAME="crowdsec-manager" +``` + +### `ALLOWLIST_DURATION` + +Gültigkeitsdauer eines Allowlist-Eintrags. Sollte **etwas länger als das Cron-Intervall** sein. + +| Cron-Intervall | Empfohlene Duration | +|:---:|:---:| +| Alle 15 Minuten | `30m` | +| Alle 30 Minuten | `1h` | +| Stündlich | `2h` | +| Alle 6 Stunden | `12h` | +| Täglich | `25h` | + +```bash +ALLOWLIST_DURATION="25h" +``` + +### `ALLOWLIST_REASON` + +Der `origin`-Tag für die verwalteten Decisions. Damit unterscheidet das Script seine eigenen Einträge von manuell erstellten. + +```bash +ALLOWLIST_REASON="allowlist-manager-auto" +``` + +> **Wichtig:** Ändere diesen Wert NICHT nach der ersten Ausführung, da sonst bestehende Einträge nicht mehr zugeordnet werden können. + +### `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..b86ba80 --- /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 bleiben bis zum Ablauf der `ALLOWLIST_DURATION` bestehen. + +**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 -- 2.54.0 From 233d423ecc79925b56adb3a0114e36453a7d2367 Mon Sep 17 00:00:00 2001 From: scriptos Date: Wed, 11 Mar 2026 21:25:53 +0100 Subject: [PATCH 2/2] Initial Release 0.1.0 --- config/crowdsec-manager.conf | 12 ++----- crowdsec-manager.sh | 61 ++++++++++++++++++++++++++---------- docs/ALLOWLIST.md | 10 ------ docs/CONFIGURATION.md | 22 ++----------- docs/TROUBLESHOOTING.md | 2 +- 5 files changed, 50 insertions(+), 57 deletions(-) diff --git a/config/crowdsec-manager.conf b/config/crowdsec-manager.conf index 3fe307d..ce3788f 100644 --- a/config/crowdsec-manager.conf +++ b/config/crowdsec-manager.conf @@ -18,20 +18,14 @@ # Podman: CSCLI_CMD="podman exec crowdsec cscli" CSCLI_CMD="docker exec crowdsec cscli" -# Name der Allowlist-Decision-Liste in CrowdSec -# Diese Liste wird vom Script angelegt und verwaltet +# 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" -# Dauer für die Allowlist-Einträge (wie lange ein Eintrag gültig ist) -# Format: s=Sekunden, m=Minuten, h=Stunden -# Empfehlung: Etwas länger als das Cron-Intervall setzen -ALLOWLIST_DURATION="25h" - -# Typ der Allowlist-Einträge -# Mögliche Werte: "allow" (Whitelist) +# Beschreibung für einzelne Einträge in der Allowlist ALLOWLIST_REASON="allowlist-manager-auto" # ---------------------------------------------------------------------------- diff --git a/crowdsec-manager.sh b/crowdsec-manager.sh index b1e4cc6..a1c6888 100644 --- a/crowdsec-manager.sh +++ b/crowdsec-manager.sh @@ -376,18 +376,42 @@ crowdsec_health_check() { 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..." - local decisions - decisions=$(cscli_exec "decisions list -o json --origin $ALLOWLIST_REASON" 2>/dev/null || echo "null") + if ! ensure_allowlist_exists; then + return 1 + fi - if [[ "$decisions" == "null" ]] || [[ -z "$decisions" ]]; then + # 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 - echo "$decisions" | grep -oP '"value"\s*:\s*"\K[^"]+' | sort -u > "$CURRENT_IPS_FILE" 2>/dev/null || true + # 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") @@ -396,13 +420,8 @@ get_current_allowlist() { add_to_allowlist() { local ip="$1" - local ip_type="ip" - if is_cidr "$ip"; then - ip_type="range" - fi - - log "DEBUG" "Füge hinzu: $ip (Typ: $ip_type)" + log "DEBUG" "Füge hinzu: $ip" if [[ "${DRY_RUN:-false}" == "true" ]]; then log "INFO" "[DRY-RUN] Würde hinzufügen: $ip" @@ -410,7 +429,7 @@ add_to_allowlist() { fi local result - if result=$(cscli_exec "decisions add --type allow --scope $ip_type --value '$ip' --duration '${ALLOWLIST_DURATION}' --reason '${ALLOWLIST_REASON}' --origin '${ALLOWLIST_REASON}'" 2>&1); then + 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 @@ -433,7 +452,7 @@ remove_from_allowlist() { fi local result - if result=$(cscli_exec "decisions delete --value '$ip'" 2>&1); then + if result=$(cscli_exec "allowlists remove ${ALLOWLIST_NAME} $ip" 2>&1); then log "DEBUG" "Erfolgreich entfernt: $ip" STAT_REMOVED=$((STAT_REMOVED + 1)) return 0 @@ -1493,7 +1512,7 @@ show_current_list() { echo "" echo -e "${BOLD}Aktuelle CrowdSec Allowlist (${ALLOWLIST_NAME}):${NC}" draw_line "-" 60 - cscli_exec "decisions list --origin $ALLOWLIST_REASON" 2>/dev/null || echo "Keine Einträge oder Fehler beim Abrufen." + cscli_exec "allowlists inspect ${ALLOWLIST_NAME}" 2>/dev/null || echo "Keine Einträge oder Allowlist nicht gefunden." draw_line "-" 60 } @@ -1501,14 +1520,22 @@ flush_allowlist() { log "WARN" "Entferne ALLE verwalteten Einträge aus der Allowlist..." if [[ "${DRY_RUN:-false}" == "true" ]]; then - log "INFO" "[DRY-RUN] Würde alle Einträge mit Origin '$ALLOWLIST_REASON' entfernen" + log "INFO" "[DRY-RUN] Würde Allowlist '${ALLOWLIST_NAME}' löschen und neu erstellen" return 0 fi - if cscli_exec "decisions delete --origin $ALLOWLIST_REASON" 2>/dev/null; then - log "INFO" "Alle verwalteten Einträge wurden entfernt." + # 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 "ERROR" "Fehler beim Entfernen der Einträge." + 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 } diff --git a/docs/ALLOWLIST.md b/docs/ALLOWLIST.md index 724b80f..33f8c10 100644 --- a/docs/ALLOWLIST.md +++ b/docs/ALLOWLIST.md @@ -151,16 +151,6 @@ Empfohlener Eintrag: */30 * * * * /opt/crowdsec-manager/crowdsec-manager.sh --run >> /var/log/crowdsec-manager-cron.log 2>&1 ``` -### ALLOWLIST_DURATION vs. Cron-Intervall - -Die `ALLOWLIST_DURATION` muss **immer größer** als das Cron-Intervall sein: - -``` -Cron: */30 * * * * → ALLOWLIST_DURATION="1h" ✅ -Cron: 0 * * * * → ALLOWLIST_DURATION="2h" ✅ -Cron: 0 * * * * → ALLOWLIST_DURATION="30m" ❌ Zu kurz! -``` - ### Cron-spezifisches Verhalten Das Script erkennt automatisch ob es im Terminal oder per Cron läuft: diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index b7a8cbe..e4032b9 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -52,38 +52,20 @@ docker exec crowdsec cscli version ### `ALLOWLIST_NAME` -Name der verwalteten Allowlist. Wird zur Identifizierung in CrowdSec verwendet. +Name der verwalteten Allowlist. Wird via `cscli allowlists` in CrowdSec verwaltet. ```bash ALLOWLIST_NAME="crowdsec-manager" ``` -### `ALLOWLIST_DURATION` - -Gültigkeitsdauer eines Allowlist-Eintrags. Sollte **etwas länger als das Cron-Intervall** sein. - -| Cron-Intervall | Empfohlene Duration | -|:---:|:---:| -| Alle 15 Minuten | `30m` | -| Alle 30 Minuten | `1h` | -| Stündlich | `2h` | -| Alle 6 Stunden | `12h` | -| Täglich | `25h` | - -```bash -ALLOWLIST_DURATION="25h" -``` - ### `ALLOWLIST_REASON` -Der `origin`-Tag für die verwalteten Decisions. Damit unterscheidet das Script seine eigenen Einträge von manuell erstellten. +Beschreibung für die einzelnen Einträge in der Allowlist. Dient zur Nachverfolgbarkeit. ```bash ALLOWLIST_REASON="allowlist-manager-auto" ``` -> **Wichtig:** Ändere diesen Wert NICHT nach der ersten Ausführung, da sonst bestehende Einträge nicht mehr zugeordnet werden können. - ### `HEALTH_CHECK` Ob vor dem Sync ein CrowdSec-Health-Check durchgeführt werden soll. diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index b86ba80..c0aa2cb 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -111,7 +111,7 @@ A: Nein. Das Script verwaltet nur Decisions mit dem konfigurierten `ALLOWLIST_RE 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 bleiben bis zum Ablauf der `ALLOWLIST_DURATION` bestehen. +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: -- 2.54.0