From f685e7eb3e738da3c97f9d9b36e55557c7f81a68 Mon Sep 17 00:00:00 2001 From: scriptos Date: Thu, 5 Mar 2026 22:59:51 +0100 Subject: [PATCH] Handling mit dem Sync externer Listen verbessert. --- adguard-shield.conf | 5 +++++ doc/benachrichtigungen.md | 10 ++++++++++ doc/konfiguration.md | 23 +++++++++++++++++++++++ external-blocklist-worker.sh | 34 +++++++++++++++++++++++++++------- install.sh | 30 +++++++++++++++--------------- 5 files changed, 80 insertions(+), 22 deletions(-) diff --git a/adguard-shield.conf b/adguard-shield.conf index 0a612e7..e52eef5 100644 --- a/adguard-shield.conf +++ b/adguard-shield.conf @@ -146,6 +146,11 @@ EXTERNAL_BLOCKLIST_BAN_DURATION=0 # Automatisch IPs entsperren die aus der externen Liste entfernt wurden? EXTERNAL_BLOCKLIST_AUTO_UNBAN=true +# Benachrichtigungen bei Blocklist-Sperren senden? +# Bei Listen mit vielen IPs empfiehlt sich false, da sonst beim Sync +# hunderte Benachrichtigungen auf einmal verschickt werden. +EXTERNAL_BLOCKLIST_NOTIFY=false + # Lokaler Cache-Pfad für die heruntergeladene Blocklist EXTERNAL_BLOCKLIST_CACHE_DIR="/var/lib/adguard-shield/external-blocklist" diff --git a/doc/benachrichtigungen.md b/doc/benachrichtigungen.md index aa96326..9c1c7b6 100644 --- a/doc/benachrichtigungen.md +++ b/doc/benachrichtigungen.md @@ -101,6 +101,16 @@ Sendet einen POST mit JSON-Body: } ``` +## Benachrichtigungen und externe Blocklisten + +Bei Sperren aus der **externen Blocklist** werden Benachrichtigungen separat über `EXTERNAL_BLOCKLIST_NOTIFY` gesteuert — unabhängig von `NOTIFY_ENABLED`. + +| Parameter | Standard | Beschreibung | +|-----------|----------|--------------| +| `EXTERNAL_BLOCKLIST_NOTIFY` | `false` | Benachrichtigungen bei Blocklist-Sperren aktivieren | + +> **Wichtig:** Bei großen Listen `EXTERNAL_BLOCKLIST_NOTIFY=false` belassen. Beim ersten Sync (oder nach einem `blocklist-flush`) werden alle IPs der Liste auf einmal gesperrt — mit `true` würde das zu einer Nachrichten-Flut im Notification-Channel führen. Nur auf `true` setzen, wenn die Liste sehr klein ist. + ## Beispiel-Nachrichten **Service gestartet:** diff --git a/doc/konfiguration.md b/doc/konfiguration.md index 684decb..bb30466 100644 --- a/doc/konfiguration.md +++ b/doc/konfiguration.md @@ -160,6 +160,7 @@ Ermöglicht das Einbinden externer Blocklisten, die IPv4-Adressen, IPv6-Adressen | `EXTERNAL_BLOCKLIST_INTERVAL` | `300` | Prüfintervall in Sekunden (300 = 5 Min.) | | `EXTERNAL_BLOCKLIST_BAN_DURATION` | `0` | Sperrdauer in Sekunden (0 = permanent bis IP aus Liste entfernt) | | `EXTERNAL_BLOCKLIST_AUTO_UNBAN` | `true` | IPs automatisch entsperren wenn aus Liste entfernt | +| `EXTERNAL_BLOCKLIST_NOTIFY` | `false` | Webhook-Benachrichtigungen bei Blocklist-Sperren senden. Bei großen Listen unbedingt auf `false` lassen — beim ersten Sync kommen sonst hunderte Nachrichten auf einmal. | | `EXTERNAL_BLOCKLIST_CACHE_DIR` | `/var/lib/adguard-shield/external-blocklist` | Lokaler Cache für heruntergeladene Listen | ### AbuseIPDB Reporting @@ -227,6 +228,28 @@ malware.example.net > **Hinweis zur Hostname-Auflösung:** Da AdGuard Shield idealerweise auf demselben Host wie der DNS-Resolver läuft, verwendet der Worker automatisch den lokalen Resolver. Hostnamen die bereits von AdGuard geblockt werden (Antwort `0.0.0.0`) werden übersprungen und nicht importiert. +#### Dateiformat der Blocklist + +Beim Erstellen eigener Blocklisten müssen zwei Dinge beachtet werden: + +- **Zeichenkodierung:** Datei in **UTF-8 ohne BOM** speichern. Dateien mit BOM (z.B. Standard-Einstellung in Notepad++) führen dazu, dass der erste Eintrag als ungültig erkannt wird. +- **Zeilenenden:** Datei mit **Unix-Zeilenenden (LF)** speichern, nicht Windows (CRLF). CRLF-Zeilenenden führen dazu, dass alle Einträge als ungültig abgelehnt werden. + +In **Notepad++:** Kodierung → „UTF-8 (ohne BOM)" und Bearbeiten → Zeilenende-Konvertierung → Unix (LF). +In **VS Code:** Unten rechts auf `CRLF` klicken → `LF` auswählen; Zeichenkodierung ebenfalls unten rechts prüfen. + +> **Empfehlung:** IP-Adressen und Hostnamen in **getrennten Listen** pflegen. Bei Hostname-Listen löst der Worker jeden Eintrag per DNS auf — das ist langsamer als direkte IP-Listen und liefert je nach DNS-Antwort mehrere IPs pro Eintrag. Getrennte Listen sind außerdem übersichtlicher und einfacher zu pflegen. + +#### Synchronisierungsverhalten + +Der Worker synchronisiert die Blocklisten: + +- **Beim Service-Start:** Der erste Sync läuft **sofort** beim Start — ohne Wartezeit. Danach beginnt erst das periodische Intervall (`EXTERNAL_BLOCKLIST_INTERVAL`). +- **Automatisch im Hintergrund:** Alle `EXTERNAL_BLOCKLIST_INTERVAL` Sekunden (Standard: 300s = 5 Min.) wird geprüft, ob sich die Liste geändert hat. Unveränderte Listen (HTTP 304 oder gleicher Inhalt) werden nicht erneut verarbeitet. +- **Manuell:** `sudo adguard-shield.sh blocklist-sync` erzwingt sofort einen Sync, unabhängig vom laufenden Worker. + +> **Nach einem Neustart** (Server oder Service) werden fehlende iptables-Regeln beim nächsten Sync automatisch nachgezogen. + 2. Aktiviere die Blocklist in der Konfiguration: ```bash diff --git a/external-blocklist-worker.sh b/external-blocklist-worker.sh index 4e41300..52da891 100644 --- a/external-blocklist-worker.sh +++ b/external-blocklist-worker.sh @@ -39,7 +39,7 @@ log() { local timestamp timestamp="$(date '+%Y-%m-%d %H:%M:%S')" local log_entry="[$timestamp] [$level] [BLOCKLIST-WORKER] $message" - echo "$log_entry" | tee -a "$LOG_FILE" + echo "$log_entry" | tee -a "$LOG_FILE" >&2 fi } @@ -115,6 +115,16 @@ ban_ip() { # Bereits gesperrt? if [[ -f "$state_file" ]]; then + # iptables-Regel prüfen und ggf. nachziehen (z.B. nach Neustart verloren gegangen) + if [[ "$ip" == *:* ]]; then + if ! ip6tables -C "$IPTABLES_CHAIN" -s "$ip" -j DROP 2>/dev/null; then + ip6tables -I "$IPTABLES_CHAIN" -s "$ip" -j DROP 2>/dev/null || true + fi + else + if ! iptables -C "$IPTABLES_CHAIN" -s "$ip" -j DROP 2>/dev/null; then + iptables -I "$IPTABLES_CHAIN" -s "$ip" -j DROP 2>/dev/null || true + fi + fi log "DEBUG" "IP $ip bereits über externe Blocklist gesperrt" return 0 fi @@ -163,8 +173,8 @@ EOF log_ban_history "BAN" "$ip" "external-blocklist" - # Benachrichtigung senden - if [[ "$NOTIFY_ENABLED" == "true" ]]; then + # Benachrichtigung senden (nur wenn EXTERNAL_BLOCKLIST_NOTIFY=true) + if [[ "$NOTIFY_ENABLED" == "true" && "${EXTERNAL_BLOCKLIST_NOTIFY:-false}" == "true" ]]; then send_notification "ban" "$ip" fi } @@ -188,7 +198,7 @@ unban_ip() { rm -f "$state_file" log_ban_history "UNBAN" "$ip" "$reason" - if [[ "$NOTIFY_ENABLED" == "true" ]]; then + if [[ "$NOTIFY_ENABLED" == "true" && "${EXTERNAL_BLOCKLIST_NOTIFY:-false}" == "true" ]]; then send_notification "unban" "$ip" fi } @@ -198,7 +208,10 @@ send_notification() { local action="$1" local ip="$2" - [[ -z "${NOTIFY_WEBHOOK_URL:-}" ]] && return + # ntfy benötigt keine NOTIFY_WEBHOOK_URL, alle anderen schon + if [[ "${NOTIFY_TYPE:-generic}" != "ntfy" && -z "${NOTIFY_WEBHOOK_URL:-}" ]]; then + return + fi local message if [[ "$action" == "ban" ]]; then @@ -382,6 +395,9 @@ parse_blocklist_ips() { [[ -f "$cache_file" ]] || return while IFS= read -r line; do + line="${line%$'\r'}" # Windows-Zeilenenden (CRLF) entfernen + line="${line#$'\xef\xbb\xbf'}" # UTF-8 BOM entfernen (erste Zeile) + # Leerzeilen und Kommentarzeilen überspringen [[ -z "$line" ]] && continue [[ "$line" =~ ^[[:space:]]*# ]] && continue @@ -439,7 +455,7 @@ parse_blocklist_ips() { continue fi local resolved - resolved=$(getent ahosts "$line" 2>/dev/null | awk '{print $1}' | sort -u) + resolved=$(getent ahosts "$line" 2>/dev/null | awk '{print $1}' | sort -u) || resolved="" if [[ -z "$resolved" ]]; then log "WARN" "Hostname konnte nicht aufgelöst werden: $line" continue @@ -537,8 +553,12 @@ sync_blocklists() { continue fi + local _state_file_before="${STATE_DIR}/ext_${ip//[:/]/_}.ban" + local _was_new=false + [[ ! -f "$_state_file_before" ]] && _was_new=true + ban_ip "$ip" - new_bans=$((new_bans + 1)) + [[ "$_was_new" == "true" ]] && new_bans=$((new_bans + 1)) done < "$unique_ips_file" # ─── Entfernte IPs entsperren ──────────────────────────────────────────── diff --git a/install.sh b/install.sh index a01228d..f3d2412 100644 --- a/install.sh +++ b/install.sh @@ -139,7 +139,7 @@ show_menu() { echo -e " ${CYAN}5)${NC} Hilfe — Hilfe & Befehlsübersicht anzeigen" echo -e " ${CYAN}0)${NC} Beenden" echo "" - read -rp " Auswahl [0-5]: " choice + read -rep " Auswahl [0-5]: " choice echo "" case "$choice" in @@ -350,7 +350,7 @@ install_service() { echo "" # Interaktiv: Autostart beim Booten? - read -rp " Soll AdGuard Shield beim Booten automatisch starten? [J/n]: " autostart + read -rep " Soll AdGuard Shield beim Booten automatisch starten? [J/n]: " autostart if [[ "${autostart,,}" != "n" ]]; then systemctl enable adguard-shield.service echo -e " ✅ Autostart aktiviert" @@ -369,17 +369,17 @@ configure() { local conf="$INSTALL_DIR/adguard-shield.conf" # AdGuard URL - read -rp " AdGuard Home URL [http://127.0.0.1:3000]: " adguard_url + read -rep " AdGuard Home URL [http://127.0.0.1:3000]: " adguard_url adguard_url="${adguard_url:-http://127.0.0.1:3000}" sed -i "s|^ADGUARD_URL=.*|ADGUARD_URL=\"$adguard_url\"|" "$conf" # Benutzername - read -rp " AdGuard Home Benutzername [admin]: " adguard_user + read -rep " AdGuard Home Benutzername [admin]: " adguard_user adguard_user="${adguard_user:-admin}" sed -i "s|^ADGUARD_USER=.*|ADGUARD_USER=\"$adguard_user\"|" "$conf" # Passwort - read -rsp " AdGuard Home Passwort: " adguard_pass + read -resp " AdGuard Home Passwort: " adguard_pass echo "" if [[ -n "$adguard_pass" ]]; then # Einfache Quotes damit $-Zeichen im Passwort nicht expandiert werden @@ -387,17 +387,17 @@ configure() { fi # Rate Limit - read -rp " Max. Anfragen pro Domain/Client pro Minute [30]: " rate_limit + read -rep " Max. Anfragen pro Domain/Client pro Minute [30]: " rate_limit rate_limit="${rate_limit:-30}" sed -i "s|^RATE_LIMIT_MAX_REQUESTS=.*|RATE_LIMIT_MAX_REQUESTS=$rate_limit|" "$conf" # Sperrdauer - read -rp " Sperrdauer in Sekunden [3600]: " ban_duration + read -rep " Sperrdauer in Sekunden [3600]: " ban_duration ban_duration="${ban_duration:-3600}" sed -i "s|^BAN_DURATION=.*|BAN_DURATION=$ban_duration|" "$conf" # Whitelist - read -rp " Whitelist IPs (kommagetrennt) [127.0.0.1,::1]: " whitelist + read -rep " Whitelist IPs (kommagetrennt) [127.0.0.1,::1]: " whitelist whitelist="${whitelist:-127.0.0.1,::1}" sed -i "s|^WHITELIST=.*|WHITELIST=\"$whitelist\"|" "$conf" @@ -575,7 +575,7 @@ do_install() { if [[ -d "$INSTALL_DIR" ]] && [[ -f "$INSTALL_DIR/adguard-shield.sh" ]]; then echo -e "${YELLOW}AdGuard Shield ist bereits installiert!${NC}" echo "" - read -rp " Möchtest du stattdessen ein Update durchführen? [j/N]: " do_upd + read -rep " Möchtest du stattdessen ein Update durchführen? [j/N]: " do_upd if [[ "${do_upd,,}" == "j" ]]; then do_update return @@ -600,7 +600,7 @@ do_install() { # Interaktiv: Service jetzt starten? echo -e "${YELLOW}Service starten:${NC}" - read -rp " Soll der AdGuard Shield Service jetzt gestartet werden? [J/n]: " start_now + read -rep " Soll der AdGuard Shield Service jetzt gestartet werden? [J/n]: " start_now if [[ "${start_now,,}" != "n" ]]; then systemctl start adguard-shield echo -e " ✅ Service gestartet" @@ -644,7 +644,7 @@ do_update() { if systemctl is-enabled adguard-shield &>/dev/null; then echo -e " ℹ️ Autostart ist bereits aktiviert" else - read -rp " Soll AdGuard Shield beim Booten automatisch starten? [J/n]: " autostart + read -rep " Soll AdGuard Shield beim Booten automatisch starten? [J/n]: " autostart if [[ "${autostart,,}" != "n" ]]; then systemctl enable adguard-shield.service echo -e " ✅ Autostart aktiviert" @@ -661,7 +661,7 @@ do_update() { fi if [[ "$service_was_active" == "true" ]]; then - read -rp " Soll der Service jetzt neu gestartet werden? [J/n]: " restart_now + read -rep " Soll der Service jetzt neu gestartet werden? [J/n]: " restart_now if [[ "${restart_now,,}" != "n" ]]; then systemctl restart adguard-shield echo -e " ✅ Service wurde neu gestartet" @@ -670,7 +670,7 @@ do_update() { echo -e " ${YELLOW}Bitte manuell neustarten: sudo systemctl restart adguard-shield${NC}" fi else - read -rp " Soll der Service jetzt gestartet werden? [J/n]: " start_now + read -rep " Soll der Service jetzt gestartet werden? [J/n]: " start_now if [[ "${start_now,,}" != "n" ]]; then systemctl start adguard-shield echo -e " ✅ Service gestartet" @@ -709,7 +709,7 @@ do_uninstall() { echo "" # Sicherheitsabfrage - read -rp " Wirklich deinstallieren? [j/N]: " confirm + read -rep " Wirklich deinstallieren? [j/N]: " confirm if [[ "${confirm,,}" != "j" ]]; then echo -e "${GREEN}Deinstallation abgebrochen.${NC}" exit 0 @@ -735,7 +735,7 @@ do_uninstall() { fi # Dateien entfernen - read -rp " Konfiguration und Logs behalten? [j/N]: " keep + read -rep " Konfiguration und Logs behalten? [j/N]: " keep if [[ "${keep,,}" == "j" ]]; then rm -f "$INSTALL_DIR/adguard-shield.sh" rm -f "$INSTALL_DIR/iptables-helper.sh"