Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c97e327f0d | |||
| c2d6f872f5 | |||
| 633331748f | |||
| df8b18ae08 | |||
| a79586de94 | |||
| b42f458d5a | |||
| 83075f2782 | |||
| 2a1d8ae975 | |||
| 0da5d01641 | |||
| 535be66b55 | |||
| 2e78b9c14e | |||
| 0af79e7a28 | |||
| 606a28ed8e | |||
| 77a5ebb144 | |||
| 01a99489ab | |||
| 2200e80f87 | |||
| 6bdeb5bc31 | |||
| 5451c01603 | |||
| 6daaf67f7c | |||
| 0970218f9b | |||
| db128f3076 | |||
| 6f14219445 | |||
| cb31aa48eb | |||
| 1e8b7557e7 | |||
| 4d1870cc85 | |||
| ebcd70ce8b | |||
| ba342dd571 | |||
| ac1af85810 | |||
| 54b6c877e5 | |||
| 8562202aa7 | |||
| 3361b571cf | |||
| 86eeb2b947 | |||
| cf915c5c80 | |||
| cf1e554a28 | |||
| 657fdbaf6b | |||
| a39dc88770 | |||
| 19f72d5be4 | |||
| 007c2b01bc | |||
| fd8388df0b | |||
| db955263ed | |||
| 4b188193f6 | |||
| ae37610ec0 | |||
| f685e7eb3e | |||
| 5f631ba858 | |||
| 6a0d40ec1a | |||
| 00cd42f35f | |||
| 66f817d656 | |||
| 7a1b61a1db | |||
| 5b3172faa2 | |||
| 1dd8a5f606 | |||
| eb8d6fcc26 | |||
| 74a35d16b7 | |||
| f18770b890 | |||
| 86d90634d2 | |||
| efe14fd0cd | |||
| 449b285c12 | |||
| 6ab1fb96e1 | |||
| c055f7f1d7 | |||
| be504eaad9 | |||
| 1d9f5cca18 | |||
| 09b4580f0e | |||
| 1c38ca7bab | |||
| b6e31e4976 | |||
| 4f679cadd4 | |||
| 9d1879ba55 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.ki-workspace
|
||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"files.eol": "\n",
|
||||
"chat.tools.terminal.autoApprove": {
|
||||
"Rename-Item": true
|
||||
"Rename-Item": true,
|
||||
"ForEach-Object": true,
|
||||
"&": true
|
||||
}
|
||||
}
|
||||
52
README.md
52
README.md
@@ -22,15 +22,22 @@ Wenn ein Client eine bestimmte Domain zu oft anfragt (z.B. >30x pro Minute), wir
|
||||
## Features
|
||||
|
||||
- Automatische Erkennung und Sperre bei Rate-Limit-Verstößen
|
||||
- **Subdomain-Flood-Erkennung** — erkennt Random-Subdomain-Attacken (z.B. `abc123.microsoft.com`, `xyz456.microsoft.com`, ...)
|
||||
- **Progressive Sperren (Recidive)** — Wiederholungstäter werden stufenweise länger gesperrt (wie bei fail2ban), mit automatischem Cleanup abgelaufener Zähler
|
||||
- Unterstützt **alle DNS-Protokolle**: DNS (53), DoH (443), DoT (853), DoQ (784/853/8853)
|
||||
- **IPv4 + IPv6**
|
||||
- Eigene iptables Chain — greift nicht in bestehende Regeln ein
|
||||
- Automatisches Entsperren nach konfigurierbarer Dauer
|
||||
- **Externe Blocklisten** — IP-Adressen von externen Textdateien (URLs) laden und automatisch sperren
|
||||
- **Externe Whitelisten** — Domains/IPs aus externen Listen laden und automatisch whitelisten (ideal für DynDNS)
|
||||
- **GeoIP-Länderfilter** — Länder sperren oder erlauben (Blocklist/Allowlist), mit automatischem MaxMind-DB-Download
|
||||
- **AbuseIPDB Reporting** — permanent gesperrte IPs automatisch an AbuseIPDB melden
|
||||
- **E-Mail Reports** — periodische Statistik-Reports als HTML oder TXT (täglich, wöchentlich, zweiwöchentlich, monatlich)
|
||||
- **Ban-History** — lückenlose Protokollierung aller Sperren/Entsperrungen mit Zeitstempel
|
||||
- Whitelist für vertrauenswürdige IPs
|
||||
- Dry-Run Modus zum gefahrlosen Testen
|
||||
- Benachrichtigungen (Discord, Slack, Gotify, Ntfy)
|
||||
- **Watchdog** — automatischer Health Check alle 5 Minuten mit Recovery und Benachrichtigung bei Service-Ausfall
|
||||
- systemd Service für dauerhaften Betrieb
|
||||
|
||||
## Voraussetzungen
|
||||
@@ -74,46 +81,43 @@ sudo bash install.sh --help # Hilfe anzeigen
|
||||
sudo bash install.sh update # Update mit automatischer Konfigurations-Migration
|
||||
sudo bash install.sh status # Installationsstatus prüfen
|
||||
|
||||
# Deinstallation (kein install.sh benötigt)
|
||||
sudo bash /opt/adguard-shield/uninstall.sh # Direkt aus dem Installationsverzeichnis
|
||||
|
||||
# Monitor
|
||||
sudo /opt/adguard-shield/adguard-shield.sh status # Aktive Sperren anzeigen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh history # Ban-History anzeigen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh unban IP # Einzelne IP entsperren
|
||||
sudo /opt/adguard-shield/adguard-shield.sh flush # Alle Sperren aufheben
|
||||
sudo /opt/adguard-shield/adguard-shield.sh reset-offenses # Offense-Zähler zurücksetzen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh test # API-Verbindung testen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh blocklist-status # Externe Blocklisten Status
|
||||
sudo /opt/adguard-shield/adguard-shield.sh blocklist-sync # Blocklisten manuell synchronisieren
|
||||
sudo /opt/adguard-shield/adguard-shield.sh whitelist-status # Externe Whitelisten Status
|
||||
sudo /opt/adguard-shield/adguard-shield.sh whitelist-sync # Whitelisten manuell synchronisieren
|
||||
sudo /opt/adguard-shield/adguard-shield.sh geoip-status # GeoIP-Status anzeigen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh geoip-sync # GeoIP einmalig prüfen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh geoip-lookup IP # GeoIP-Lookup einer IP
|
||||
sudo /opt/adguard-shield/report-generator.sh send # Report jetzt senden
|
||||
sudo /opt/adguard-shield/report-generator.sh status # Report-Status anzeigen
|
||||
sudo /opt/adguard-shield/report-generator.sh install # Cron-Job einrichten
|
||||
sudo journalctl -u adguard-shield -f # Logs live verfolgen
|
||||
```
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
├── adguard-shield.sh # Haupt-Monitor-Script
|
||||
├── adguard-shield.conf # Konfiguration
|
||||
├── adguard-shield.service # systemd Unit
|
||||
├── external-blocklist-worker.sh # Externer Blocklist-Worker
|
||||
├── iptables-helper.sh # Manuelle iptables-Verwaltung
|
||||
├── unban-expired.sh # Cron-basiertes Entsperren
|
||||
├── install.sh # Installer / Updater / Uninstaller
|
||||
├── README.md
|
||||
└── doc/
|
||||
├── architektur.md # Architektur & Funktionsweise
|
||||
├── konfiguration.md # Alle Parameter erklärt + Konfig-Migration
|
||||
├── befehle.md # Vollständige Befehlsreferenz inkl. Installer
|
||||
├── benachrichtigungen.md # Webhook-Setup (Discord, Slack, Gotify, Ntfy)
|
||||
└── tipps-und-troubleshooting.md
|
||||
```
|
||||
# Watchdog (automatischer Health Check)
|
||||
sudo systemctl status adguard-shield-watchdog.timer # Watchdog-Status
|
||||
sudo systemctl list-timers adguard-shield-watchdog.timer # Nächste Ausführung
|
||||
```
|
||||
|
||||
## Dokumentation
|
||||
|
||||
| Dokument | Inhalt |
|
||||
|----------|--------|
|
||||
| [Architektur](doc/architektur.md) | Wie das Tool funktioniert, iptables-Strategie, Konfig-Migration |
|
||||
| [Konfiguration](doc/konfiguration.md) | Alle Parameter, Ports, Whitelist-Pflege, automatische Migration |
|
||||
| [Befehle](doc/befehle.md) | Vollständige Befehlsreferenz für Installer, Monitor, iptables-Helper und systemd |
|
||||
| [Benachrichtigungen](doc/benachrichtigungen.md) | Setup für Discord, Slack, Gotify, Ntfy |
|
||||
| [Tipps & Troubleshooting](doc/tipps-und-troubleshooting.md) | Best Practices, häufige Probleme, Deinstallation |
|
||||
| [Architektur](docs/architektur.md) | Wie das Tool funktioniert, iptables-Strategie, Konfig-Migration |
|
||||
| [Konfiguration](docs/konfiguration.md) | Alle Parameter, Ports, Whitelist-Pflege, automatische Migration |
|
||||
| [Befehle](docs/befehle.md) | Vollständige Befehlsreferenz für Installer, Monitor, iptables-Helper und systemd |
|
||||
| [Benachrichtigungen](docs/benachrichtigungen.md) | Setup für Discord, Slack, Gotify, Ntfy |
|
||||
| [E-Mail Report](docs/report.md) | Periodische Statistik-Reports per E-Mail (HTML/TXT) |
|
||||
| [Tipps & Troubleshooting](docs/tipps-und-troubleshooting.md) | Best Practices, häufige Probleme, Watchdog, Deinstallation |
|
||||
|
||||
## Lizenz
|
||||
|
||||
|
||||
7
adguard-shield-watchdog.service
Normal file
7
adguard-shield-watchdog.service
Normal file
@@ -0,0 +1,7 @@
|
||||
[Unit]
|
||||
Description=AdGuard Shield - Watchdog Health Check
|
||||
Documentation=https://git.techniverse.net/scriptos/adguard-shield
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/opt/adguard-shield/adguard-shield-watchdog.sh
|
||||
166
adguard-shield-watchdog.sh
Normal file
166
adguard-shield-watchdog.sh
Normal file
@@ -0,0 +1,166 @@
|
||||
#!/bin/bash
|
||||
###############################################################################
|
||||
# AdGuard Shield - Watchdog
|
||||
# Prüft ob der Hauptservice läuft und startet ihn bei Bedarf neu.
|
||||
# Wird über adguard-shield-watchdog.timer alle 5 Minuten ausgeführt.
|
||||
#
|
||||
# Autor: Patrick Asmus
|
||||
# E-Mail: support@techniverse.net
|
||||
# Lizenz: MIT
|
||||
###############################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
INSTALL_DIR="/opt/adguard-shield"
|
||||
CONFIG_FILE="${INSTALL_DIR}/adguard-shield.conf"
|
||||
SERVICE_NAME="adguard-shield.service"
|
||||
LOG_FILE="/var/log/adguard-shield.log"
|
||||
WATCHDOG_STATE_FILE="/var/lib/adguard-shield/watchdog.state"
|
||||
|
||||
# ─── Logging ──────────────────────────────────────────────────────────────────
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
local message="$*"
|
||||
local timestamp
|
||||
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
local log_entry="[$timestamp] [WATCHDOG] [$level] $message"
|
||||
|
||||
echo "$log_entry" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# ─── Benachrichtigung senden ──────────────────────────────────────────────────
|
||||
send_watchdog_notification() {
|
||||
local action="$1" # "recovery" oder "failure"
|
||||
local detail="$2"
|
||||
|
||||
# Konfiguration laden für Benachrichtigungs-Einstellungen
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
return
|
||||
fi
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
if [[ "${NOTIFY_ENABLED:-false}" != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local my_hostname
|
||||
my_hostname=$(hostname)
|
||||
local title message
|
||||
|
||||
if [[ "$action" == "recovery" ]]; then
|
||||
title="🔄 AdGuard Shield Watchdog"
|
||||
message="🔄 AdGuard Shield Watchdog auf ${my_hostname}
|
||||
---
|
||||
Der Service war ausgefallen und wurde automatisch neu gestartet.
|
||||
${detail}"
|
||||
elif [[ "$action" == "failure" ]]; then
|
||||
title="🚨 AdGuard Shield Watchdog"
|
||||
message="🚨 AdGuard Shield Watchdog auf ${my_hostname}
|
||||
---
|
||||
Der Service konnte NICHT automatisch neu gestartet werden!
|
||||
Manuelles Eingreifen erforderlich.
|
||||
${detail}"
|
||||
fi
|
||||
|
||||
case "${NOTIFY_TYPE:-}" in
|
||||
discord)
|
||||
local json_payload
|
||||
json_payload=$(jq -nc --arg msg "$message" '{content: $msg}')
|
||||
curl -s -H "Content-Type: application/json" \
|
||||
-d "$json_payload" \
|
||||
"$NOTIFY_WEBHOOK_URL" &>/dev/null || true
|
||||
;;
|
||||
slack)
|
||||
local json_payload
|
||||
json_payload=$(jq -nc --arg msg "$message" '{text: $msg}')
|
||||
curl -s -H "Content-Type: application/json" \
|
||||
-d "$json_payload" \
|
||||
"$NOTIFY_WEBHOOK_URL" &>/dev/null || true
|
||||
;;
|
||||
gotify)
|
||||
curl -s -X POST "$NOTIFY_WEBHOOK_URL" \
|
||||
-F "title=${title}" \
|
||||
-F "message=${message}" \
|
||||
-F "priority=5" &>/dev/null || true
|
||||
;;
|
||||
ntfy)
|
||||
if [[ -n "${NTFY_TOPIC:-}" ]]; then
|
||||
local ntfy_url="${NTFY_SERVER_URL:-https://ntfy.sh}"
|
||||
local auth_args=()
|
||||
if [[ -n "${NTFY_TOKEN:-}" ]]; then
|
||||
auth_args=(-H "Authorization: Bearer ${NTFY_TOKEN}")
|
||||
fi
|
||||
curl -s \
|
||||
-H "Title: ${title}" \
|
||||
-H "Priority: ${NTFY_PRIORITY:-5}" \
|
||||
-H "Tags: warning,watchdog" \
|
||||
"${auth_args[@]}" \
|
||||
-d "$message" \
|
||||
"${ntfy_url}/${NTFY_TOPIC}" &>/dev/null || true
|
||||
fi
|
||||
;;
|
||||
generic)
|
||||
local json_payload
|
||||
json_payload=$(jq -nc --arg msg "$message" --arg act "watchdog_${action}" \
|
||||
'{message: $msg, action: $act}')
|
||||
curl -s -H "Content-Type: application/json" \
|
||||
-d "$json_payload" \
|
||||
"$NOTIFY_WEBHOOK_URL" &>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── Hauptlogik ──────────────────────────────────────────────────────────────
|
||||
main() {
|
||||
# Verzeichnis für State-Datei sicherstellen
|
||||
mkdir -p "$(dirname "$WATCHDOG_STATE_FILE")"
|
||||
|
||||
# Prüfen ob der Service aktiv ist
|
||||
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||
# Service läuft – falls vorher ausgefallen war, Status zurücksetzen
|
||||
if [[ -f "$WATCHDOG_STATE_FILE" ]]; then
|
||||
rm -f "$WATCHDOG_STATE_FILE"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Service läuft NICHT – Recovery versuchen
|
||||
log "WARN" "Service $SERVICE_NAME ist nicht aktiv – starte Recovery..."
|
||||
|
||||
# Zähler für fehlgeschlagene Recovery-Versuche
|
||||
local fail_count=0
|
||||
if [[ -f "$WATCHDOG_STATE_FILE" ]]; then
|
||||
fail_count=$(cat "$WATCHDOG_STATE_FILE" 2>/dev/null || echo "0")
|
||||
fi
|
||||
|
||||
# systemd reset-failed damit StartLimit zurückgesetzt wird
|
||||
systemctl reset-failed "$SERVICE_NAME" 2>/dev/null || true
|
||||
|
||||
# Service starten
|
||||
if systemctl start "$SERVICE_NAME" 2>/dev/null; then
|
||||
# Kurz warten und prüfen ob er auch wirklich läuft
|
||||
sleep 3
|
||||
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||
log "INFO" "Service $SERVICE_NAME erfolgreich neu gestartet (Watchdog Recovery)"
|
||||
send_watchdog_notification "recovery" "Versuch: $((fail_count + 1))"
|
||||
rm -f "$WATCHDOG_STATE_FILE"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start fehlgeschlagen
|
||||
fail_count=$((fail_count + 1))
|
||||
echo "$fail_count" > "$WATCHDOG_STATE_FILE"
|
||||
log "ERROR" "Service $SERVICE_NAME konnte nicht gestartet werden (Fehlversuch: $fail_count)"
|
||||
|
||||
# Bei jedem 3. Fehlversuch eine Benachrichtigung senden (Spam vermeiden)
|
||||
if [[ $((fail_count % 3)) -eq 1 ]]; then
|
||||
send_watchdog_notification "failure" "Fehlversuche: $fail_count
|
||||
Letzter Fehler: $(systemctl status "$SERVICE_NAME" 2>&1 | tail -5)"
|
||||
fi
|
||||
|
||||
exit 1
|
||||
}
|
||||
|
||||
main
|
||||
11
adguard-shield-watchdog.timer
Normal file
11
adguard-shield-watchdog.timer
Normal file
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=AdGuard Shield - Watchdog Timer
|
||||
Documentation=https://git.techniverse.net/scriptos/adguard-shield
|
||||
|
||||
[Timer]
|
||||
OnBootSec=2min
|
||||
OnUnitActiveSec=5min
|
||||
AccuracySec=30s
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -1,114 +1,102 @@
|
||||
###############################################################################
|
||||
# AdGuard Shield - Konfigurationsdatei
|
||||
# Schutz vor übermäßigen DNS-Anfragen einzelner Clients
|
||||
# Ausführliche Dokumentation: docs/konfiguration.md
|
||||
###############################################################################
|
||||
|
||||
# --- AdGuard Home API Einstellungen ---
|
||||
# URL der AdGuard Home Web-Oberfläche (ohne trailing slash)
|
||||
ADGUARD_URL="https://dns1.techniverse.net"
|
||||
|
||||
# AdGuard Home Zugangsdaten (Web-UI Login)
|
||||
ADGUARD_USER="adg-admin"
|
||||
# --- AdGuard Home API ---
|
||||
ADGUARD_URL="https://dns1.domain.com"
|
||||
ADGUARD_USER="admin"
|
||||
ADGUARD_PASS='changeme'
|
||||
|
||||
# --- Rate-Limit Einstellungen ---
|
||||
# Maximale Anfragen pro Domain pro Client innerhalb des Zeitfensters
|
||||
RATE_LIMIT_MAX_REQUESTS=30
|
||||
# --- Rate-Limit ---
|
||||
RATE_LIMIT_MAX_REQUESTS=30 # Max. Anfragen pro Domain/Client im Zeitfenster
|
||||
RATE_LIMIT_WINDOW=60 # Zeitfenster in Sekunden
|
||||
CHECK_INTERVAL=10 # Prüfintervall in Sekunden
|
||||
|
||||
# Zeitfenster in Sekunden (60 = 1 Minute)
|
||||
RATE_LIMIT_WINDOW=60
|
||||
|
||||
# Wie oft das Script die Logs prüft (in Sekunden)
|
||||
CHECK_INTERVAL=10
|
||||
# --- Subdomain-Flood-Erkennung ---
|
||||
SUBDOMAIN_FLOOD_ENABLED=true
|
||||
SUBDOMAIN_FLOOD_MAX_UNIQUE=50 # Max. eindeutige Subdomains pro Basisdomain/Client
|
||||
SUBDOMAIN_FLOOD_WINDOW=60 # Zeitfenster in Sekunden
|
||||
|
||||
# --- Sperr-Einstellungen ---
|
||||
# Wie lange ein Client gesperrt wird (in Sekunden, 3600 = 1 Stunde)
|
||||
BAN_DURATION=3600
|
||||
|
||||
# iptables Chain-Name für die Sperren
|
||||
BAN_DURATION=3600 # Basis-Sperrdauer in Sekunden
|
||||
IPTABLES_CHAIN="ADGUARD_SHIELD"
|
||||
|
||||
# Welche Ports gesperrt werden sollen (DNS, DoT, DoH, DNSv5/QUIC)
|
||||
# Port 53 = DNS (UDP + TCP)
|
||||
# Port 443 = DNS-over-HTTPS (DoH)
|
||||
# Port 853 = DNS-over-TLS (DoT) / DNS-over-QUIC
|
||||
# Port 784 = DNS-over-QUIC (alternativ)
|
||||
# Port 8853 = DNS-over-QUIC (alternativ)
|
||||
BLOCKED_PORTS="53 443 853 784 8853"
|
||||
BLOCKED_PORTS="53 443 853" # DNS(53), DoH(443), DoT/DoQ(853)
|
||||
|
||||
# --- Whitelist ---
|
||||
# IP-Adressen die NIEMALS gesperrt werden (kommagetrennt)
|
||||
# Lokale Netze und wichtige Server hier eintragen
|
||||
# IPs die niemals gesperrt werden (kommagetrennt)
|
||||
WHITELIST="127.0.0.1,::1"
|
||||
|
||||
# --- Logging ---
|
||||
# Log-Datei Pfad
|
||||
LOG_FILE="/var/log/adguard-shield.log"
|
||||
|
||||
# Log-Level: DEBUG, INFO, WARN, ERROR
|
||||
LOG_LEVEL="INFO"
|
||||
|
||||
# Maximale Größe der Log-Datei in MB (danach wird rotiert)
|
||||
LOG_MAX_SIZE_MB=50
|
||||
|
||||
# Ban-History Datei (protokolliert alle Sperren & Entsperrungen dauerhaft)
|
||||
LOG_LEVEL="INFO" # DEBUG, INFO, WARN, ERROR
|
||||
LOG_MAX_SIZE_MB=50 # Max. Größe in MB (danach Rotation)
|
||||
BAN_HISTORY_FILE="/var/log/adguard-shield-bans.log"
|
||||
BAN_HISTORY_RETENTION_DAYS=0 # 0 = unbegrenzt
|
||||
|
||||
# --- Benachrichtigungen (optional) ---
|
||||
# Aktiviert Benachrichtigungen bei Sperren
|
||||
# --- Benachrichtigungen ---
|
||||
NOTIFY_ENABLED=false
|
||||
NOTIFY_TYPE="ntfy" # ntfy, discord, slack, gotify, generic
|
||||
NOTIFY_WEBHOOK_URL="" # Webhook-URL (nicht für ntfy)
|
||||
|
||||
# Webhook-URL für Benachrichtigungen (z.B. Discord, Slack, Gotify)
|
||||
# Discord: https://discord.com/api/webhooks/xxx/yyy
|
||||
# Gotify: https://gotify.example.com/message?token=xxx
|
||||
NOTIFY_WEBHOOK_URL=""
|
||||
|
||||
# Benachrichtigungs-Typ: "discord", "slack", "gotify", "ntfy", "generic"
|
||||
NOTIFY_TYPE="generic"
|
||||
|
||||
# --- Ntfy Einstellungen (nur bei NOTIFY_TYPE="ntfy") ---
|
||||
# Server-URL der Ntfy-Instanz (ohne trailing slash)
|
||||
# Ntfy-Einstellungen (nur bei NOTIFY_TYPE="ntfy")
|
||||
NTFY_SERVER_URL="https://ntfy.sh"
|
||||
|
||||
# Topic-Name für die Benachrichtigungen
|
||||
NTFY_TOPIC=""
|
||||
|
||||
# Optionaler Access-Token (leer lassen wenn nicht benötigt)
|
||||
NTFY_TOKEN=""
|
||||
NTFY_PRIORITY="4" # 1=min, 3=default, 5=max
|
||||
|
||||
# Priorität der Ntfy-Nachrichten (1=min, 3=default, 5=max)
|
||||
NTFY_PRIORITY="1"
|
||||
# --- E-Mail Report ---
|
||||
REPORT_ENABLED=false
|
||||
REPORT_INTERVAL="weekly" # daily, weekly, biweekly, monthly
|
||||
REPORT_TIME="08:00"
|
||||
REPORT_EMAIL_TO="admin@example.com"
|
||||
REPORT_EMAIL_FROM="adguard-shield@example.com"
|
||||
REPORT_FORMAT="html" # html, txt
|
||||
REPORT_MAIL_CMD="msmtp"
|
||||
REPORT_BUSIEST_DAY_RANGE=30 # Tage für "Aktivster Tag" (0 = nur Berichtszeitraum)
|
||||
|
||||
# --- Externe Blocklist (optional) ---
|
||||
# Aktiviert den externen Blocklist-Worker
|
||||
# --- Externe Whitelist ---
|
||||
# Externe Whitelist-Dateien mit Domains/IPs; Domains werden per DNS aufgelöst
|
||||
EXTERNAL_WHITELIST_ENABLED=false
|
||||
EXTERNAL_WHITELIST_URLS="" # URL(s) kommagetrennt
|
||||
EXTERNAL_WHITELIST_INTERVAL=300 # Prüfintervall in Sekunden
|
||||
EXTERNAL_WHITELIST_CACHE_DIR="/var/lib/adguard-shield/external-whitelist"
|
||||
|
||||
# --- Externe Blocklist ---
|
||||
EXTERNAL_BLOCKLIST_ENABLED=false
|
||||
|
||||
# URL(s) zu externen Textdateien mit IP-Adressen (eine IP pro Zeile)
|
||||
# Mehrere URLs kommagetrennt angeben
|
||||
# Beispiel: "https://example.com/blocklist.txt,https://other.com/bad-ips.txt"
|
||||
EXTERNAL_BLOCKLIST_URLS=""
|
||||
|
||||
# Wie oft die externe Blocklist geprüft wird (in Sekunden, 300 = 5 Minuten)
|
||||
EXTERNAL_BLOCKLIST_INTERVAL=300
|
||||
|
||||
# Sperrdauer für externe Blocklist-IPs in Sekunden (0 = permanent bis IP aus Liste entfernt)
|
||||
EXTERNAL_BLOCKLIST_BAN_DURATION=0
|
||||
|
||||
# Automatisch IPs entsperren die aus der externen Liste entfernt wurden?
|
||||
EXTERNAL_BLOCKLIST_URLS="" # URL(s) kommagetrennt
|
||||
EXTERNAL_BLOCKLIST_INTERVAL=300 # Prüfintervall in Sekunden
|
||||
EXTERNAL_BLOCKLIST_BAN_DURATION=0 # 0 = permanent bis IP aus Liste entfernt
|
||||
EXTERNAL_BLOCKLIST_AUTO_UNBAN=true
|
||||
|
||||
# Lokaler Cache-Pfad für die heruntergeladene Blocklist
|
||||
EXTERNAL_BLOCKLIST_NOTIFY=false # Bei großen Listen auf false lassen
|
||||
EXTERNAL_BLOCKLIST_CACHE_DIR="/var/lib/adguard-shield/external-blocklist"
|
||||
|
||||
# --- Progressive Sperren (Recidive) ---
|
||||
# Wiederholungstäter werden stufenweise länger gesperrt
|
||||
PROGRESSIVE_BAN_ENABLED=true
|
||||
PROGRESSIVE_BAN_MULTIPLIER=2 # Multiplikator pro Stufe (2 = Verdopplung)
|
||||
PROGRESSIVE_BAN_MAX_LEVEL=5 # Ab dieser Stufe permanent sperren (0 = nie)
|
||||
PROGRESSIVE_BAN_RESET_AFTER=86400 # Zähler-Reset nach X Sekunden ohne Vergehen
|
||||
|
||||
# --- AbuseIPDB Reporting ---
|
||||
# Meldet nur permanent gesperrte IPs an AbuseIPDB
|
||||
ABUSEIPDB_ENABLED=false
|
||||
ABUSEIPDB_API_KEY=""
|
||||
ABUSEIPDB_CATEGORIES="4" # 4 = DDoS Attack (siehe abuseipdb.com/categories)
|
||||
|
||||
# --- GeoIP-basierte Länderfilter ---
|
||||
# Sperrt/erlaubt DNS-Anfragen nach Herkunftsland (lokale DB, keine Online-API)
|
||||
GEOIP_ENABLED=false
|
||||
GEOIP_MODE="blocklist" # blocklist oder allowlist
|
||||
GEOIP_COUNTRIES="" # ISO 3166-1 Alpha-2 Codes, z.B. "CN,RU,KP,IR"
|
||||
GEOIP_CHECK_INTERVAL=0 # 0 = nutzt CHECK_INTERVAL
|
||||
GEOIP_NOTIFY=true
|
||||
GEOIP_SKIP_PRIVATE=true # Private IPs ausnehmen
|
||||
GEOIP_LICENSE_KEY="" # MaxMind GeoLite2 Key (optional, für Auto-Download)
|
||||
GEOIP_MMDB_PATH="" # Manueller DB-Pfad (optional, hat Vorrang)
|
||||
|
||||
# --- Erweiterte Einstellungen ---
|
||||
# Pfad zur State-Datei (speichert aktive Sperren)
|
||||
STATE_DIR="/var/lib/adguard-shield"
|
||||
|
||||
# Pfad zum PID-File
|
||||
PID_FILE="/var/run/adguard-shield.pid"
|
||||
|
||||
# Anzahl der API-Einträge die pro Abfrage geholt werden (max 5000)
|
||||
API_QUERY_LIMIT=500
|
||||
|
||||
# Dry-Run Modus: true = nur loggen, nicht sperren (zum Testen)
|
||||
DRY_RUN=false
|
||||
API_QUERY_LIMIT=500 # API-Einträge pro Abfrage (max 5000)
|
||||
DRY_RUN=false # true = nur loggen, nicht sperren
|
||||
|
||||
@@ -4,7 +4,7 @@ Documentation=https://git.techniverse.net/scriptos/adguard-shield
|
||||
After=network.target AdGuardHome.service
|
||||
Wants=AdGuardHome.service
|
||||
StartLimitBurst=5
|
||||
StartLimitIntervalSec=60
|
||||
StartLimitIntervalSec=300
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
@@ -14,7 +14,7 @@ ExecReload=/bin/kill -HUP $MAINPID
|
||||
|
||||
# Neustart-Verhalten
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
RestartSec=30
|
||||
|
||||
# Sicherheits-Hardening
|
||||
ProtectSystem=full
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
190
doc/befehle.md
190
doc/befehle.md
@@ -1,190 +0,0 @@
|
||||
# Befehle & Nutzung
|
||||
|
||||
## Installer / Updater
|
||||
|
||||
Der Installer bietet ein interaktives Menü wenn er ohne Argumente aufgerufen wird:
|
||||
|
||||
```bash
|
||||
# Interaktives Menü anzeigen
|
||||
sudo bash install.sh
|
||||
|
||||
# Neuinstallation
|
||||
sudo bash install.sh install
|
||||
|
||||
# Update (mit automatischer Konfigurations-Migration)
|
||||
sudo bash install.sh update
|
||||
|
||||
# Deinstallation
|
||||
sudo bash install.sh uninstall
|
||||
|
||||
# Installationsstatus anzeigen
|
||||
sudo bash install.sh status
|
||||
|
||||
# Hilfe anzeigen
|
||||
sudo bash install.sh --help
|
||||
```
|
||||
|
||||
### Update-Verhalten
|
||||
|
||||
Beim Update passiert automatisch:
|
||||
1. Alle Scripts werden aktualisiert
|
||||
2. Die bestehende Konfiguration wird als `adguard-shield.conf.old` gesichert
|
||||
3. Neue Konfigurationsparameter werden automatisch zur bestehenden Konfig hinzugefügt
|
||||
4. Bestehende Einstellungen bleiben **immer** erhalten
|
||||
5. Der systemd Service wird per `daemon-reload` neu geladen
|
||||
6. Der Service wird automatisch neu gestartet (falls er lief)
|
||||
|
||||
### Voraussetzungen
|
||||
|
||||
Folgende Pakete werden bei der Installation automatisch installiert (via `apt`):
|
||||
- `curl` — API-Kommunikation mit AdGuard Home
|
||||
- `jq` — JSON-Verarbeitung der API-Antworten
|
||||
- `iptables` — Firewall-Regeln für IP-Sperren
|
||||
- `gawk` — Textverarbeitung
|
||||
- `systemd` — Service-Management
|
||||
|
||||
## Monitor (Hauptscript)
|
||||
|
||||
```bash
|
||||
# Starten
|
||||
sudo /opt/adguard-shield/adguard-shield.sh start
|
||||
|
||||
# Stoppen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh stop
|
||||
|
||||
# Status + aktive Sperren anzeigen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh status
|
||||
|
||||
# Ban-History anzeigen (letzte 50 Einträge)
|
||||
sudo /opt/adguard-shield/adguard-shield.sh history
|
||||
|
||||
# Ban-History anzeigen (letzte 100 Einträge)
|
||||
sudo /opt/adguard-shield/adguard-shield.sh history 100
|
||||
|
||||
# Alle Sperren aufheben
|
||||
sudo /opt/adguard-shield/adguard-shield.sh flush
|
||||
|
||||
# Einzelne IP entsperren
|
||||
sudo /opt/adguard-shield/adguard-shield.sh unban 192.168.1.100
|
||||
|
||||
# API-Verbindung testen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh test
|
||||
|
||||
# Dry-Run (nur loggen, nichts sperren)
|
||||
sudo /opt/adguard-shield/adguard-shield.sh dry-run
|
||||
|
||||
# Externe Blocklist - Status anzeigen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh blocklist-status
|
||||
|
||||
# Externe Blocklist - Einmalige Synchronisation
|
||||
sudo /opt/adguard-shield/adguard-shield.sh blocklist-sync
|
||||
|
||||
# Externe Blocklist - Alle Sperren der externen Liste aufheben
|
||||
sudo /opt/adguard-shield/adguard-shield.sh blocklist-flush
|
||||
```
|
||||
|
||||
## iptables Helper
|
||||
|
||||
Für die manuelle Verwaltung der Firewall-Regeln:
|
||||
|
||||
```bash
|
||||
# Chain erstellen
|
||||
sudo /opt/adguard-shield/iptables-helper.sh create
|
||||
|
||||
# Alle Regeln anzeigen
|
||||
sudo /opt/adguard-shield/iptables-helper.sh status
|
||||
|
||||
# IP manuell sperren
|
||||
sudo /opt/adguard-shield/iptables-helper.sh ban 192.168.1.100
|
||||
|
||||
# IP entsperren
|
||||
sudo /opt/adguard-shield/iptables-helper.sh unban 192.168.1.100
|
||||
|
||||
# Alle Regeln leeren
|
||||
sudo /opt/adguard-shield/iptables-helper.sh flush
|
||||
|
||||
# Chain komplett entfernen
|
||||
sudo /opt/adguard-shield/iptables-helper.sh remove
|
||||
|
||||
# Regeln speichern / wiederherstellen
|
||||
sudo /opt/adguard-shield/iptables-helper.sh save
|
||||
sudo /opt/adguard-shield/iptables-helper.sh restore
|
||||
```
|
||||
|
||||
## Externer Blocklist-Worker
|
||||
|
||||
Der Worker kann auch standalone gesteuert werden:
|
||||
|
||||
```bash
|
||||
# Worker manuell starten (normalerweise automatisch per Hauptscript)
|
||||
sudo /opt/adguard-shield/external-blocklist-worker.sh start
|
||||
|
||||
# Worker stoppen
|
||||
sudo /opt/adguard-shield/external-blocklist-worker.sh stop
|
||||
|
||||
# Einmalige Synchronisation (z.B. nach Konfigurationsänderung)
|
||||
sudo /opt/adguard-shield/external-blocklist-worker.sh sync
|
||||
|
||||
# Status anzeigen
|
||||
sudo /opt/adguard-shield/external-blocklist-worker.sh status
|
||||
|
||||
# Alle externen Sperren aufheben
|
||||
sudo /opt/adguard-shield/external-blocklist-worker.sh flush
|
||||
```
|
||||
|
||||
## systemd Service
|
||||
|
||||
Der Service wird bei der Installation automatisch für den **Autostart beim Booten** aktiviert.
|
||||
|
||||
```bash
|
||||
# Start / Stop / Restart
|
||||
sudo systemctl start adguard-shield
|
||||
sudo systemctl stop adguard-shield
|
||||
sudo systemctl restart adguard-shield
|
||||
|
||||
# Status
|
||||
sudo systemctl status adguard-shield
|
||||
|
||||
# Autostart aktivieren / deaktivieren
|
||||
sudo systemctl enable adguard-shield
|
||||
sudo systemctl disable adguard-shield
|
||||
```
|
||||
|
||||
> **Hinweis:** Nach einem Update wird der Service automatisch neu gestartet. Ein manueller Neustart ist nicht nötig.
|
||||
|
||||
## Logs
|
||||
|
||||
```bash
|
||||
# systemd Journal
|
||||
sudo journalctl -u adguard-shield -f
|
||||
|
||||
# Log-Datei direkt
|
||||
sudo tail -f /var/log/adguard-shield.log
|
||||
|
||||
# Nur Sperr-Einträge
|
||||
sudo grep "SPERRE" /var/log/adguard-shield.log
|
||||
|
||||
# Nur Entsperr-Einträge
|
||||
sudo grep "ENTSPERRE" /var/log/adguard-shield.log
|
||||
```
|
||||
|
||||
## Cron-basiertes Entsperren
|
||||
|
||||
Als Alternative oder Ergänzung zum Haupt-Monitor:
|
||||
|
||||
```bash
|
||||
# Crontab bearbeiten
|
||||
sudo crontab -e
|
||||
|
||||
# Alle 5 Minuten abgelaufene Sperren prüfen
|
||||
*/5 * * * * /opt/adguard-shield/unban-expired.sh
|
||||
```
|
||||
|
||||
## Hilfe
|
||||
|
||||
Alle verfügbaren Befehle und Optionen des Installers anzeigen:
|
||||
|
||||
```bash
|
||||
sudo bash install.sh --help
|
||||
sudo bash install.sh -h
|
||||
```
|
||||
@@ -1,110 +0,0 @@
|
||||
# Webhook-Benachrichtigungen
|
||||
|
||||
Das Tool kann bei Sperren und Entsperrungen Benachrichtigungen an verschiedene Dienste senden.
|
||||
|
||||
## Aktivierung
|
||||
|
||||
In der Konfiguration (`adguard-shield.conf`):
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="<typ>"
|
||||
NOTIFY_WEBHOOK_URL="<url>"
|
||||
```
|
||||
|
||||
## Ntfy
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="ntfy"
|
||||
NTFY_SERVER_URL="https://ntfy.sh"
|
||||
NTFY_TOPIC="adguard-shield"
|
||||
NTFY_TOKEN=""
|
||||
NTFY_PRIORITY="4"
|
||||
```
|
||||
|
||||
> **Hinweis:** Bei Ntfy wird `NOTIFY_WEBHOOK_URL` nicht benötigt – Server-URL und Topic werden separat konfiguriert.
|
||||
|
||||
**Eigene Ntfy-Instanz:**
|
||||
```bash
|
||||
NTFY_SERVER_URL="https://ntfy.mein-server.de"
|
||||
NTFY_TOPIC="dns-security"
|
||||
NTFY_TOKEN="tk_mein_geheimer_token"
|
||||
```
|
||||
|
||||
**Prioritäten:**
|
||||
| Wert | Bedeutung |
|
||||
|------|-----------|
|
||||
| 1 | Minimum |
|
||||
| 2 | Niedrig |
|
||||
| 3 | Standard |
|
||||
| 4 | Hoch |
|
||||
| 5 | Maximum |
|
||||
|
||||
**Token erstellen (Self-hosted):**
|
||||
1. Ntfy Web-UI → Benutzer/Tokens
|
||||
2. Token kopieren und in `NTFY_TOKEN` eintragen
|
||||
3. Bei ntfy.sh: Account erstellen → Access Token generieren
|
||||
|
||||
## Discord
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="discord"
|
||||
NOTIFY_WEBHOOK_URL="https://discord.com/api/webhooks/xxx/yyy"
|
||||
```
|
||||
|
||||
**Webhook erstellen:**
|
||||
1. Discord Server → Servereinstellungen → Integrationen → Webhooks
|
||||
2. Neuer Webhook → URL kopieren
|
||||
|
||||
## Gotify
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="gotify"
|
||||
NOTIFY_WEBHOOK_URL="https://gotify.example.com/message?token=xxx"
|
||||
```
|
||||
|
||||
**Token erstellen:**
|
||||
1. Gotify Web-UI → Apps → App erstellen
|
||||
2. Token kopieren und in die URL einfügen
|
||||
|
||||
## Slack
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="slack"
|
||||
NOTIFY_WEBHOOK_URL="https://hooks.slack.com/services/xxx/yyy/zzz"
|
||||
```
|
||||
|
||||
**Webhook erstellen:**
|
||||
1. Slack App → Incoming Webhooks aktivieren
|
||||
2. Webhook-URL kopieren
|
||||
|
||||
## Generic (eigener Endpoint)
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="generic"
|
||||
NOTIFY_WEBHOOK_URL="https://your-server.com/webhook"
|
||||
```
|
||||
|
||||
Sendet einen POST mit JSON-Body:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "🚫 AdGuard Shield: Client 192.168.1.50 gesperrt ...",
|
||||
"action": "ban",
|
||||
"client": "192.168.1.50",
|
||||
"domain": "microsoft.com"
|
||||
}
|
||||
```
|
||||
|
||||
## Beispiel-Nachrichten
|
||||
|
||||
**Sperre:**
|
||||
> 🚫 AdGuard Shield: Client **192.168.1.50** gesperrt (45x microsoft.com in 60s). Sperre für 3600s.
|
||||
|
||||
**Entsperrung:**
|
||||
> ✅ AdGuard Shield: Client **192.168.1.50** wurde entsperrt.
|
||||
@@ -1,142 +0,0 @@
|
||||
# Konfiguration
|
||||
|
||||
Die Konfigurationsdatei liegt nach der Installation unter:
|
||||
|
||||
```
|
||||
/opt/adguard-shield/adguard-shield.conf
|
||||
```
|
||||
|
||||
## Automatische Konfigurations-Migration
|
||||
|
||||
Bei einem **Update** (`sudo bash install.sh update`) wird die Konfiguration automatisch migriert:
|
||||
|
||||
1. Die aktuelle Konfiguration wird als **Backup** gespeichert: `adguard-shield.conf.old`
|
||||
2. Neue Parameter (die in der alten Konfig noch nicht existieren) werden **automatisch** zur bestehenden Konfiguration hinzugefügt
|
||||
3. Alle bestehenden Einstellungen bleiben **unverändert** erhalten
|
||||
|
||||
Dadurch muss der Benutzer bei Updates die Konfiguration nicht manuell austauschen oder vergleichen.
|
||||
|
||||
> **Hinweis:** Nach einem Update empfiehlt es sich, die eventuell neu hinzugefügten Parameter zu prüfen und bei Bedarf anzupassen.
|
||||
|
||||
## Alle Parameter
|
||||
|
||||
### AdGuard Home API
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `ADGUARD_URL` | `http://127.0.0.1:3000` | AdGuard Home Web-UI URL |
|
||||
| `ADGUARD_USER` | `admin` | API Benutzername |
|
||||
| `ADGUARD_PASS` | `changeme` | API Passwort |
|
||||
|
||||
### Rate-Limit
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `RATE_LIMIT_MAX_REQUESTS` | `30` | Max. Anfragen pro Domain/Client innerhalb des Zeitfensters |
|
||||
| `RATE_LIMIT_WINDOW` | `60` | Zeitfenster in Sekunden |
|
||||
| `CHECK_INTERVAL` | `10` | Wie oft die Logs geprüft werden (Sekunden) |
|
||||
| `API_QUERY_LIMIT` | `500` | Anzahl API-Einträge pro Abfrage (max 5000) |
|
||||
|
||||
### Sperr-Einstellungen
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `BAN_DURATION` | `3600` | Sperrdauer in Sekunden (3600 = 1 Stunde) |
|
||||
| `IPTABLES_CHAIN` | `ADGUARD_SHIELD` | Name der iptables Chain |
|
||||
| `BLOCKED_PORTS` | `53 443 853 784 8853` | Ports die gesperrt werden |
|
||||
| `WHITELIST` | `127.0.0.1,::1` | IPs die nie gesperrt werden (kommagetrennt) |
|
||||
|
||||
### Logging
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `LOG_FILE` | `/var/log/adguard-shield.log` | Pfad zur Log-Datei |
|
||||
| `LOG_LEVEL` | `INFO` | Log-Level: `DEBUG`, `INFO`, `WARN`, `ERROR` |
|
||||
| `LOG_MAX_SIZE_MB` | `50` | Max. Log-Größe bevor rotiert wird |
|
||||
| `BAN_HISTORY_FILE` | `/var/log/adguard-shield-bans.log` | Datei für die Ban-History (alle Sperren/Entsperrungen) |
|
||||
|
||||
### Benachrichtigungen
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `NOTIFY_ENABLED` | `false` | Webhook-Benachrichtigungen aktivieren |
|
||||
| `NOTIFY_WEBHOOK_URL` | *(leer)* | Webhook-URL |
|
||||
| `NOTIFY_TYPE` | `generic` | Typ: `discord`, `slack`, `gotify`, `generic` |
|
||||
|
||||
### Erweitert
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `STATE_DIR` | `/var/lib/adguard-shield` | Verzeichnis für State-Dateien |
|
||||
| `PID_FILE` | `/var/run/adguard-shield.pid` | PID-Datei |
|
||||
| `DRY_RUN` | `false` | Testmodus — nur loggen, nicht sperren |
|
||||
### Externe Blocklist
|
||||
|
||||
Ermöglicht das Einbinden externer IP-Blocklisten (z.B. gehostete Textdateien mit einer IP pro Zeile). Der Worker läuft als Hintergrundprozess und prüft periodisch auf Änderungen.
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|}--------------|
|
||||
| `EXTERNAL_BLOCKLIST_ENABLED` | `false` | Aktiviert den externen Blocklist-Worker |
|
||||
| `EXTERNAL_BLOCKLIST_URLS` | *(leer)* | URL(s) zu Textdateien mit IPs (kommagetrennt) |
|
||||
| `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_CACHE_DIR` | `/var/lib/adguard-shield/external-blocklist` | Lokaler Cache für heruntergeladene Listen |
|
||||
|
||||
#### Externe Blocklist einrichten
|
||||
|
||||
1. Erstelle eine Textdatei auf einem Webserver mit einer IP pro Zeile:
|
||||
|
||||
```text
|
||||
# Kommentare werden ignoriert
|
||||
192.168.100.50
|
||||
10.0.0.99
|
||||
2001:db8::dead:beef
|
||||
```
|
||||
|
||||
2. Aktiviere die Blocklist in der Konfiguration:
|
||||
|
||||
```bash
|
||||
EXTERNAL_BLOCKLIST_ENABLED=true
|
||||
EXTERNAL_BLOCKLIST_URLS="https://example.com/blocklist.txt"
|
||||
EXTERNAL_BLOCKLIST_INTERVAL=300
|
||||
```
|
||||
|
||||
3. Mehrere Listen können kommagetrennt angegeben werden:
|
||||
|
||||
```bash
|
||||
EXTERNAL_BLOCKLIST_URLS="https://example.com/list1.txt,https://other.com/list2.txt"
|
||||
```
|
||||
|
||||
4. Service neustarten:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart adguard-shield
|
||||
```
|
||||
## Gesperrte Ports im Detail
|
||||
|
||||
Bei einem Rate-Limit-Verstoß werden **alle** DNS-Protokoll-Ports für den Client gesperrt:
|
||||
|
||||
| Port | Protokoll | Beschreibung |
|
||||
|------|-----------|-------------|
|
||||
| 53 | UDP/TCP | Standard DNS |
|
||||
| 443 | TCP | DNS-over-HTTPS (DoH) |
|
||||
| 853 | TCP | DNS-over-TLS (DoT) |
|
||||
| 853 | UDP | DNS-over-QUIC (DoQ) |
|
||||
| 784 | UDP | DNS-over-QUIC (alternativ) |
|
||||
| 8853 | UDP | DNS-over-QUIC (alternativ) |
|
||||
|
||||
## Whitelist richtig pflegen
|
||||
|
||||
Die Whitelist sollte mindestens enthalten:
|
||||
|
||||
- `127.0.0.1` und `::1` (Localhost)
|
||||
- Die IP deines Routers / Gateways
|
||||
- Deine eigenen Management-IPs
|
||||
- Andere vertrauenswürdige DNS-Clients
|
||||
|
||||
Beispiel:
|
||||
|
||||
```
|
||||
WHITELIST="127.0.0.1,::1,192.168.1.1,192.168.1.10,fd00::1"
|
||||
```
|
||||
@@ -1,127 +0,0 @@
|
||||
# Tipps & Troubleshooting
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **Erst immer im Dry-Run testen**, bevor der scharfe Modus aktiviert wird
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh dry-run
|
||||
```
|
||||
- **Whitelist großzügig pflegen**: Eigene IPs, Router, wichtige Server nicht vergessen
|
||||
- **Sperrdauer anpassen**: Für DDoS-artige Muster ggf. länger sperren
|
||||
- **Logs regelmäßig prüfen**: Falsche Positive erkennen und Whitelist anpassen
|
||||
- **Ban-History nutzen**: `history`-Befehl zeigt alle vergangenen Sperren — hilfreich um Muster zu erkennen
|
||||
- **Log-Level auf DEBUG** setzen wenn etwas nicht funktioniert
|
||||
|
||||
## Häufige Probleme
|
||||
|
||||
### API-Verbindung schlägt fehl
|
||||
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh test
|
||||
```
|
||||
|
||||
**Mögliche Ursachen:**
|
||||
- Falsche URL in `ADGUARD_URL` (Port prüfen!)
|
||||
- Falsche Zugangsdaten (`ADGUARD_USER` / `ADGUARD_PASS`)
|
||||
- AdGuard Home läuft nicht
|
||||
- Firewall blockiert lokale Verbindung
|
||||
|
||||
**Lösung:** URL manuell testen:
|
||||
```bash
|
||||
curl -s -u admin:passwort http://127.0.0.1:3000/control/querylog?limit=1
|
||||
```
|
||||
|
||||
### iptables-Fehler: "Permission denied"
|
||||
|
||||
Das Script muss als **root** laufen, da iptables Root-Rechte benötigt.
|
||||
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh start
|
||||
```
|
||||
|
||||
### Client wird fälschlich gesperrt
|
||||
|
||||
1. Client sofort entsperren:
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh unban 192.168.1.100
|
||||
```
|
||||
2. In der Ban-History prüfen, warum gesperrt wurde:
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh history | grep 192.168.1.100
|
||||
```
|
||||
3. IP zur Whitelist hinzufügen in `adguard-shield.conf`
|
||||
3. Service neustarten:
|
||||
```bash
|
||||
sudo systemctl restart adguard-shield
|
||||
```
|
||||
|
||||
### Sperren überleben Reboot nicht
|
||||
|
||||
Das ist normal — iptables-Regeln sind flüchtig. Der **Service** erstellt die Chain beim Start automatisch neu. Aktive Sperren aus dem State-Verzeichnis werden aber nicht automatisch wiederhergestellt.
|
||||
|
||||
**Optionen:**
|
||||
- `iptables-persistent` installieren (`apt install iptables-persistent`)
|
||||
- Oder den State beim Boot wiederherstellen lassen (Feature-Idee)
|
||||
|
||||
### Zu viele false positives
|
||||
|
||||
- `RATE_LIMIT_MAX_REQUESTS` erhöhen (z.B. 50 oder 100)
|
||||
- `RATE_LIMIT_WINDOW` vergrößern (z.B. 120 Sekunden)
|
||||
- Windows-Clients fragen manche Domains von Natur aus sehr oft an — Whitelist nutzen
|
||||
|
||||
### Monitor startet nicht (PID-File)
|
||||
|
||||
```bash
|
||||
# Altes PID-File entfernen
|
||||
sudo rm -f /var/run/adguard-shield.pid
|
||||
sudo systemctl start adguard-shield
|
||||
```
|
||||
|
||||
## Update durchführen
|
||||
|
||||
```bash
|
||||
# Repository aktualisieren
|
||||
cd /tmp/adguard-shield
|
||||
git pull
|
||||
|
||||
# Update ausführen (Konfig wird automatisch migriert, Service neu gestartet)
|
||||
sudo bash install.sh update
|
||||
```
|
||||
|
||||
**Was passiert beim Update:**
|
||||
- Alle Scripts werden aktualisiert
|
||||
- Konfiguration wird als `adguard-shield.conf.old` gesichert
|
||||
- Neue Konfigurationsparameter werden automatisch zur bestehenden Konfig ergänzt
|
||||
- Bestehende Einstellungen bleiben erhalten
|
||||
- Service wird per `daemon-reload` neu geladen und automatisch neu gestartet
|
||||
|
||||
## Deinstallation
|
||||
|
||||
```bash
|
||||
# Über den Installer (interaktiv mit Menü)
|
||||
sudo bash install.sh uninstall
|
||||
```
|
||||
|
||||
Oder manuell:
|
||||
```bash
|
||||
sudo systemctl stop adguard-shield
|
||||
sudo systemctl disable adguard-shield
|
||||
sudo /opt/adguard-shield/iptables-helper.sh remove
|
||||
sudo rm -rf /opt/adguard-shield
|
||||
sudo rm -f /etc/systemd/system/adguard-shield.service
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
Folgende Pakete werden für den Betrieb benötigt und bei der Installation automatisch installiert:
|
||||
|
||||
| Paket | Zweck |
|
||||
|-------|-------|
|
||||
| `curl` | API-Kommunikation mit AdGuard Home |
|
||||
| `jq` | JSON-Verarbeitung der API-Antworten |
|
||||
| `iptables` | Firewall-Regeln (IPv4 + IPv6) |
|
||||
| `gawk` | Textverarbeitung in Scripts |
|
||||
| `systemd` | Service-Management und Autostart |
|
||||
|
||||
Diese werden bei `sudo bash install.sh install` automatisch geprüft und bei Bedarf über den Paketmanager (`apt`, `dnf`, `yum`, `pacman`) nachinstalliert.
|
||||
15
docs/README.md
Normal file
15
docs/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Dokumentation
|
||||
|
||||
Hier findest du die vollständige Dokumentation zu AdGuard Shield.
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
| Dokument | Beschreibung |
|
||||
|---|---|
|
||||
| [Architektur & Funktionsweise](architektur.md) | Überblick über den Systemaufbau, Datenfluss und die internen Komponenten |
|
||||
| [Befehle & Nutzung](befehle.md) | Alle verfügbaren Befehle des Installers, des Hauptskripts und des Watchdogs |
|
||||
| [Konfiguration](konfiguration.md) | Beschreibung aller Konfigurationsparameter in `adguard-shield.conf` |
|
||||
| [Webhook-Benachrichtigungen](benachrichtigungen.md) | Einrichtung von Push-Benachrichtigungen über Telegram, Discord, Gotify u.a. |
|
||||
| [E-Mail Report](report.md) | Konfiguration des automatischen Statistik-Reports per E-Mail |
|
||||
| [Update-Anleitung](update.md) | Schritt-für-Schritt-Anleitung zum Aktualisieren einer bestehenden Installation |
|
||||
| [Tipps & Troubleshooting](tipps-und-troubleshooting.md) | Best Practices, häufige Probleme und deren Lösungen |
|
||||
@@ -9,14 +9,14 @@
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐ ┌──────────────────────┐
|
||||
│ AdGuard Home │────▶│ Query Log (API) │
|
||||
│ DNS Server │ └──────────┬───────────┘
|
||||
┌─────────────────────┐ ┌──────────────────────┐
|
||||
│ AdGuard Home │────▶ │ Query Log (API) │
|
||||
│ DNS Server │ └──────────┬───────────┘
|
||||
└─────────────────────┘ │
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ adguard-shield.sh │
|
||||
│ (Monitor Script) │
|
||||
│ adguard-shield.sh │
|
||||
│ (Monitor Script) │
|
||||
└──────────┬───────────┘
|
||||
│
|
||||
┌──────────────┼──────────────┐
|
||||
@@ -29,16 +29,32 @@
|
||||
|
||||
## Ablauf einer Sperre
|
||||
|
||||
### Rate-Limit-Sperre
|
||||
|
||||
1. Client `192.168.1.50` fragt `microsoft.com` 45x in 60 Sekunden an
|
||||
2. Monitor fragt die AdGuard Home API alle 10 Sekunden ab (`/control/querylog`)
|
||||
3. Die Anfragen werden pro Client+Domain-Kombination gezählt
|
||||
4. Monitor erkennt: 45 > 30 (Limit überschritten)
|
||||
5. Prüfung: Ist der Client auf der Whitelist? → Nein
|
||||
6. iptables-Regel wird erstellt: `DROP` für `192.168.1.50` auf allen DNS-Ports
|
||||
7. State-Datei wird angelegt: `/var/lib/adguard-shield/192.168.1.50.ban`
|
||||
8. Ban-History Eintrag wird in `/var/log/adguard-shield-bans.log` geschrieben
|
||||
9. Log-Eintrag + optionale Webhook-Benachrichtigung
|
||||
10. Nach 3600 Sekunden (1 Stunde): automatische Entsperrung + History-Eintrag
|
||||
6. **Progressive Sperren:** Offense-Level wird geprüft/erhöht, Sperrdauer berechnet
|
||||
7. iptables-Regel wird erstellt: `DROP` für `192.168.1.50` auf allen DNS-Ports
|
||||
8. State-Datei wird angelegt: `/var/lib/adguard-shield/192.168.1.50.ban`
|
||||
9. Offense-Datei wird aktualisiert: `/var/lib/adguard-shield/192.168.1.50.offenses`
|
||||
10. Ban-History Eintrag wird in `/var/log/adguard-shield-bans.log` geschrieben
|
||||
11. Log-Eintrag + optionale Webhook-Benachrichtigung
|
||||
12. Nach Ablauf der (progressiven) Sperrdauer: automatische Entsperrung + History-Eintrag
|
||||
|
||||
### Subdomain-Flood-Sperre (Random Subdomain Attack)
|
||||
|
||||
1. Client `10.0.0.99` fragt `abc123.microsoft.com`, `xyz456.microsoft.com`, ... ab
|
||||
2. Monitor extrahiert die **Basisdomain** (`microsoft.com`) aus jeder Anfrage
|
||||
3. Pro Client wird gezählt, wie viele **eindeutige Subdomains** einer Basisdomain im Zeitfenster abgefragt wurden
|
||||
4. Monitor erkennt: 63 eindeutige Subdomains > 50 (Schwellwert überschritten)
|
||||
5. Prüfung: Ist der Client auf der Whitelist? → Nein
|
||||
6. Sperre wird ausgeführt mit Domain `*.microsoft.com` und Grund `subdomain-flood`
|
||||
7. Progressive Sperren greifen auch hier — Wiederholungstäter werden stufenweise länger gesperrt
|
||||
|
||||
> **Hinweis:** Die Subdomain-Flood-Erkennung hat ein eigenes Zeitfenster (`SUBDOMAIN_FLOOD_WINDOW`) und einen eigenen Schwellwert (`SUBDOMAIN_FLOOD_MAX_UNIQUE`), unabhängig von den Rate-Limit-Einstellungen.
|
||||
|
||||
## iptables Strategie
|
||||
|
||||
@@ -82,12 +98,32 @@ COUNT=45
|
||||
BAN_TIME=2026-03-03 14:30:00
|
||||
BAN_UNTIL_EPOCH=1741012200
|
||||
BAN_UNTIL=2026-03-03 15:30:00
|
||||
BAN_DURATION=3600
|
||||
OFFENSE_LEVEL=1
|
||||
IS_PERMANENT=false
|
||||
REASON=rate-limit
|
||||
```
|
||||
|
||||
Zusätzlich wird für jede IP ein Offense-Tracker gespeichert:
|
||||
|
||||
```
|
||||
/var/lib/adguard-shield/192.168.1.50.offenses
|
||||
```
|
||||
|
||||
Inhalt:
|
||||
```
|
||||
CLIENT_IP=192.168.1.50
|
||||
OFFENSE_LEVEL=2
|
||||
LAST_OFFENSE_EPOCH=1741008600
|
||||
LAST_OFFENSE=2026-03-03 14:30:00
|
||||
FIRST_OFFENSE=2026-03-03 12:15:00
|
||||
```
|
||||
|
||||
Das ermöglicht:
|
||||
- Persistenz über Script-Neustarts hinweg
|
||||
- Statusabfragen jederzeit möglich
|
||||
- Automatisches Aufräumen per Cron-Job
|
||||
- Progressive Sperrzeiten über mehrere Ban-Zyklen hinweg
|
||||
|
||||
## Dateistruktur nach Installation
|
||||
|
||||
@@ -96,16 +132,26 @@ Das ermöglicht:
|
||||
├── adguard-shield.sh # Haupt-Monitor-Script
|
||||
├── adguard-shield.conf # Konfiguration (chmod 600)
|
||||
├── adguard-shield.conf.old # Backup der Konfig nach Update
|
||||
├── adguard-shield-watchdog.sh # Watchdog Health-Check-Script
|
||||
├── iptables-helper.sh # iptables Verwaltung
|
||||
├── external-blocklist-worker.sh # Externer Blocklist-Worker
|
||||
└── unban-expired.sh # Cron-basiertes Entsperren
|
||||
├── external-whitelist-worker.sh # Externer Whitelist-Worker (DNS-Auflösung)
|
||||
├── geoip-worker.sh # GeoIP-Länderfilter-Worker
|
||||
├── offense-cleanup-worker.sh # Aufräumen abgelaufener Offense-Zähler (nice 19, idle I/O)
|
||||
├── unban-expired.sh # Cron-basiertes Entsperren
|
||||
└── geoip/ # Auto-Download MaxMind GeoLite2 DB (optional)
|
||||
|
||||
/etc/systemd/system/
|
||||
└── adguard-shield.service # systemd Service (Autostart aktiv)
|
||||
├── adguard-shield.service # systemd Service (Autostart aktiv)
|
||||
├── adguard-shield-watchdog.service # systemd Watchdog-Unit (oneshot)
|
||||
└── adguard-shield-watchdog.timer # systemd Timer (alle 5 Min.)
|
||||
|
||||
/var/lib/adguard-shield/
|
||||
├── *.ban # State-Dateien aktiver Sperren
|
||||
└── external-blocklist/ # Cache für externe Blocklisten
|
||||
├── *.offenses # Offense-Zähler (Progressive Sperren)
|
||||
├── external-blocklist/ # Cache für externe Blocklisten
|
||||
├── external-whitelist/ # Cache für externe Whitelisten + aufgelöste IPs
|
||||
└── geoip-cache/ # Cache für GeoIP-Lookups (24h)
|
||||
|
||||
/var/log/
|
||||
├── adguard-shield.log # Laufzeit-Log
|
||||
@@ -118,8 +164,8 @@ Der Installer (`install.sh`) bietet ein interaktives Menü und folgende Funktion
|
||||
|
||||
| Befehl | Beschreibung |
|
||||
|--------|--------------|
|
||||
| `install` | Vollständige Neuinstallation (Abhängigkeiten, Dateien, Konfiguration, Service) |
|
||||
| `update` | Update mit automatischer Konfigurations-Migration und Service-Neustart |
|
||||
| `install` | Vollständige Neuinstallation (Abhängigkeiten, Dateien, Konfiguration, Service, Watchdog) |
|
||||
| `update` | Update mit automatischer Konfigurations-Migration, Watchdog-Aktivierung und Service-Neustart |
|
||||
| `uninstall` | Deinstallation mit optionalem Behalten der Konfiguration |
|
||||
| `status` | Installationsstatus, Version und Service-Status anzeigen |
|
||||
| `--help` | Hilfe und Befehlsübersicht |
|
||||
@@ -129,7 +175,7 @@ Der Installer (`install.sh`) bietet ein interaktives Menü und folgende Funktion
|
||||
```
|
||||
┌─────────────────────────┐ ┌─────────────────────────┐
|
||||
│ Bestehende Konfig │ │ Neue Konfig (Repo) │
|
||||
│ (Benutzer-Settings) │ │ (mit neuen Parametern) │
|
||||
│ (Benutzer-Settings) │ │ (mit neuen Parametern) │
|
||||
└───────────┬─────────────┘ └───────────┬─────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
@@ -143,7 +189,7 @@ Der Installer (`install.sh`) bietet ein interaktives Menü und folgende Funktion
|
||||
▼
|
||||
┌──────────────────────────┐
|
||||
│ Aktualisierte Konfig │
|
||||
│ (alte Werte + neue Keys) │
|
||||
│ (alte Werte + neue Keys) │
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -163,7 +209,9 @@ ZEITSTEMPEL | AKTION | CLIENT-IP | DOMAIN
|
||||
| Grund | Bedeutung |
|
||||
|-------|----------|
|
||||
| `rate-limit` | Automatische Sperre wegen Limit-Überschreitung |
|
||||
| `subdomain-flood` | Sperre wegen zu vieler eindeutiger Subdomains einer Basisdomain |
|
||||
| `dry-run` | Im Dry-Run erkannt (nicht wirklich gesperrt) |
|
||||
| `dry-run (subdomain-flood)` | Subdomain-Flood im Dry-Run erkannt |
|
||||
| `expired` | Automatisch entsperrt nach Ablauf der Sperrdauer |
|
||||
| `expired-cron` | Entsperrt durch den Cron-Job (`unban-expired.sh`) |
|
||||
| `manual` | Manuell entsperrt per `unban`-Befehl |
|
||||
499
docs/befehle.md
Normal file
499
docs/befehle.md
Normal file
@@ -0,0 +1,499 @@
|
||||
# Befehle & Nutzung
|
||||
|
||||
## Installer / Updater
|
||||
|
||||
Der Installer bietet ein interaktives Menü wenn er ohne Argumente aufgerufen wird:
|
||||
|
||||
```bash
|
||||
# Interaktives Menü anzeigen
|
||||
sudo bash install.sh
|
||||
|
||||
# Neuinstallation
|
||||
sudo bash install.sh install
|
||||
|
||||
# Update (mit automatischer Konfigurations-Migration)
|
||||
sudo bash install.sh update
|
||||
|
||||
# Deinstallation (delegiert automatisch an den installierten Uninstaller)
|
||||
sudo bash install.sh uninstall
|
||||
|
||||
# Installationsstatus anzeigen
|
||||
sudo bash install.sh status
|
||||
|
||||
# Hilfe anzeigen
|
||||
sudo bash install.sh --help
|
||||
```
|
||||
|
||||
## Uninstaller (eigenständig)
|
||||
|
||||
Ab Version 0.5.2 wird bei der Installation ein eigenständiger Uninstaller nach `/opt/adguard-shield/uninstall.sh` kopiert. Die Deinstallation kann damit **ohne die originalen Installationsdateien** durchgeführt werden:
|
||||
|
||||
```bash
|
||||
# Direkt aus dem Installationsverzeichnis — kein install.sh benötigt
|
||||
sudo bash /opt/adguard-shield/uninstall.sh
|
||||
```
|
||||
|
||||
Der Uninstaller kennt seinen Speicherort und leitet daraus automatisch das Installationsverzeichnis ab. `install.sh uninstall` delegiert intern ebenfalls dorthin — beide Wege führen zum selben Ergebnis.
|
||||
|
||||
### Update-Verhalten
|
||||
|
||||
Beim Update passiert automatisch:
|
||||
1. Alle Scripts werden aktualisiert
|
||||
2. Die bestehende Konfiguration wird als `adguard-shield.conf.old` gesichert
|
||||
3. Neue Konfigurationsparameter werden automatisch zur bestehenden Konfig hinzugefügt
|
||||
4. Bestehende Einstellungen bleiben **immer** erhalten
|
||||
5. Der systemd Service und Watchdog-Timer werden per `daemon-reload` neu geladen
|
||||
6. Der Watchdog-Timer wird automatisch aktiviert (falls noch nicht aktiv)
|
||||
7. Der Service wird automatisch neu gestartet (falls er lief)
|
||||
|
||||
### API-Verbindungstest nach Installation
|
||||
|
||||
Nach der Installation wird automatisch ein **zweistufiger Verbindungstest** durchgeführt:
|
||||
|
||||
1. **Base-URL Erreichbarkeit** — Prüft ob die konfigurierte `ADGUARD_URL` erreichbar ist (DNS, TCP, HTTP). Bei Fehlern werden spezifische Hinweise angezeigt (z.B. DNS-Fehler, Timeout, SSL-Problem).
|
||||
2. **API-Authentifizierung** — Testet ob die hinterlegten Zugangsdaten (`ADGUARD_USER` / `ADGUARD_PASS`) korrekt sind, indem der API-Endpunkt `/control/querylog` abgefragt wird.
|
||||
|
||||
> **Hinweis:** Dieser Test kann auch jederzeit manuell ausgeführt werden:
|
||||
> ```bash
|
||||
> sudo /opt/adguard-shield/adguard-shield.sh test
|
||||
> ```
|
||||
|
||||
### Voraussetzungen
|
||||
|
||||
Folgende Pakete werden bei der Installation automatisch installiert (via `apt`):
|
||||
- `curl` — API-Kommunikation mit AdGuard Home
|
||||
- `jq` — JSON-Verarbeitung der API-Antworten
|
||||
- `iptables` — Firewall-Regeln für IP-Sperren
|
||||
- `gawk` — Textverarbeitung
|
||||
- `systemd` — Service-Management
|
||||
|
||||
## systemd Service
|
||||
|
||||
AdGuard Shield wird als systemd Service betrieben. **Zum Starten, Stoppen und Neustarten immer `systemctl` verwenden:**
|
||||
|
||||
```bash
|
||||
# Start / Stop / Restart
|
||||
sudo systemctl start adguard-shield
|
||||
sudo systemctl stop adguard-shield
|
||||
sudo systemctl restart adguard-shield
|
||||
|
||||
# Status
|
||||
sudo systemctl status adguard-shield
|
||||
|
||||
# Autostart aktivieren / deaktivieren
|
||||
sudo systemctl enable adguard-shield
|
||||
sudo systemctl disable adguard-shield
|
||||
```
|
||||
|
||||
> **Hinweis:** Der Service wird bei der Installation automatisch für den Autostart beim Booten aktiviert. Nach einem Update wird der Service automatisch neu gestartet — ein manueller Neustart ist nicht nötig.
|
||||
|
||||
## Watchdog (automatischer Health Check)
|
||||
|
||||
Der Watchdog prüft alle 5 Minuten ob der Hauptservice läuft und startet ihn bei Bedarf automatisch neu. Er wird als systemd Timer betrieben und bei der Installation automatisch aktiviert.
|
||||
|
||||
```bash
|
||||
# Watchdog-Status
|
||||
sudo systemctl status adguard-shield-watchdog.timer
|
||||
|
||||
# Nächste geplante Ausführung anzeigen
|
||||
sudo systemctl list-timers adguard-shield-watchdog.timer
|
||||
|
||||
# Watchdog aktivieren / deaktivieren
|
||||
sudo systemctl enable adguard-shield-watchdog.timer
|
||||
sudo systemctl disable adguard-shield-watchdog.timer
|
||||
|
||||
# Watchdog starten / stoppen
|
||||
sudo systemctl start adguard-shield-watchdog.timer
|
||||
sudo systemctl stop adguard-shield-watchdog.timer
|
||||
|
||||
# Watchdog-Logs anzeigen
|
||||
sudo journalctl -u adguard-shield-watchdog.service --no-pager -n 20
|
||||
```
|
||||
|
||||
> **Hinweis:** Der Watchdog sendet automatisch Benachrichtigungen (falls `NOTIFY_ENABLED=true`), wenn er den Service wiederbeleben muss oder die Recovery fehlschlägt.
|
||||
|
||||
## Monitor — Verwaltungsbefehle
|
||||
|
||||
Die folgenden Befehle dienen der **Verwaltung und Diagnose** und können jederzeit ausgeführt werden, auch während der Service läuft:
|
||||
|
||||
```bash
|
||||
# Status + aktive Sperren anzeigen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh status
|
||||
|
||||
# Ban-History anzeigen (letzte 50 Einträge)
|
||||
sudo /opt/adguard-shield/adguard-shield.sh history
|
||||
|
||||
# Ban-History anzeigen (letzte 100 Einträge)
|
||||
sudo /opt/adguard-shield/adguard-shield.sh history 100
|
||||
|
||||
# Alle Sperren aufheben
|
||||
sudo /opt/adguard-shield/adguard-shield.sh flush
|
||||
|
||||
# Einzelne IP entsperren
|
||||
sudo /opt/adguard-shield/adguard-shield.sh unban 192.168.1.100
|
||||
|
||||
# API-Verbindung testen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh test
|
||||
|
||||
# Dry-Run (nur loggen, nichts sperren — läuft im Vordergrund!)
|
||||
sudo /opt/adguard-shield/adguard-shield.sh dry-run
|
||||
|
||||
# Offense-Zähler für alle IPs zurücksetzen (Progressive Sperren)
|
||||
sudo /opt/adguard-shield/adguard-shield.sh reset-offenses
|
||||
|
||||
# Offense-Zähler für eine bestimmte IP zurücksetzen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh reset-offenses 192.168.1.100
|
||||
|
||||
# Externe Blocklist - Status anzeigen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh blocklist-status
|
||||
|
||||
# Externe Blocklist - Einmalige Synchronisation
|
||||
sudo /opt/adguard-shield/adguard-shield.sh blocklist-sync
|
||||
|
||||
# Externe Blocklist - Alle Sperren der externen Liste aufheben
|
||||
sudo /opt/adguard-shield/adguard-shield.sh blocklist-flush
|
||||
```
|
||||
|
||||
> **⚠ Wichtig:** Zum Starten und Stoppen des Monitors **nicht** `adguard-shield.sh start` bzw. `stop` verwenden! Diese Befehle starten den Prozess im **Vordergrund** — die Ausgabe wird live angezeigt und `Strg+C` beendet den gesamten Prozess. Stattdessen immer `sudo systemctl start/stop/restart adguard-shield` nutzen.
|
||||
|
||||
## iptables Helper
|
||||
|
||||
Für die manuelle Verwaltung der Firewall-Regeln:
|
||||
|
||||
```bash
|
||||
# Chain erstellen
|
||||
sudo /opt/adguard-shield/iptables-helper.sh create
|
||||
|
||||
# Alle Regeln anzeigen
|
||||
sudo /opt/adguard-shield/iptables-helper.sh status
|
||||
|
||||
# IP manuell sperren
|
||||
sudo /opt/adguard-shield/iptables-helper.sh ban 192.168.1.100
|
||||
|
||||
# IP entsperren
|
||||
sudo /opt/adguard-shield/iptables-helper.sh unban 192.168.1.100
|
||||
|
||||
# Alle Regeln leeren
|
||||
sudo /opt/adguard-shield/iptables-helper.sh flush
|
||||
|
||||
# Chain komplett entfernen
|
||||
sudo /opt/adguard-shield/iptables-helper.sh remove
|
||||
|
||||
# Regeln speichern / wiederherstellen
|
||||
sudo /opt/adguard-shield/iptables-helper.sh save
|
||||
sudo /opt/adguard-shield/iptables-helper.sh restore
|
||||
```
|
||||
|
||||
## Externer Whitelist-Worker
|
||||
|
||||
Der Whitelist-Worker löst Domains aus externen Listen regelmäßig per DNS auf und stellt die IPs als dynamische Whitelist bereit:
|
||||
|
||||
```bash
|
||||
# Status anzeigen (aufgelöste IPs, konfigurierte Listen)
|
||||
sudo /opt/adguard-shield/adguard-shield.sh whitelist-status
|
||||
|
||||
# Einmalige Synchronisation (z.B. nach Konfigurationsänderung)
|
||||
sudo /opt/adguard-shield/adguard-shield.sh whitelist-sync
|
||||
|
||||
# Alle aufgelösten Whitelist-IPs entfernen
|
||||
sudo /opt/adguard-shield/adguard-shield.sh whitelist-flush
|
||||
```
|
||||
|
||||
Der Worker kann auch standalone gesteuert werden:
|
||||
|
||||
```bash
|
||||
# Worker manuell starten (normalerweise automatisch per Hauptscript)
|
||||
sudo /opt/adguard-shield/external-whitelist-worker.sh start
|
||||
|
||||
# Worker stoppen
|
||||
sudo /opt/adguard-shield/external-whitelist-worker.sh stop
|
||||
|
||||
# Einmalige Synchronisation
|
||||
sudo /opt/adguard-shield/external-whitelist-worker.sh sync
|
||||
|
||||
# Status anzeigen
|
||||
sudo /opt/adguard-shield/external-whitelist-worker.sh status
|
||||
|
||||
# Aufgelöste IPs entfernen
|
||||
sudo /opt/adguard-shield/external-whitelist-worker.sh flush
|
||||
```
|
||||
|
||||
## Externer Blocklist-Worker
|
||||
|
||||
Der Worker kann auch standalone gesteuert werden:
|
||||
|
||||
```bash
|
||||
# Worker manuell starten (normalerweise automatisch per Hauptscript)
|
||||
sudo /opt/adguard-shield/external-blocklist-worker.sh start
|
||||
|
||||
# Worker stoppen
|
||||
sudo /opt/adguard-shield/external-blocklist-worker.sh stop
|
||||
|
||||
# Einmalige Synchronisation (z.B. nach Konfigurationsänderung)
|
||||
sudo /opt/adguard-shield/external-blocklist-worker.sh sync
|
||||
|
||||
# Status anzeigen
|
||||
sudo /opt/adguard-shield/external-blocklist-worker.sh status
|
||||
|
||||
# Alle externen Sperren aufheben
|
||||
sudo /opt/adguard-shield/external-blocklist-worker.sh flush
|
||||
```
|
||||
|
||||
## GeoIP-Worker (Länderfilter)
|
||||
|
||||
Der GeoIP-Worker prüft Client-IPs auf ihr Herkunftsland und sperrt/erlaubt sie basierend auf der Konfiguration:
|
||||
|
||||
```bash
|
||||
# GeoIP-Status anzeigen (Modus, Länder, aktive Sperren, verfügbare Tools)
|
||||
sudo /opt/adguard-shield/adguard-shield.sh geoip-status
|
||||
|
||||
# Einmalige GeoIP-Prüfung aller aktiven Clients
|
||||
sudo /opt/adguard-shield/adguard-shield.sh geoip-sync
|
||||
|
||||
# Alle GeoIP-Sperren aufheben
|
||||
sudo /opt/adguard-shield/adguard-shield.sh geoip-flush
|
||||
|
||||
# GeoIP-Lookup für eine einzelne IP
|
||||
sudo /opt/adguard-shield/adguard-shield.sh geoip-lookup 8.8.8.8
|
||||
```
|
||||
|
||||
Der Worker kann auch standalone gesteuert werden:
|
||||
|
||||
```bash
|
||||
# Worker manuell starten (normalerweise automatisch per Hauptscript)
|
||||
sudo /opt/adguard-shield/geoip-worker.sh start
|
||||
|
||||
# Worker stoppen
|
||||
sudo /opt/adguard-shield/geoip-worker.sh stop
|
||||
|
||||
# Einmalige Synchronisation
|
||||
sudo /opt/adguard-shield/geoip-worker.sh sync
|
||||
|
||||
# Status anzeigen
|
||||
sudo /opt/adguard-shield/geoip-worker.sh status
|
||||
|
||||
# IP nachschlagen
|
||||
sudo /opt/adguard-shield/geoip-worker.sh lookup 1.2.3.4
|
||||
|
||||
# Alle GeoIP-Sperren aufheben
|
||||
sudo /opt/adguard-shield/geoip-worker.sh flush
|
||||
|
||||
# GeoIP-Lookup-Cache leeren
|
||||
sudo /opt/adguard-shield/geoip-worker.sh flush-cache
|
||||
```
|
||||
|
||||
## Offense-Cleanup-Worker
|
||||
|
||||
Der Offense-Cleanup-Worker räumt abgelaufene Offense-Zähler (progressive Sperren) automatisch auf. Er startet automatisch mit dem Hauptservice, wenn progressive Sperren aktiviert sind, und prüft stündlich ob Zähler aufgeräumt werden können. Der Worker läuft mit niedrigster CPU- und I/O-Priorität (`nice 19`, `ionice idle`), um den DNS-Dienst nicht zu beeinträchtigen.
|
||||
|
||||
Der Worker kann auch standalone gesteuert werden:
|
||||
|
||||
```bash
|
||||
# Worker manuell starten (normalerweise automatisch per Hauptscript)
|
||||
sudo /opt/adguard-shield/offense-cleanup-worker.sh start
|
||||
|
||||
# Worker stoppen
|
||||
sudo /opt/adguard-shield/offense-cleanup-worker.sh stop
|
||||
|
||||
# Einmaliger Cleanup-Durchlauf
|
||||
sudo /opt/adguard-shield/offense-cleanup-worker.sh run-once
|
||||
|
||||
# Status anzeigen (aktive/abgelaufene Zähler)
|
||||
sudo /opt/adguard-shield/offense-cleanup-worker.sh status
|
||||
```
|
||||
|
||||
## E-Mail Report
|
||||
|
||||
```bash
|
||||
# Report sofort generieren und per E-Mail versenden
|
||||
sudo /opt/adguard-shield/report-generator.sh send
|
||||
|
||||
# Test-E-Mail senden (prüft alle Voraussetzungen + Mailversand)
|
||||
sudo /opt/adguard-shield/report-generator.sh test
|
||||
|
||||
# Report als Datei generieren (Ausgabe auf stdout)
|
||||
sudo /opt/adguard-shield/report-generator.sh generate
|
||||
|
||||
# Report im HTML-Format in Datei speichern
|
||||
sudo /opt/adguard-shield/report-generator.sh generate html > report.html
|
||||
|
||||
# Report im TXT-Format in Datei speichern
|
||||
sudo /opt/adguard-shield/report-generator.sh generate txt > report.txt
|
||||
|
||||
# Cron-Job für automatischen Versand einrichten
|
||||
sudo /opt/adguard-shield/report-generator.sh install
|
||||
|
||||
# Cron-Job entfernen
|
||||
sudo /opt/adguard-shield/report-generator.sh remove
|
||||
|
||||
# Report-Konfiguration und Cron-Status anzeigen
|
||||
sudo /opt/adguard-shield/report-generator.sh status
|
||||
```
|
||||
|
||||
> Voraussetzung: Ein funktionierender Mail-Transport (z.B. msmtp). Anleitung: [Linux: Einfach E-Mails versenden mit msmtp](https://www.cleveradmin.de/blog/2024/12/linux-einfach-emails-versenden-mit-msmtp/)
|
||||
|
||||
## Logs
|
||||
|
||||
```bash
|
||||
# systemd Journal
|
||||
sudo journalctl -u adguard-shield -f
|
||||
|
||||
# Log-Datei direkt
|
||||
sudo tail -f /var/log/adguard-shield.log
|
||||
|
||||
# Nur Sperr-Einträge
|
||||
sudo grep "SPERRE" /var/log/adguard-shield.log
|
||||
|
||||
# Nur Entsperr-Einträge
|
||||
sudo grep "ENTSPERRE" /var/log/adguard-shield.log
|
||||
```
|
||||
|
||||
## Cron-basiertes Entsperren
|
||||
|
||||
Als Alternative oder Ergänzung zum Haupt-Monitor:
|
||||
|
||||
```bash
|
||||
# Crontab bearbeiten
|
||||
sudo crontab -e
|
||||
|
||||
# Alle 5 Minuten abgelaufene Sperren prüfen
|
||||
*/5 * * * * /opt/adguard-shield/unban-expired.sh
|
||||
```
|
||||
|
||||
## DNS-Abfragen zum Testen (von einem Linux-Client)
|
||||
|
||||
> **⚠ WARNUNG — Bitte unbedingt lesen:**
|
||||
>
|
||||
> Die folgenden Befehle dienen **ausschließlich zu Testzwecken**, um die eigene AdGuard-Shield-Installation zu überprüfen. Sie simulieren erhöhtes DNS-Aufkommen und können dazu genutzt werden, die Erkennungs- und Sperrmechanismen zu validieren.
|
||||
>
|
||||
> **DNS-Flooding ist illegal!** Das massenhafte Senden von DNS-Anfragen an fremde Server oder Infrastruktur ohne ausdrückliche Genehmigung kann als **Denial-of-Service-Angriff (DoS)** gewertet werden und ist in den meisten Ländern **strafbar**. Die Konsequenzen reichen von Abmahnungen über Strafanzeigen bis hin zu empfindlichen Geld- und Freiheitsstrafen.
|
||||
>
|
||||
> **Diese Befehle dürfen nur gegen den eigenen DNS-Server in einer kontrollierten Testumgebung eingesetzt werden.** Die Nutzung gegen fremde Server ist ausdrücklich untersagt. Jede Verantwortung liegt beim Anwender.
|
||||
|
||||
### Voraussetzungen
|
||||
|
||||
Die folgenden Tools müssen auf dem **Linux-Client** installiert sein (nicht auf dem Server):
|
||||
|
||||
```bash
|
||||
# Für DNS-Abfragen (dig)
|
||||
sudo apt install dnsutils
|
||||
|
||||
# Für DoH-Abfragen (curl)
|
||||
sudo apt install curl
|
||||
|
||||
# Für DoT-Abfragen (knotc)
|
||||
sudo apt install knot-dnsutils
|
||||
|
||||
# Für DoQ-Abfragen
|
||||
# https://github.com/natesales/q — Releases herunterladen oder via Go installieren:
|
||||
go install github.com/natesales/q@latest
|
||||
```
|
||||
|
||||
> **Hinweis:** In den folgenden Befehlen muss die IP-Adresse `203.0.113.50` durch die **eigene DNS-Server-IP** und `microsoft.com` durch die gewünschte **Ziel-Domain** ersetzt werden.
|
||||
|
||||
---
|
||||
|
||||
### Klassisches DNS (Port 53/UDP)
|
||||
|
||||
#### Direkte Abfragen (gleiche Domain, viele Anfragen)
|
||||
|
||||
200 parallele DNS-Anfragen für dieselbe Domain — jede mit einem zufälligen DNS-Cookie, um Caching zu umgehen:
|
||||
|
||||
```bash
|
||||
for i in {1..200}; do \
|
||||
dig @203.0.113.50 microsoft.com +short +cookie=$(openssl rand -hex 8) > /dev/null & \
|
||||
done; wait
|
||||
```
|
||||
|
||||
#### Zufällige Subdomain-Abfragen (NXDOMAIN-Flood)
|
||||
|
||||
200 parallele Anfragen mit zufällig generierten Subdomains — simuliert typisches Verhalten von DNS-basierten Angriffen:
|
||||
|
||||
```bash
|
||||
for i in {1..200}; do \
|
||||
dig @203.0.113.50 $(openssl rand -hex 6).microsoft.com +short > /dev/null & \
|
||||
done; wait
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### DNS over HTTPS (DoH)
|
||||
|
||||
DoH-Anfragen werden über HTTPS (Port 443) gesendet. Die meisten AdGuard-Home-Instanzen bieten DoH unter `/dns-query` an:
|
||||
|
||||
#### Direkte Abfragen via DoH
|
||||
|
||||
```bash
|
||||
for i in {1..200}; do \
|
||||
curl -s -H "accept: application/dns-json" \
|
||||
"https://203.0.113.50/dns-query?name=microsoft.com&type=A" > /dev/null & \
|
||||
done; wait
|
||||
```
|
||||
|
||||
#### Zufällige Subdomain-Abfragen via DoH
|
||||
|
||||
```bash
|
||||
for i in {1..200}; do \
|
||||
curl -s -H "accept: application/dns-json" \
|
||||
"https://203.0.113.50/dns-query?name=$(openssl rand -hex 6).microsoft.com&type=A" > /dev/null & \
|
||||
done; wait
|
||||
```
|
||||
|
||||
> **Hinweis:** Falls der Server ein selbstsigniertes Zertifikat verwendet, muss `-k` (unsicherer Modus) an `curl` angehängt werden.
|
||||
|
||||
---
|
||||
|
||||
### DNS over TLS (DoT)
|
||||
|
||||
DoT verwendet TLS über Port 853. Mit `kdig` (aus dem Paket `knot-dnsutils`):
|
||||
|
||||
#### Direkte Abfragen via DoT
|
||||
|
||||
```bash
|
||||
for i in {1..200}; do \
|
||||
kdig @203.0.113.50 microsoft.com +tls +short > /dev/null & \
|
||||
done; wait
|
||||
```
|
||||
|
||||
#### Zufällige Subdomain-Abfragen via DoT
|
||||
|
||||
```bash
|
||||
for i in {1..200}; do \
|
||||
kdig @203.0.113.50 $(openssl rand -hex 6).microsoft.com +tls +short > /dev/null & \
|
||||
done; wait
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### DNS over QUIC (DoQ)
|
||||
|
||||
DoQ verwendet das QUIC-Protokoll über Port 853/UDP. Mit dem Tool [`q`](https://github.com/natesales/q):
|
||||
|
||||
#### Direkte Abfragen via DoQ
|
||||
|
||||
```bash
|
||||
for i in {1..200}; do \
|
||||
q microsoft.com A @quic://203.0.113.50 --short > /dev/null & \
|
||||
done; wait
|
||||
```
|
||||
|
||||
#### Zufällige Subdomain-Abfragen via DoQ
|
||||
|
||||
```bash
|
||||
for i in {1..200}; do \
|
||||
q $(openssl rand -hex 6).microsoft.com A @quic://203.0.113.50 --short > /dev/null & \
|
||||
done; wait
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **⚠ Abschließender Hinweis:** Alle oben genannten Befehle sind **ausschließlich für das Testen der eigenen Infrastruktur** gedacht. Wer diese Befehle gegen fremde DNS-Server oder Dienste einsetzt, macht sich unter Umständen **strafbar**. Sei verantwortungsvoll — teste nur, was dir gehört.
|
||||
|
||||
## Hilfe
|
||||
|
||||
Alle verfügbaren Befehle und Optionen des Installers anzeigen:
|
||||
|
||||
```bash
|
||||
sudo bash install.sh --help
|
||||
sudo bash install.sh -h
|
||||
```
|
||||
208
docs/benachrichtigungen.md
Normal file
208
docs/benachrichtigungen.md
Normal file
@@ -0,0 +1,208 @@
|
||||
# Webhook-Benachrichtigungen
|
||||
|
||||
Das Tool kann beim Starten und Stoppen des Services sowie bei Sperren und Entsperrungen Benachrichtigungen an verschiedene Dienste senden.
|
||||
|
||||
## Aktivierung
|
||||
|
||||
In der Konfiguration (`adguard-shield.conf`):
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="<typ>"
|
||||
NOTIFY_WEBHOOK_URL="<url>"
|
||||
```
|
||||
|
||||
## Ntfy
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="ntfy"
|
||||
NTFY_SERVER_URL="https://ntfy.sh"
|
||||
NTFY_TOPIC="adguard-shield"
|
||||
NTFY_TOKEN=""
|
||||
NTFY_PRIORITY="4"
|
||||
```
|
||||
|
||||
> **Hinweis:** Bei Ntfy wird `NOTIFY_WEBHOOK_URL` nicht benötigt – Server-URL und Topic werden separat konfiguriert.
|
||||
|
||||
**Eigene Ntfy-Instanz:**
|
||||
```bash
|
||||
NTFY_SERVER_URL="https://ntfy.mein-server.de"
|
||||
NTFY_TOPIC="dns-security"
|
||||
NTFY_TOKEN="tk_mein_geheimer_token"
|
||||
```
|
||||
|
||||
**Prioritäten:**
|
||||
| Wert | Bedeutung |
|
||||
|------|-----------|
|
||||
| 1 | Minimum |
|
||||
| 2 | Niedrig |
|
||||
| 3 | Standard |
|
||||
| 4 | Hoch |
|
||||
| 5 | Maximum |
|
||||
|
||||
**Token erstellen (Self-hosted):**
|
||||
1. Ntfy Web-UI → Benutzer/Tokens
|
||||
2. Token kopieren und in `NTFY_TOKEN` eintragen
|
||||
3. Bei ntfy.sh: Account erstellen → Access Token generieren
|
||||
|
||||
## Discord
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="discord"
|
||||
NOTIFY_WEBHOOK_URL="https://discord.com/api/webhooks/xxx/yyy"
|
||||
```
|
||||
|
||||
**Webhook erstellen:**
|
||||
1. Discord Server → Servereinstellungen → Integrationen → Webhooks
|
||||
2. Neuer Webhook → URL kopieren
|
||||
|
||||
## Gotify
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="gotify"
|
||||
NOTIFY_WEBHOOK_URL="https://gotify.example.com/message?token=xxx"
|
||||
```
|
||||
|
||||
**Token erstellen:**
|
||||
1. Gotify Web-UI → Apps → App erstellen
|
||||
2. Token kopieren und in die URL einfügen
|
||||
|
||||
## Slack
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="slack"
|
||||
NOTIFY_WEBHOOK_URL="https://hooks.slack.com/services/xxx/yyy/zzz"
|
||||
```
|
||||
|
||||
**Webhook erstellen:**
|
||||
1. Slack App → Incoming Webhooks aktivieren
|
||||
2. Webhook-URL kopieren
|
||||
|
||||
## Generic (eigener Endpoint)
|
||||
|
||||
```bash
|
||||
NOTIFY_ENABLED=true
|
||||
NOTIFY_TYPE="generic"
|
||||
NOTIFY_WEBHOOK_URL="https://your-server.com/webhook"
|
||||
```
|
||||
|
||||
Sendet einen POST mit JSON-Body:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "🚫 AdGuard Shield Ban auf dns1\n---\nIP: 192.168.1.50\nHostname: client.local\nGrund: 45x microsoft.com in 60s via DNS, Rate-Limit\nDauer: 1h 0m\n\nWhois: https://www.whois.com/whois/192.168.1.50\nAbuseIPDB: https://www.abuseipdb.com/check/192.168.1.50",
|
||||
"action": "ban",
|
||||
"client": "192.168.1.50",
|
||||
"domain": "microsoft.com"
|
||||
}
|
||||
```
|
||||
|
||||
## 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
|
||||
**Überschrift:** ✅ AdGuard Shield
|
||||
|
||||
> 🟢 AdGuard Shield v0.8.1 wurde auf dns1 gestartet.
|
||||
|
||||
### Service gestoppt
|
||||
**Überschrift:** 🚨 🛡️ AdGuard Shield
|
||||
|
||||
> 🔴 AdGuard Shield v0.8.1 wurde auf dns1 gestoppt.
|
||||
|
||||
### Watchdog — Service wiederhergestellt
|
||||
**Überschrift:** 🔄 AdGuard Shield Watchdog
|
||||
|
||||
> 🔄 AdGuard Shield Watchdog auf dns1
|
||||
> ---
|
||||
> Der Service war ausgefallen und wurde automatisch neu gestartet.
|
||||
> Versuch: 1
|
||||
|
||||
### Watchdog — Recovery fehlgeschlagen
|
||||
**Überschrift:** 🚨 AdGuard Shield Watchdog
|
||||
|
||||
> 🚨 AdGuard Shield Watchdog auf dns1
|
||||
> ---
|
||||
> Der Service konnte NICHT automatisch neu gestartet werden!
|
||||
> Manuelles Eingreifen erforderlich.
|
||||
> Fehlversuche: 1
|
||||
> Letzter Fehler: (systemd Statusausgabe)
|
||||
|
||||
### Sperre (Ban)
|
||||
**Überschrift:** 🚨 🛡️ AdGuard Shield
|
||||
|
||||
> 🚫 AdGuard Shield Ban auf dns1
|
||||
> ---
|
||||
> IP: 95.71.42.116
|
||||
> Hostname: example-host.provider.net
|
||||
> Grund: 153x radioportal.techniverse.net in 60s via DNS, Rate-Limit
|
||||
> Dauer: 1h 0m [Stufe 1/5]
|
||||
>
|
||||
> Whois: https://www.whois.com/whois/95.71.42.116
|
||||
> AbuseIPDB: https://www.abuseipdb.com/check/95.71.42.116
|
||||
|
||||
Bei permanenter Sperre mit aktiviertem AbuseIPDB-Reporting erscheint zusätzlich:
|
||||
|
||||
> 🚫 AdGuard Shield Ban auf dns1
|
||||
> ⚠️ IP wurde an AbuseIPDB gemeldet
|
||||
> ---
|
||||
> IP: 95.71.42.116
|
||||
> Hostname: example-host.provider.net
|
||||
> Grund: 153x radioportal.techniverse.net in 60s via DNS, Rate-Limit
|
||||
> Dauer: PERMANENT [Stufe 5/5]
|
||||
>
|
||||
> Whois: https://www.whois.com/whois/95.71.42.116
|
||||
> AbuseIPDB: https://www.abuseipdb.com/check/95.71.42.116
|
||||
|
||||
### Entsperrung (Unban)
|
||||
**Überschrift:** ✅ AdGuard Shield
|
||||
|
||||
> ✅ AdGuard Shield Freigabe auf dns1
|
||||
> ---
|
||||
> IP: 95.71.42.116
|
||||
> Hostname: example-host.provider.net
|
||||
>
|
||||
> AbuseIPDB: https://www.abuseipdb.com/check/95.71.42.116
|
||||
|
||||
### Externe Blocklist – Sperre
|
||||
**Überschrift:** 🚨 🛡️ AdGuard Shield
|
||||
|
||||
> 🚫 AdGuard Shield Ban auf dns1 (Externe Blocklist)
|
||||
> ---
|
||||
> IP: 203.0.113.50
|
||||
> Hostname: bad-actor.example.com
|
||||
>
|
||||
> Whois: https://www.whois.com/whois/203.0.113.50
|
||||
> AbuseIPDB: https://www.abuseipdb.com/check/203.0.113.50
|
||||
|
||||
### Externe Blocklist – Entsperrung
|
||||
**Überschrift:** ✅ AdGuard Shield
|
||||
|
||||
> ✅ AdGuard Shield Freigabe auf dns1 (Externe Blocklist)
|
||||
> ---
|
||||
> IP: 203.0.113.50
|
||||
> Hostname: bad-actor.example.com
|
||||
>
|
||||
> AbuseIPDB: https://www.abuseipdb.com/check/203.0.113.50
|
||||
|
||||
### Hinweise
|
||||
|
||||
- Der **Hostname** der IP wird automatisch per Reverse-DNS aufgelöst (`dig`, `host` oder `getent`). Ist kein PTR-Record vorhanden, wird `(unbekannt)` angezeigt.
|
||||
- Der **Servername** (`dns1` in den Beispielen) wird dynamisch über `$(hostname)` ermittelt und zeigt, auf welchem Server das Ereignis stattfand.
|
||||
- Die **Überschrift** unterscheidet sich je nach Aktion:
|
||||
- 🚨 🛡️ bei Sperren und Service-Stopp
|
||||
- ✅ bei Freigaben und Service-Start
|
||||
- Bei **permanenten Sperren** mit aktiviertem AbuseIPDB-Reporting wird ein Hinweis eingeblendet, dass die IP an AbuseIPDB gemeldet wurde.
|
||||
486
docs/konfiguration.md
Normal file
486
docs/konfiguration.md
Normal file
@@ -0,0 +1,486 @@
|
||||
# Konfiguration
|
||||
|
||||
Die Konfigurationsdatei liegt nach der Installation unter:
|
||||
|
||||
```
|
||||
/opt/adguard-shield/adguard-shield.conf
|
||||
```
|
||||
|
||||
## Automatische Konfigurations-Migration
|
||||
|
||||
Bei einem **Update** (`sudo bash install.sh update`) wird die Konfiguration automatisch migriert:
|
||||
|
||||
1. Die aktuelle Konfiguration wird als **Backup** gespeichert: `adguard-shield.conf.old`
|
||||
2. Neue Parameter (die in der alten Konfig noch nicht existieren) werden **automatisch** zur bestehenden Konfiguration hinzugefügt
|
||||
3. Alle bestehenden Einstellungen bleiben **unverändert** erhalten
|
||||
|
||||
Dadurch muss der Benutzer bei Updates die Konfiguration nicht manuell austauschen oder vergleichen.
|
||||
|
||||
> **Hinweis:** Nach einem Update empfiehlt es sich, die eventuell neu hinzugefügten Parameter zu prüfen und bei Bedarf anzupassen.
|
||||
|
||||
## Alle Parameter
|
||||
|
||||
### AdGuard Home API
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `ADGUARD_URL` | `http://127.0.0.1:3000` | AdGuard Home Web-UI URL |
|
||||
| `ADGUARD_USER` | `admin` | API Benutzername |
|
||||
| `ADGUARD_PASS` | `changeme` | API Passwort |
|
||||
|
||||
### Rate-Limit
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `RATE_LIMIT_MAX_REQUESTS` | `30` | Max. Anfragen pro Domain/Client innerhalb des Zeitfensters |
|
||||
| `RATE_LIMIT_WINDOW` | `60` | Zeitfenster in Sekunden |
|
||||
| `CHECK_INTERVAL` | `10` | Wie oft die Logs geprüft werden (Sekunden) |
|
||||
| `API_QUERY_LIMIT` | `500` | Anzahl API-Einträge pro Abfrage (max 5000) |
|
||||
|
||||
### Subdomain-Flood-Erkennung (Random Subdomain Attack)
|
||||
|
||||
Erkennt Bot-Angriffe, bei denen massenhaft zufällige Subdomains einer Domain abgefragt werden (z.B. `abc123.microsoft.com`, `xyz456.microsoft.com`, ...). Dabei wird pro Client gezählt, wie viele **eindeutige** Subdomains einer Basisdomain (z.B. `microsoft.com`) im Zeitfenster aufgerufen werden.
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `SUBDOMAIN_FLOOD_ENABLED` | `true` | Subdomain-Flood-Erkennung aktivieren |
|
||||
| `SUBDOMAIN_FLOOD_MAX_UNIQUE` | `50` | Max. eindeutige Subdomains pro Basisdomain/Client im Zeitfenster |
|
||||
| `SUBDOMAIN_FLOOD_WINDOW` | `60` | Zeitfenster in Sekunden |
|
||||
|
||||
#### Wie funktioniert die Erkennung?
|
||||
|
||||
1. Aus jeder DNS-Anfrage wird die **Basisdomain** extrahiert (z.B. `microsoft.com` aus `abc.microsoft.com`)
|
||||
2. Pro Client wird gezählt, wie viele **verschiedene** Subdomains einer Basisdomain im Zeitfenster abgefragt wurden
|
||||
3. Überschreitet die Anzahl eindeutiger Subdomains den Schwellwert, wird der Client gesperrt
|
||||
|
||||
#### Beispiel
|
||||
|
||||
Ein Bot fragt innerhalb von 60 Sekunden folgende Domains ab:
|
||||
|
||||
```
|
||||
hbidcw.microsoft.com
|
||||
ftdzewf.microsoft.com
|
||||
xk9z3a.microsoft.com
|
||||
... (50+ verschiedene Subdomains)
|
||||
```
|
||||
|
||||
→ Alle Anfragen haben die gleiche Basisdomain `microsoft.com`. Sobald mehr als 50 eindeutige Subdomains erkannt werden, wird der Client gesperrt.
|
||||
|
||||
> **Hinweis:** Nur echte Subdomains werden gezählt. Anfragen direkt an `microsoft.com` (ohne Subdomain) lösen diese Erkennung nicht aus. Multi-Part-TLDs wie `.co.uk`, `.com.au` etc. werden korrekt behandelt.
|
||||
|
||||
> **Tipp:** Der Schwellwert `SUBDOMAIN_FLOOD_MAX_UNIQUE` sollte hoch genug sein, um legitime Clients nicht zu stören (z.B. CDNs nutzen oft viele Subdomains). Ein Wert von 50–100 ist in den meisten Fällen sinnvoll.
|
||||
|
||||
### Sperr-Einstellungen
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `BAN_DURATION` | `3600` | Basis-Sperrdauer in Sekunden (3600 = 1 Stunde) |
|
||||
| `IPTABLES_CHAIN` | `ADGUARD_SHIELD` | Name der iptables Chain |
|
||||
| `BLOCKED_PORTS` | `53 443 853` | Ports die gesperrt werden (IPv4 + IPv6) |
|
||||
| `WHITELIST` | `127.0.0.1,::1` | IPs die nie gesperrt werden (kommagetrennt) |
|
||||
|
||||
### Progressive Sperren (Recidive)
|
||||
|
||||
Wiederholungstäter werden wie bei fail2ban stufenweise länger gesperrt. Wird eine IP nach dem Ablauf ihrer Sperre erneut auffällig, steigt die Sperrdauer exponentiell.
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `PROGRESSIVE_BAN_ENABLED` | `true` | Progressive Sperren aktivieren |
|
||||
| `PROGRESSIVE_BAN_MULTIPLIER` | `2` | Multiplikator pro Stufe (2 = Verdopplung) |
|
||||
| `PROGRESSIVE_BAN_MAX_LEVEL` | `5` | Ab dieser Stufe wird permanent gesperrt (0 = nie) |
|
||||
| `PROGRESSIVE_BAN_RESET_AFTER` | `86400` | Zähler-Reset nach X Sekunden ohne Vergehen (86400 = 24h) |
|
||||
|
||||
#### Beispiel bei Standardwerten
|
||||
|
||||
| Vergehen | Stufe | Sperrdauer | Berechnung |
|
||||
|----------|-------|------------|------------|
|
||||
| 1. Mal | 1 | 1 Stunde | 3600 × 1 |
|
||||
| 2. Mal | 2 | 2 Stunden | 3600 × 2 |
|
||||
| 3. Mal | 3 | 4 Stunden | 3600 × 4 |
|
||||
| 4. Mal | 4 | 8 Stunden | 3600 × 8 |
|
||||
| 5. Mal | 5 | **PERMANENT** | Max-Stufe erreicht |
|
||||
|
||||
> **Hinweis:** Abgelaufene Offense-Zähler werden automatisch vom **Offense-Cleanup-Worker** aufgeräumt, der stündlich prüft, ob das letzte Vergehen einer IP länger als `PROGRESSIVE_BAN_RESET_AFTER` zurückliegt. Der Worker startet automatisch zusammen mit dem Hauptservice, wenn progressive Sperren aktiviert sind. Er läuft mit niedrigster CPU- und I/O-Priorität (`nice 19`, `ionice idle`), sodass andere Dienste nicht beeinträchtigt werden. Manuelles Zurücksetzen ist jederzeit mit `reset-offenses` möglich. Permanente Sperren werden **nicht** automatisch aufgehoben – sie müssen manuell mit `unban` oder `flush` entfernt werden.
|
||||
|
||||
### Logging
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `LOG_FILE` | `/var/log/adguard-shield.log` | Pfad zur Log-Datei |
|
||||
| `LOG_LEVEL` | `INFO` | Log-Level: `DEBUG`, `INFO`, `WARN`, `ERROR` |
|
||||
| `LOG_MAX_SIZE_MB` | `50` | Max. Log-Größe bevor rotiert wird |
|
||||
| `BAN_HISTORY_FILE` | `/var/log/adguard-shield-bans.log` | Datei für die Ban-History (alle Sperren/Entsperrungen) |
|
||||
| `BAN_HISTORY_RETENTION_DAYS` | `0` | Aufbewahrungsdauer der Ban-History in Tagen. `0` = unbegrenzt (niemals löschen). Alte Einträge werden beim nächsten Report automatisch entfernt. |
|
||||
|
||||
### Benachrichtigungen
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `NOTIFY_ENABLED` | `false` | Benachrichtigungen aktivieren |
|
||||
| `NOTIFY_TYPE` | `ntfy` | Typ: `ntfy`, `discord`, `slack`, `gotify`, `generic` |
|
||||
| `NOTIFY_WEBHOOK_URL` | *(leer)* | Webhook-URL (nur für discord, slack, gotify, generic) |
|
||||
| `NTFY_SERVER_URL` | `https://ntfy.sh` | Ntfy Server-URL |
|
||||
| `NTFY_TOPIC` | *(leer)* | Ntfy Topic-Name |
|
||||
| `NTFY_TOKEN` | *(leer)* | Optionaler Ntfy Access-Token |
|
||||
| `NTFY_PRIORITY` | `4` | Ntfy Priorität (1–5) |
|
||||
|
||||
### E-Mail Report
|
||||
|
||||
Regelmäßige Statistik-Reports per E-Mail. Voraussetzung ist ein funktionierender Mail-Transport (z.B. msmtp).
|
||||
|
||||
> **Anleitung für msmtp:** [Linux: Einfach E-Mails versenden mit msmtp](https://www.cleveradmin.de/blog/2024/12/linux-einfach-emails-versenden-mit-msmtp/)
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `REPORT_ENABLED` | `false` | Report-Funktion aktivieren |
|
||||
| `REPORT_INTERVAL` | `weekly` | Intervall: `daily`, `weekly`, `biweekly`, `monthly` |
|
||||
| `REPORT_TIME` | `08:00` | Versanduhrzeit (HH:MM, 24h) |
|
||||
| `REPORT_EMAIL_TO` | *(leer)* | E-Mail-Empfänger |
|
||||
| `REPORT_EMAIL_FROM` | `adguard-shield@hostname` | E-Mail-Absender |
|
||||
| `REPORT_FORMAT` | `html` | Format: `html` oder `txt` |
|
||||
| `REPORT_MAIL_CMD` | `msmtp` | Mail-Befehl (`msmtp`, `sendmail`, `mail`) |
|
||||
| `REPORT_BUSIEST_DAY_RANGE` | `30` | Zeitraum in Tagen für „Aktivster Tag“. `30` = letzte 30 Tage. `0` = nur Berichtszeitraum (altes Verhalten) |
|
||||
|
||||
> Siehe [E-Mail Report Dokumentation](report.md) für Details zu Inhalten, Templates und Befehlen.
|
||||
|
||||
### Erweitert
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `STATE_DIR` | `/var/lib/adguard-shield` | Verzeichnis für State-Dateien |
|
||||
| `PID_FILE` | `/var/run/adguard-shield.pid` | PID-Datei |
|
||||
| `DRY_RUN` | `false` | Testmodus — nur loggen, nicht sperren |
|
||||
|
||||
### Externe Whitelist
|
||||
|
||||
Ermöglicht das Einbinden externer Whitelist-Dateien mit Domains und IP-Adressen. Der Worker löst Domains regelmäßig per DNS auf — ideal für DynDNS-Einträge mit wechselnden IP-Adressen. Aufgelöste IPs werden automatisch zur Whitelist hinzugefügt und bei jeder Prüfung aktualisiert.
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `EXTERNAL_WHITELIST_ENABLED` | `false` | Aktiviert den externen Whitelist-Worker |
|
||||
| `EXTERNAL_WHITELIST_URLS` | *(leer)* | URL(s) zu Whitelist-Textdateien (kommagetrennt). Unterstützt IPv4, IPv6, CIDR und Hostnamen |
|
||||
| `EXTERNAL_WHITELIST_INTERVAL` | `300` | Prüfintervall in Sekunden (300 = 5 Min.). Bei DynDNS-Einträgen ggf. kürzer wählen |
|
||||
| `EXTERNAL_WHITELIST_CACHE_DIR` | `/var/lib/adguard-shield/external-whitelist` | Lokaler Cache für heruntergeladene Listen und aufgelöste IPs |
|
||||
|
||||
#### Externe Whitelist einrichten
|
||||
|
||||
1. Erstelle eine Textdatei auf einem Webserver. Pro Zeile ein Eintrag — Domain, IPv4, IPv6 oder CIDR:
|
||||
|
||||
```text
|
||||
# Domains (werden regelmäßig per DNS aufgelöst — ideal für DynDNS)
|
||||
mein-router.dyndns.org
|
||||
homeserver.example.com
|
||||
|
||||
# Feste IPs
|
||||
192.168.1.100
|
||||
10.0.0.0/24
|
||||
2001:db8::1
|
||||
|
||||
# Kommentare und Inline-Kommentare werden unterstützt
|
||||
192.168.1.200 # Backup-Server
|
||||
```
|
||||
|
||||
2. Aktiviere die Whitelist in der Konfiguration:
|
||||
|
||||
```bash
|
||||
EXTERNAL_WHITELIST_ENABLED=true
|
||||
EXTERNAL_WHITELIST_URLS="https://example.com/whitelist.txt"
|
||||
EXTERNAL_WHITELIST_INTERVAL=300
|
||||
```
|
||||
|
||||
3. Mehrere Listen können kommagetrennt angegeben werden:
|
||||
|
||||
```bash
|
||||
EXTERNAL_WHITELIST_URLS="https://example.com/trusted.txt,https://other.com/whitelist.txt"
|
||||
```
|
||||
|
||||
4. Service neustarten:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart adguard-shield
|
||||
```
|
||||
|
||||
> **Hinweis:** Da Domains bei jedem Prüfintervall neu aufgelöst werden, eignet sich diese Funktion besonders für DynDNS-Einträge. Ändert sich die IP eines DynDNS-Hostnamens, wird die neue IP beim nächsten Sync automatisch erkannt und in die Whitelist aufgenommen.
|
||||
|
||||
> **Wichtig:** Wird eine bereits gesperrte IP durch eine Whitelist-Aktualisierung gewhitelistet, wird sie **automatisch entsperrt**.
|
||||
|
||||
### Externe Blocklist
|
||||
|
||||
Ermöglicht das Einbinden externer Blocklisten, die IPv4-Adressen, IPv6-Adressen und Hostnamen enthalten können. Der Worker läuft als Hintergrundprozess, prüft periodisch auf Änderungen und löst Hostnamen automatisch über den lokalen DNS-Resolver auf.
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `EXTERNAL_BLOCKLIST_ENABLED` | `false` | Aktiviert den externen Blocklist-Worker |
|
||||
| `EXTERNAL_BLOCKLIST_URLS` | *(leer)* | URL(s) zu Blocklist-Textdateien (kommagetrennt). Unterstützt IPv4, IPv6, CIDR und Hostnamen |
|
||||
| `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
|
||||
|
||||
Meldet permanent gesperrte IPs automatisch an [AbuseIPDB](https://www.abuseipdb.com/). Damit wird die IP in einer öffentlichen Datenbank als missbräuchlich markiert und andere Administratoren können davon profitieren.
|
||||
|
||||
> **Wichtig:** Es werden **nur permanent gesperrte IPs** gemeldet — also erst wenn die maximale Progressive-Ban-Stufe erreicht ist. Einzelne temporäre Sperren lösen keinen AbuseIPDB-Report aus.
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|---------------|
|
||||
| `ABUSEIPDB_ENABLED` | `false` | AbuseIPDB-Reporting aktivieren |
|
||||
| `ABUSEIPDB_API_KEY` | *(leer)* | API-Key von [abuseipdb.com/account/api](https://www.abuseipdb.com/account/api) |
|
||||
| `ABUSEIPDB_CATEGORIES` | `4` | Report-Kategorien (4 = DDoS Attack). Siehe [Kategorien](https://www.abuseipdb.com/categories) |
|
||||
|
||||
#### AbuseIPDB einrichten
|
||||
|
||||
1. Erstelle einen kostenlosen Account auf [abuseipdb.com](https://www.abuseipdb.com/)
|
||||
2. Erstelle einen API-Key unter [Account → API](https://www.abuseipdb.com/account/api)
|
||||
3. Aktiviere das Reporting in der Konfiguration:
|
||||
|
||||
```bash
|
||||
ABUSEIPDB_ENABLED=true
|
||||
ABUSEIPDB_API_KEY="dein-api-key-hier"
|
||||
ABUSEIPDB_CATEGORIES="4"
|
||||
```
|
||||
|
||||
4. Service neustarten:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart adguard-shield
|
||||
```
|
||||
|
||||
#### Was wird gemeldet?
|
||||
|
||||
Der Report an AbuseIPDB enthält (auf Englisch):
|
||||
|
||||
- **Bei Rate-Limit:** `DNS flooding on our DNS server: 100x microsoft.com in 60s. Banned by Adguard Shield 🔗 https://tnvs.de/as`
|
||||
- **Bei Subdomain-Flood:** `DNS flooding on our DNS server: 85x *.microsoft.com in 60s (random subdomain attack). Banned by Adguard Shield 🔗 https://tnvs.de/as`
|
||||
|
||||
Die Kategorie `4` (DDoS Attack) wird standardmäßig verwendet. Weitere Kategorien können kommagetrennt angegeben werden (z.B. `"4,15"`).
|
||||
|
||||
### GeoIP-basierte Länderfilter
|
||||
|
||||
Ermöglicht das Sperren oder Erlauben von DNS-Anfragen basierend auf dem Herkunftsland der Client-IP. Unterstützt zwei Modi:
|
||||
|
||||
- **Blocklist-Modus:** Nur die gelisteten Länder werden gesperrt (alle anderen erlaubt)
|
||||
- **Allowlist-Modus:** Nur die gelisteten Länder werden erlaubt (alle anderen gesperrt)
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `GEOIP_ENABLED` | `false` | GeoIP-Filter aktivieren |
|
||||
| `GEOIP_MODE` | `blocklist` | Modus: `blocklist` oder `allowlist` |
|
||||
| `GEOIP_COUNTRIES` | *(leer)* | ISO 3166-1 Alpha-2 Ländercodes (kommagetrennt), z.B. `CN,RU,KP,IR` |
|
||||
| `GEOIP_CHECK_INTERVAL` | `0` | Prüfintervall in Sekunden (`0` = nutzt `CHECK_INTERVAL`) |
|
||||
| `GEOIP_NOTIFY` | `true` | Benachrichtigungen bei GeoIP-Sperren senden |
|
||||
| `GEOIP_SKIP_PRIVATE` | `true` | Private/lokale IPs von der GeoIP-Prüfung ausnehmen |
|
||||
| `GEOIP_LICENSE_KEY` | *(leer)* | MaxMind License-Key für automatischen DB-Download (kostenlos) |
|
||||
| `GEOIP_MMDB_PATH` | *(leer)* | Manueller Pfad zur MaxMind GeoLite2 Datenbank (überschreibt Auto-Download) |
|
||||
|
||||
#### Voraussetzungen
|
||||
|
||||
Es muss mindestens eines der folgenden GeoIP-Tools installiert sein:
|
||||
|
||||
1. **Automatischer MaxMind-Download** (empfohlen):
|
||||
```bash
|
||||
# Kostenlosen Account erstellen: https://www.maxmind.com/en/geolite2/signup
|
||||
# License-Key generieren und in adguard-shield.conf eintragen:
|
||||
GEOIP_LICENSE_KEY="dein_license_key_hier"
|
||||
```
|
||||
Die GeoLite2-Country-Datenbank wird automatisch heruntergeladen und alle 24 Stunden aktualisiert.
|
||||
Es wird zusätzlich `mmdbinspect` oder `mmdblookup` benötigt:
|
||||
```bash
|
||||
sudo apt install mmdb-bin # für mmdblookup
|
||||
```
|
||||
|
||||
2. **geoiplookup** (einfachster Einstieg, weniger genau):
|
||||
```bash
|
||||
sudo apt install geoip-bin geoip-database
|
||||
```
|
||||
|
||||
3. **Manueller MaxMind-Pfad** (eigene Datenbank):
|
||||
```bash
|
||||
# mmdbinspect oder mmdblookup installieren
|
||||
# Datenbank manuell herunterladen: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
|
||||
GEOIP_MMDB_PATH="/usr/share/GeoIP/GeoLite2-Country.mmdb"
|
||||
```
|
||||
|
||||
> **Priorität:** `GEOIP_MMDB_PATH` (manuell) → Auto-Download-DB → `geoiplookup` (Legacy-Fallback)
|
||||
|
||||
#### Beispiel: Bestimmte Länder sperren (Blocklist)
|
||||
|
||||
```bash
|
||||
GEOIP_ENABLED=true
|
||||
GEOIP_MODE="blocklist"
|
||||
GEOIP_COUNTRIES="CN,RU,KP,IR"
|
||||
GEOIP_LICENSE_KEY="dein_maxmind_license_key" # optional, für Auto-Download
|
||||
```
|
||||
|
||||
→ Alle Anfragen aus China, Russland, Nordkorea und Iran werden permanent gesperrt.
|
||||
|
||||
#### Beispiel: Nur bestimmte Länder erlauben (Allowlist)
|
||||
|
||||
```bash
|
||||
GEOIP_ENABLED=true
|
||||
GEOIP_MODE="allowlist"
|
||||
GEOIP_COUNTRIES="DE,AT,CH"
|
||||
```
|
||||
|
||||
→ Nur Anfragen aus Deutschland, Österreich und der Schweiz werden erlaubt. Alle anderen Länder werden gesperrt.
|
||||
|
||||
> **Hinweis:** Private IP-Adressen (10.x.x.x, 192.168.x.x, etc.) und Whitelist-IPs werden niemals durch GeoIP gesperrt. GeoIP-Sperren sind **immer permanent**.
|
||||
|
||||
> **Auto-Unban:** Wird ein Land aus `GEOIP_COUNTRIES` entfernt oder der Modus (`GEOIP_MODE`) geändert, werden die nicht mehr zutreffenden Sperren beim nächsten Sync **automatisch aufgehoben**. Dasselbe gilt, wenn GeoIP komplett deaktiviert wird (`GEOIP_ENABLED=false`).
|
||||
|
||||
> **Tipp:** GeoIP-Lookups werden für 24 Stunden gecacht. Mit `geoip-flush-cache` kann der Cache manuell geleert werden.
|
||||
|
||||
> **Auto-Download:** Ist `GEOIP_LICENSE_KEY` gesetzt, wird die GeoLite2-Country-Datenbank automatisch nach `<INSTALL_DIR>/geoip/` heruntergeladen und alle 24 Stunden aktualisiert. Bei einem Update wird der Download im Hintergrund durchgeführt — der Worker läuft während des Downloads normal weiter. Ein manuell gesetzter `GEOIP_MMDB_PATH` hat immer Vorrang vor der automatisch heruntergeladenen Datenbank.
|
||||
|
||||
#### GeoIP-Befehle
|
||||
|
||||
| Befehl | Beschreibung |
|
||||
|--------|--------------|
|
||||
| `adguard-shield.sh geoip-status` | Zeigt GeoIP-Status, aktive Sperren und verfügbare Tools |
|
||||
| `adguard-shield.sh geoip-sync` | Einmalige GeoIP-Prüfung aller aktiven Clients |
|
||||
| `adguard-shield.sh geoip-flush` | Alle GeoIP-Sperren aufheben |
|
||||
| `adguard-shield.sh geoip-lookup <IP>` | GeoIP-Lookup einer einzelnen IP-Adresse |
|
||||
|
||||
#### Externe Blocklist einrichten
|
||||
|
||||
1. Erstelle eine Textdatei auf einem Webserver. Pro Zeile ein Eintrag — IPv4, IPv6, CIDR oder Hostname:
|
||||
|
||||
```text
|
||||
# Kommentare werden ignoriert
|
||||
# Inline-Kommentare ebenfalls: 1.2.3.4 # dieser Kommentar wird entfernt
|
||||
|
||||
# IPv4
|
||||
192.168.100.50
|
||||
10.0.0.0/8
|
||||
|
||||
# IPv6
|
||||
2001:db8::dead:beef
|
||||
2001:db8::/32
|
||||
|
||||
# Hostnamen (werden über den lokalen DNS-Resolver aufgelöst)
|
||||
# Liefert ein Hostname mehrere IPs, werden alle gesperrt
|
||||
bad-actor.example.com
|
||||
malware.example.net
|
||||
|
||||
# Hosts-Datei-Format wird ebenfalls erkannt (Routing-IP wird ignoriert, Hostname aufgelöst)
|
||||
0.0.0.0 bad-actor.example.com
|
||||
127.0.0.1 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
|
||||
EXTERNAL_BLOCKLIST_ENABLED=true
|
||||
EXTERNAL_BLOCKLIST_URLS="https://example.com/blocklist.txt"
|
||||
EXTERNAL_BLOCKLIST_INTERVAL=300
|
||||
```
|
||||
|
||||
3. Mehrere Listen können kommagetrennt angegeben werden:
|
||||
|
||||
```bash
|
||||
EXTERNAL_BLOCKLIST_URLS="https://example.com/list1.txt,https://other.com/list2.txt"
|
||||
```
|
||||
|
||||
4. Service neustarten:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart adguard-shield
|
||||
```
|
||||
|
||||
#### Unterstützte Eintragsformate
|
||||
|
||||
| Format | Beispiel | Verhalten |
|
||||
|--------|----------|----------|
|
||||
| IPv4 | `1.2.3.4` | direkt gesperrt |
|
||||
| IPv4-CIDR | `10.0.0.0/8` | direkt gesperrt |
|
||||
| IPv6 | `2001:db8::1` | direkt gesperrt |
|
||||
| IPv6-CIDR | `2001:db8::/32` | direkt gesperrt |
|
||||
| Hostname | `bad.example.com` | per lokalem DNS aufgelöst, alle IPs (IPv4 + IPv6) gesperrt |
|
||||
| Hosts-Format | `0.0.0.0 bad.example.com` | Hostname extrahiert und aufgelöst |
|
||||
| Kommentar | `# Text` | übersprungen |
|
||||
| Inline-Kommentar | `1.2.3.4 # Text` | Kommentar entfernt, IP gesperrt |
|
||||
|
||||
Folgende Einträge werden mit einer Warnung im Log übersprungen:
|
||||
|
||||
- URLs (`https://...`, `http://...`)
|
||||
- IP:Port-Kombinationen (`1.2.3.4:8080`)
|
||||
- Hostnamen mit ungültigen Zeichen oder ohne Punkt
|
||||
- Einträge mit nicht auflösbarem Hostnamen
|
||||
- `0.0.0.0` und `::` (AdGuard-Blocking-Antwort)
|
||||
## Gesperrte Ports im Detail
|
||||
|
||||
Bei einem Rate-Limit-Verstoß werden **alle** DNS-Protokoll-Ports für den Client gesperrt (IPv4 via `iptables` und IPv6 via `ip6tables`):
|
||||
|
||||
| Port | Protokoll | Beschreibung |
|
||||
|------|-----------|-------------|
|
||||
| 53 | UDP/TCP | Standard DNS |
|
||||
| 443 | TCP | DNS-over-HTTPS (DoH) |
|
||||
| 853 | TCP | DNS-over-TLS (`tls://dns1.techniverse.net:853`) |
|
||||
| 853 | UDP | DNS-over-QUIC (`quic://dns1.techniverse.net:853`) |
|
||||
|
||||
## Protokoll-Erkennung
|
||||
|
||||
AdGuard Shield erkennt **automatisch**, welches DNS-Protokoll ein Client verwendet. Diese Information wird aus dem Feld `client_proto` der AdGuard Home Query Log API extrahiert und an folgenden Stellen angezeigt:
|
||||
|
||||
- **Log-Datei**: Jede Anfrage wird mit dem verwendeten Protokoll geloggt
|
||||
- **Ban-History**: Die Protokoll-Spalte zeigt, über welches Protokoll die Anfragen kamen
|
||||
- **Status-Anzeige**: Aktive Sperren zeigen das verwendete Protokoll an
|
||||
- **Benachrichtigungen**: Push-Nachrichten enthalten das Protokoll
|
||||
|
||||
### Unterstützte Protokolle
|
||||
|
||||
| API-Wert | Anzeige | Beschreibung |
|
||||
|----------|---------|-------------|
|
||||
| *(leer)* | `DNS` | Klassisches DNS über UDP/TCP (Port 53) |
|
||||
| `doh` | `DoH` | DNS-over-HTTPS (Port 443) |
|
||||
| `dot` | `DoT` | DNS-over-TLS (Port 853) |
|
||||
| `doq` | `DoQ` | DNS-over-QUIC (Port 853/UDP) |
|
||||
| `dnscrypt` | `DNSCrypt` | DNSCrypt-Protokoll |
|
||||
|
||||
Verwendet ein Client mehrere Protokolle gleichzeitig (z.B. DoH und DNS), werden alle erkannten Protokolle kommagetrennt angezeigt (z.B. `DNS,DoH`).
|
||||
|
||||
> **Wichtig:** Alle Protokolle werden gleichermaßen überwacht und gegen das Rate-Limit geprüft. Ein DoH-Flood wird genauso erkannt und gesperrt wie ein klassischer DNS-Flood – die Erkennung basiert auf den AdGuard Home Logdaten, nicht auf Netzwerk-Traffic.
|
||||
|
||||
## Whitelist richtig pflegen
|
||||
|
||||
Die Whitelist sollte mindestens enthalten:
|
||||
|
||||
- `127.0.0.1` und `::1` (Localhost)
|
||||
- Die IP deines Routers / Gateways
|
||||
- Deine eigenen Management-IPs
|
||||
- Andere vertrauenswürdige DNS-Clients
|
||||
|
||||
Beispiel:
|
||||
|
||||
```
|
||||
WHITELIST="127.0.0.1,::1,192.168.1.1,192.168.1.10,fd00::1"
|
||||
```
|
||||
|
||||
### Externe Whitelist für dynamische IPs
|
||||
|
||||
Für Clients mit wechselnden IP-Adressen (z.B. DynDNS) kann eine **externe Whitelist** genutzt werden. Der Whitelist-Worker löst Domains regelmäßig per DNS auf und fügt die aktuellen IPs automatisch zur Whitelist hinzu. Siehe [Externe Whitelist](#externe-whitelist) für die Konfiguration.
|
||||
276
docs/report.md
Normal file
276
docs/report.md
Normal file
@@ -0,0 +1,276 @@
|
||||
# E-Mail Report
|
||||
|
||||
AdGuard Shield kann regelmäßig einen Statistik-Report per E-Mail versenden. Der Report enthält eine Übersicht über alle Sperren, die auffälligsten IPs, meistbetroffene Domains und weitere Statistiken.
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
Der Server muss E-Mails versenden können. Empfohlen wird **msmtp** als leichtgewichtiger SMTP-Client.
|
||||
|
||||
**Anleitung zur Einrichtung von msmtp:**
|
||||
👉 [Linux: Einfach E-Mails versenden mit msmtp](https://www.cleveradmin.de/blog/2024/12/linux-einfach-emails-versenden-mit-msmtp/)
|
||||
|
||||
Alternativ funktioniert auch `sendmail`, `mail` oder ein anderer Befehl, der E-Mails über stdin entgegennimmt.
|
||||
|
||||
## Aktivierung
|
||||
|
||||
In der Konfiguration (`adguard-shield.conf`):
|
||||
|
||||
```bash
|
||||
REPORT_ENABLED=true
|
||||
REPORT_INTERVAL="weekly"
|
||||
REPORT_TIME="08:00"
|
||||
REPORT_EMAIL_TO="admin@example.com"
|
||||
REPORT_EMAIL_FROM="adguard-shield@example.com"
|
||||
REPORT_FORMAT="html"
|
||||
REPORT_MAIL_CMD="msmtp"
|
||||
```
|
||||
|
||||
Anschließend den Cron-Job einrichten:
|
||||
|
||||
```bash
|
||||
sudo /opt/adguard-shield/report-generator.sh install
|
||||
```
|
||||
|
||||
## Konfigurationsparameter
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `REPORT_ENABLED` | `false` | Report-Funktion aktivieren |
|
||||
| `REPORT_INTERVAL` | `weekly` | Versandintervall (siehe unten) |
|
||||
| `REPORT_TIME` | `08:00` | Uhrzeit für den Versand (HH:MM, 24h) |
|
||||
| `REPORT_EMAIL_TO` | *(leer)* | E-Mail-Empfänger |
|
||||
| `REPORT_EMAIL_FROM` | `adguard-shield@hostname` | E-Mail-Absender |
|
||||
| `REPORT_FORMAT` | `html` | Format: `html` oder `txt` |
|
||||
| `REPORT_MAIL_CMD` | `msmtp` | Mail-Befehl (`msmtp`, `sendmail`, `mail`) |
|
||||
| `REPORT_BUSIEST_DAY_RANGE` | `30` | Zeitraum in Tagen für „Aktivster Tag“ (0 = Berichtszeitraum) |
|
||||
|
||||
### Versandintervalle
|
||||
|
||||
| Wert | Beschreibung |
|
||||
|------|-------------|
|
||||
| `daily` | Täglich zur konfigurierten Uhrzeit |
|
||||
| `weekly` | Wöchentlich am Montag |
|
||||
| `biweekly` | Alle zwei Wochen am Montag |
|
||||
| `monthly` | Monatlich am 1. des Monats |
|
||||
|
||||
## Report-Inhalte
|
||||
|
||||
Der Report enthält folgende Statistiken:
|
||||
|
||||
### Zeitraum-Schnellübersicht *(immer ganz oben)*
|
||||
|
||||
Eine Vergleichstabelle mit Live-Zahlen für vier feste Zeitfenster – unabhängig vom konfigurierten `REPORT_INTERVAL`:
|
||||
|
||||
| Zeitraum | Sperren | Entsperrungen | Eindeutige IPs | Permanent gebannt |
|
||||
|----------|---------|---------------|----------------|-------------------|
|
||||
| Heute *(nur nach 20:00 Uhr)* | … | … | … | … |
|
||||
| Gestern | … | … | … | … |
|
||||
| Letzte 7 Tage | … | … | … | … |
|
||||
| Letzte 14 Tage | … | … | … | … |
|
||||
| Letzte 30 Tage | … | … | … | … |
|
||||
|
||||
Im HTML-Format wird **Gestern** grün hervorgehoben, **Heute** blau (erscheint nur ab 20:00 Uhr).
|
||||
- **Gestern** umfasst exakt 00:00:00 – 23:59:59 des gestrigen Tages.
|
||||
- **Heute** umfasst den laufenden Tag von 00:00:00 bis zum Zeitpunkt der Reportgenerierung und wird nur eingeblendet, wenn der Report nach 20:00 Uhr erstellt wird.
|
||||
Die übrigen Zeiträume laufen vom Starttag 00:00 Uhr bis zum Zeitpunkt der Reportgenerierung.
|
||||
|
||||
> **Hinweis:** Die AbuseIPDB-Meldungen werden in der Schnellübersicht nicht mehr separat ausgewiesen, da sie immer mit einer Permanentsperre korrelieren – der Wert „Permanent gebannt" ist daher ausreichend. Die Gesamtanzahl der AbuseIPDB-Reports im Berichtszeitraum ist weiterhin in der allgemeinen Übersicht sichtbar.
|
||||
|
||||
### Übersicht (Berichtszeitraum)
|
||||
- Gesamtzahl der Sperren und Entsperrungen
|
||||
- Anzahl eindeutiger gesperrter IPs
|
||||
- Permanente Sperren
|
||||
- Aktuell aktive Sperren
|
||||
- AbuseIPDB-Reports
|
||||
|
||||
### Angriffsarten
|
||||
- Rate-Limit Sperren
|
||||
- Subdomain-Flood Sperren
|
||||
- Externe Blocklist Sperren
|
||||
- Aktivster Tag – wird über einen konfigurierbaren Zeitraum ermittelt (Standard: letzte 30 Tage, `REPORT_BUSIEST_DAY_RANGE`). Zeigt zusätzlich die Anzahl der Sperren an diesem Tag. Bei `REPORT_BUSIEST_DAY_RANGE=0` wird nur der Berichtszeitraum betrachtet.
|
||||
|
||||
### Top 10 Listen
|
||||
- **Auffälligste IPs** — Die 10 IPs mit den meisten Sperren (mit Balkendiagramm im HTML-Format)
|
||||
- **Meistbetroffene Domains** — Die 10 am häufigsten betroffenen Domains
|
||||
|
||||
### Weitere Details
|
||||
- **Protokoll-Verteilung** — Aufschlüsselung nach DNS, DoH, DoT, DoQ
|
||||
- **Letzte 10 Sperren** — Die aktuellsten Sperrereignisse mit Zeitstempel, IP, Domain und Grund
|
||||
|
||||
## Befehle
|
||||
|
||||
```bash
|
||||
# Report sofort generieren und versenden
|
||||
sudo /opt/adguard-shield/report-generator.sh send
|
||||
|
||||
# Test-E-Mail senden (prüft alle Voraussetzungen + Mailversand)
|
||||
sudo /opt/adguard-shield/report-generator.sh test
|
||||
|
||||
# Report als Datei generieren (auf stdout ausgeben)
|
||||
sudo /opt/adguard-shield/report-generator.sh generate
|
||||
|
||||
# Report im spezifischen Format generieren
|
||||
sudo /opt/adguard-shield/report-generator.sh generate html > report.html
|
||||
sudo /opt/adguard-shield/report-generator.sh generate txt > report.txt
|
||||
|
||||
# Cron-Job für automatischen Versand einrichten
|
||||
sudo /opt/adguard-shield/report-generator.sh install
|
||||
|
||||
# Cron-Job entfernen
|
||||
sudo /opt/adguard-shield/report-generator.sh remove
|
||||
|
||||
# Report-Konfiguration und Cron-Status anzeigen
|
||||
sudo /opt/adguard-shield/report-generator.sh status
|
||||
```
|
||||
|
||||
## Report-Intervall ändern
|
||||
|
||||
Um das Intervall, die Uhrzeit oder andere Einstellungen zu ändern:
|
||||
|
||||
```bash
|
||||
# 1. Konfiguration bearbeiten
|
||||
sudo nano /opt/adguard-shield/adguard-shield.conf
|
||||
# → z.B. REPORT_INTERVAL="weekly" auf "daily" ändern
|
||||
# → z.B. REPORT_TIME="09:00"
|
||||
|
||||
# 2. Cron-Job neu einrichten (überschreibt den alten automatisch)
|
||||
sudo /opt/adguard-shield/report-generator.sh install
|
||||
```
|
||||
|
||||
> **Hinweis:** Der `install`-Befehl überschreibt den bestehenden Cron-Job mit den aktuellen Werten aus der Konfiguration. Ein vorheriges `remove` ist nicht nötig, schadet aber auch nicht.
|
||||
|
||||
Alternativ in zwei Schritten:
|
||||
|
||||
```bash
|
||||
# Alten Cron-Job erst entfernen, dann neu anlegen
|
||||
sudo /opt/adguard-shield/report-generator.sh remove
|
||||
sudo nano /opt/adguard-shield/adguard-shield.conf
|
||||
sudo /opt/adguard-shield/report-generator.sh install
|
||||
```
|
||||
|
||||
## Templates
|
||||
|
||||
Die Report-Templates liegen unter:
|
||||
|
||||
```
|
||||
/opt/adguard-shield/templates/report.html # HTML-Template
|
||||
/opt/adguard-shield/templates/report.txt # TXT-Template
|
||||
```
|
||||
|
||||
Die Templates verwenden Platzhalter (z.B. `{{TOTAL_BANS}}`, `{{TOP10_IPS_TABLE}}`), die beim Generieren durch die tatsächlichen Werte ersetzt werden. Die Templates können nach Bedarf angepasst werden.
|
||||
|
||||
### Verfügbare Platzhalter
|
||||
|
||||
| Platzhalter | Beschreibung |
|
||||
|-------------|-------------|
|
||||
| `{{REPORT_PERIOD}}` | Berichtszeitraum mit Label |
|
||||
| `{{REPORT_DATE}}` | Erstellungsdatum des Reports |
|
||||
| `{{HOSTNAME}}` | Server-Hostname |
|
||||
| `{{VERSION}}` | AdGuard Shield Version |
|
||||
| `{{TOTAL_BANS}}` | Gesamtzahl Sperren |
|
||||
| `{{TOTAL_UNBANS}}` | Gesamtzahl Entsperrungen |
|
||||
| `{{UNIQUE_IPS}}` | Anzahl eindeutiger IPs |
|
||||
| `{{PERMANENT_BANS}}` | Permanente Sperren |
|
||||
| `{{ACTIVE_BANS}}` | Aktuell aktive Sperren |
|
||||
| `{{ABUSEIPDB_REPORTS}}` | Anzahl AbuseIPDB-Reports |
|
||||
| `{{RATELIMIT_BANS}}` | Rate-Limit Sperren |
|
||||
| `{{SUBDOMAIN_FLOOD_BANS}}` | Subdomain-Flood Sperren |
|
||||
| `{{EXTERNAL_BLOCKLIST_BANS}}` | Externe Blocklist Sperren |
|
||||
| `{{BUSIEST_DAY}}` | Aktivster Tag (Datum + Anzahl Sperren) |
|
||||
| `{{BUSIEST_DAY_LABEL}}` | Dynamisches Label für den aktivsten Tag (z.B. „Aktivster Tag (30 Tage)“) |
|
||||
| `{{TOP10_IPS_TABLE}}` | Top 10 IPs (HTML-Tabelle) |
|
||||
| `{{TOP10_IPS_TEXT}}` | Top 10 IPs (Text-Tabelle) |
|
||||
| `{{TOP10_DOMAINS_TABLE}}` | Top 10 Domains (HTML-Tabelle) |
|
||||
| `{{TOP10_DOMAINS_TEXT}}` | Top 10 Domains (Text-Tabelle) |
|
||||
| `{{PROTOCOL_TABLE}}` | Protokoll-Verteilung (HTML) |
|
||||
| `{{PROTOCOL_TEXT}}` | Protokoll-Verteilung (Text) |
|
||||
| `{{RECENT_BANS_TABLE}}` | Letzte Sperren (HTML) |
|
||||
| `{{RECENT_BANS_TEXT}}` | Letzte Sperren (Text) |
|
||||
|
||||
## Beispiel: Schnellstart
|
||||
|
||||
```bash
|
||||
# 1. msmtp installieren und konfigurieren
|
||||
sudo apt install msmtp msmtp-mta
|
||||
# Anleitung: https://www.cleveradmin.de/blog/2024/12/linux-einfach-emails-versenden-mit-msmtp/
|
||||
|
||||
# 2. Report-Konfiguration anpassen
|
||||
sudo nano /opt/adguard-shield/adguard-shield.conf
|
||||
# → REPORT_ENABLED=true
|
||||
# → REPORT_EMAIL_TO="deine@email.de"
|
||||
|
||||
# 3. Test-Mail senden (prüft alle Voraussetzungen)
|
||||
sudo /opt/adguard-shield/report-generator.sh test
|
||||
|
||||
# 4. Wenn die Test-Mail angekommen ist: echten Report testen
|
||||
sudo /opt/adguard-shield/report-generator.sh send
|
||||
|
||||
# 5. Automatischen Versand einrichten
|
||||
sudo /opt/adguard-shield/report-generator.sh install
|
||||
|
||||
# 6. Status prüfen
|
||||
sudo /opt/adguard-shield/report-generator.sh status
|
||||
```
|
||||
|
||||
## Test-Mail
|
||||
|
||||
Bevor du den automatischen Versand einrichtest, kannst du mit dem `test`-Befehl prüfen, ob alles funktioniert:
|
||||
|
||||
```bash
|
||||
sudo /opt/adguard-shield/report-generator.sh test
|
||||
```
|
||||
|
||||
Der Test prüft Schritt für Schritt:
|
||||
|
||||
1. **E-Mail-Empfänger** — Ist `REPORT_EMAIL_TO` konfiguriert?
|
||||
2. **E-Mail-Absender** — Zeigt den konfigurierten Absender an
|
||||
3. **Mail-Befehl** — Ist `msmtp` (oder der konfigurierte Befehl) installiert?
|
||||
4. **Report-Template** — Existiert das HTML/TXT-Template?
|
||||
5. **Ban-History** — Gibt es vorhandene Daten?
|
||||
6. **Test-Versand** — Sendet eine Test-E-Mail und prüft den Exit-Code
|
||||
|
||||
Die Test-Mail enthält eine Übersicht der aktuellen Konfiguration und bestätigt, dass der Mailversand funktioniert.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### E-Mail wird nicht versendet
|
||||
|
||||
1. Prüfe ob der Mail-Befehl installiert ist:
|
||||
```bash
|
||||
which msmtp
|
||||
```
|
||||
|
||||
2. Teste den Mailversand manuell:
|
||||
```bash
|
||||
echo "Test" | msmtp -t deine@email.de
|
||||
```
|
||||
|
||||
3. Prüfe die msmtp-Konfiguration:
|
||||
```bash
|
||||
cat ~/.msmtprc
|
||||
# oder
|
||||
cat /etc/msmtprc
|
||||
```
|
||||
|
||||
4. Prüfe die Report-Konfiguration:
|
||||
```bash
|
||||
sudo /opt/adguard-shield/report-generator.sh status
|
||||
```
|
||||
|
||||
### Report enthält keine Daten
|
||||
|
||||
Der Report basiert auf der Ban-History-Datei (`/var/log/adguard-shield-bans.log`). Wenn keine Sperren im Berichtszeitraum vorhanden sind, zeigt der Report „Keine Daten" an.
|
||||
|
||||
### Cron-Job wird nicht ausgeführt
|
||||
|
||||
1. Prüfe ob der Cron-Job angelegt wurde:
|
||||
```bash
|
||||
cat /etc/cron.d/adguard-shield-report
|
||||
```
|
||||
|
||||
2. Prüfe die Cron-Logs:
|
||||
```bash
|
||||
grep adguard-shield /var/log/syslog
|
||||
# oder
|
||||
journalctl -u cron
|
||||
```
|
||||
285
docs/tipps-und-troubleshooting.md
Normal file
285
docs/tipps-und-troubleshooting.md
Normal file
@@ -0,0 +1,285 @@
|
||||
# Tipps & Troubleshooting
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **Erst immer im Dry-Run testen**, bevor der scharfe Modus aktiviert wird
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh dry-run
|
||||
```
|
||||
- **Whitelist großzügig pflegen**: Eigene IPs, Router, wichtige Server nicht vergessen
|
||||
- **Sperrdauer anpassen**: Für DDoS-artige Muster ggf. länger sperren
|
||||
- **Logs regelmäßig prüfen**: Falsche Positive erkennen und Whitelist anpassen
|
||||
- **Ban-History nutzen**: `history`-Befehl zeigt alle vergangenen Sperren — hilfreich um Muster zu erkennen
|
||||
- **Log-Level auf DEBUG** setzen wenn etwas nicht funktioniert
|
||||
|
||||
## Häufige Probleme
|
||||
|
||||
### API-Verbindung schlägt fehl
|
||||
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh test
|
||||
```
|
||||
|
||||
**Mögliche Ursachen:**
|
||||
- Falsche URL in `ADGUARD_URL` (Port prüfen!)
|
||||
- Falsche Zugangsdaten (`ADGUARD_USER` / `ADGUARD_PASS`)
|
||||
- AdGuard Home läuft nicht
|
||||
- Firewall blockiert lokale Verbindung
|
||||
- DNS-Auflösung des Hostnames fehlgeschlagen
|
||||
- SSL/TLS-Zertifikatfehler (bei HTTPS)
|
||||
|
||||
#### Schritt-für-Schritt Diagnose
|
||||
|
||||
**1. Base-URL Erreichbarkeit prüfen (ohne Auth):**
|
||||
```bash
|
||||
# Vollständige Diagnose mit HTTP-Headern und Verbindungsdetails
|
||||
curl -ikv https://dns1.domain.com 2>&1
|
||||
|
||||
# Nur HTTP-Statuscode prüfen (schnell)
|
||||
curl -s -o /dev/null -w "%{http_code}\n" -k https://dns1.domain.com
|
||||
```
|
||||
|
||||
> `-i` zeigt HTTP-Response-Header, `-k` ignoriert SSL-Fehler, `-v` zeigt Verbindungsdetails (DNS, TLS-Handshake, etc.)
|
||||
|
||||
**2. DNS-Auflösung testen:**
|
||||
```bash
|
||||
# Hostname auflösen
|
||||
dig +short dns1.domain.com
|
||||
|
||||
# Oder mit nslookup
|
||||
nslookup dns1.domain.com
|
||||
```
|
||||
|
||||
**3. Port-Erreichbarkeit testen:**
|
||||
```bash
|
||||
# TCP-Verbindung zum Port prüfen (z.B. Port 3000)
|
||||
nc -zv 127.0.0.1 3000
|
||||
|
||||
# Oder mit curl
|
||||
curl -v telnet://127.0.0.1:3000
|
||||
```
|
||||
|
||||
**4. API-Endpunkt mit Authentifizierung testen:**
|
||||
```bash
|
||||
# Query-Log abfragen (mit Auth + Response-Header)
|
||||
curl -i -u admin:passwort https://dns1.domain.com/control/querylog?limit=1
|
||||
|
||||
# Nur HTTP-Status zurückgeben
|
||||
curl -s -o /dev/null -w "%{http_code}\n" -u admin:passwort https://dns1.domain.com/control/querylog?limit=1
|
||||
```
|
||||
|
||||
**5. AdGuard Home Status-API prüfen:**
|
||||
```bash
|
||||
# Allgemeinen Status abfragen (benötigt keine Auth)
|
||||
curl -ik https://dns1.domain.com/control/status
|
||||
```
|
||||
|
||||
#### Typische Fehlercodes
|
||||
|
||||
| HTTP-Code | Bedeutung | Lösung |
|
||||
|-----------|-----------|--------|
|
||||
| `000` | Keine Verbindung | Host nicht erreichbar, DNS-Fehler oder Firewall |
|
||||
| `200` | Erfolg | Alles in Ordnung ✅ |
|
||||
| `301/302` | Weiterleitung | URL prüfen — evtl. fehlt `https://` oder Port |
|
||||
| `401` | Nicht autorisiert | `ADGUARD_USER` / `ADGUARD_PASS` prüfen |
|
||||
| `403` | Zugriff verweigert | Zugangsdaten oder IP-Beschränkung in AdGuard Home |
|
||||
| `404` | Nicht gefunden | URL falsch oder AdGuard Home Version zu alt |
|
||||
| `502/503` | Service nicht verfügbar | AdGuard Home läuft nicht oder wird gerade neu gestartet |
|
||||
|
||||
#### curl Exit-Codes
|
||||
|
||||
| Exit-Code | Bedeutung |
|
||||
|-----------|-----------|
|
||||
| `6` | DNS-Auflösung fehlgeschlagen — Hostname prüfen |
|
||||
| `7` | Verbindung abgelehnt — Läuft AdGuard Home? Port korrekt? |
|
||||
| `28` | Timeout — Host nicht erreichbar oder Firewall blockiert |
|
||||
| `35` | SSL/TLS-Handshake fehlgeschlagen |
|
||||
| `51` | SSL-Zertifikat: Hostname stimmt nicht überein |
|
||||
| `60` | SSL-Zertifikat: nicht vertrauenswürdig (selbstsigniert?) |
|
||||
|
||||
> **Tipp:** Bei selbstsignierten Zertifikaten `-k` an curl anhängen, um SSL-Fehler zu ignorieren. AdGuard Shield verwendet intern automatisch `-k` bei der API-Kommunikation.
|
||||
|
||||
**Lösung:** URL und Zugangsdaten in der Konfiguration anpassen:
|
||||
```bash
|
||||
sudo nano /opt/adguard-shield/adguard-shield.conf
|
||||
sudo systemctl restart adguard-shield
|
||||
```
|
||||
|
||||
### iptables-Fehler: "Permission denied"
|
||||
|
||||
Das Script muss als **root** laufen, da iptables Root-Rechte benötigt.
|
||||
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh start
|
||||
```
|
||||
|
||||
### Client wird fälschlich gesperrt
|
||||
|
||||
1. Client sofort entsperren:
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh unban 192.168.1.100
|
||||
```
|
||||
2. In der Ban-History prüfen, warum gesperrt wurde:
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh history | grep 192.168.1.100
|
||||
```
|
||||
3. Offense-Zähler für die IP zurücksetzen (damit die progressive Sperre wieder bei Stufe 1 beginnt):
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh reset-offenses 192.168.1.100
|
||||
```
|
||||
4. IP zur Whitelist hinzufügen in `adguard-shield.conf`
|
||||
5. Service neustarten:
|
||||
```bash
|
||||
sudo systemctl restart adguard-shield
|
||||
```
|
||||
|
||||
### Client wurde permanent gesperrt (Progressive Sperren)
|
||||
|
||||
Wenn eine IP die maximale Stufe der progressiven Sperren erreicht hat, wird sie permanent gesperrt und nicht automatisch aufgehoben.
|
||||
|
||||
1. IP entsperren:
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh unban 192.168.1.100
|
||||
```
|
||||
2. Offense-Zähler zurücksetzen:
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh reset-offenses 192.168.1.100
|
||||
```
|
||||
3. Prüfen ob die IP auf die Whitelist gehört, oder die Progressive-Ban-Einstellungen anpassen (`PROGRESSIVE_BAN_MAX_LEVEL` erhöhen oder auf `0` setzen für keine permanenten Sperren)
|
||||
|
||||
### Sperren überleben Reboot nicht
|
||||
|
||||
Das ist normal — iptables-Regeln sind flüchtig. Der **Service** erstellt die Chain beim Start automatisch neu. Aktive Sperren aus dem State-Verzeichnis werden aber nicht automatisch wiederhergestellt.
|
||||
|
||||
**Optionen:**
|
||||
- `iptables-persistent` installieren (`apt install iptables-persistent`)
|
||||
- Oder den State beim Boot wiederherstellen lassen (Feature-Idee)
|
||||
|
||||
### Zu viele false positives
|
||||
|
||||
- `RATE_LIMIT_MAX_REQUESTS` erhöhen (z.B. 50 oder 100)
|
||||
- `RATE_LIMIT_WINDOW` vergrößern (z.B. 120 Sekunden)
|
||||
- Windows-Clients fragen manche Domains von Natur aus sehr oft an — Whitelist nutzen
|
||||
|
||||
### Subdomain-Flood-Erkennung sperrt legitime Clients
|
||||
|
||||
Manche Dienste (z.B. CDNs, Cloud-Dienste, Microsoft 365) nutzen von Natur aus viele verschiedene Subdomains. Falls ein legitimer Client fälschlicherweise durch die Subdomain-Flood-Erkennung gesperrt wird:
|
||||
|
||||
1. Client sofort entsperren:
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh unban <IP>
|
||||
```
|
||||
2. Schwellwert erhöhen — z.B. von 50 auf 100 oder 150:
|
||||
```bash
|
||||
SUBDOMAIN_FLOOD_MAX_UNIQUE=100
|
||||
```
|
||||
3. Zeitfenster vergrößern — z.B. auf 120 Sekunden:
|
||||
```bash
|
||||
SUBDOMAIN_FLOOD_WINDOW=120
|
||||
```
|
||||
4. Oder die IP zur Whitelist hinzufügen
|
||||
5. Im Zweifelsfall die Erkennung temporär deaktivieren:
|
||||
```bash
|
||||
SUBDOMAIN_FLOOD_ENABLED=false
|
||||
```
|
||||
|
||||
> **Tipp:** Im Dry-Run-Modus (`sudo /opt/adguard-shield/adguard-shield.sh dry-run`) kann man beobachten, welche Clients die Subdomain-Flood-Erkennung auslösen würden, ohne sie wirklich zu sperren.
|
||||
|
||||
### Monitor startet nicht (PID-File)
|
||||
|
||||
```bash
|
||||
# Altes PID-File entfernen
|
||||
sudo rm -f /var/run/adguard-shield.pid
|
||||
sudo systemctl start adguard-shield
|
||||
```
|
||||
|
||||
### Service ist ausgefallen und startet nicht mehr
|
||||
|
||||
Wenn systemd das Restart-Limit erreicht hat (z.B. `"Start request repeated too quickly"`), hilft der **Watchdog** — er prüft alle 5 Minuten ob der Service läuft und startet ihn automatisch neu.
|
||||
|
||||
**Watchdog-Status prüfen:**
|
||||
```bash
|
||||
# Timer-Status anzeigen
|
||||
sudo systemctl status adguard-shield-watchdog.timer
|
||||
|
||||
# Letzte Watchdog-Ausführungen anzeigen
|
||||
sudo systemctl list-timers adguard-shield-watchdog.timer
|
||||
|
||||
# Watchdog-Logs prüfen
|
||||
sudo journalctl -u adguard-shield-watchdog.service --no-pager -n 20
|
||||
```
|
||||
|
||||
**Manuelles Recovery (sofort):**
|
||||
```bash
|
||||
# systemd-Fehlerzähler zurücksetzen und Service starten
|
||||
sudo systemctl reset-failed adguard-shield.service
|
||||
sudo systemctl start adguard-shield.service
|
||||
```
|
||||
|
||||
**Watchdog nachträglich aktivieren:**
|
||||
```bash
|
||||
sudo systemctl enable adguard-shield-watchdog.timer
|
||||
sudo systemctl start adguard-shield-watchdog.timer
|
||||
```
|
||||
|
||||
> **Hinweis:** Der Watchdog sendet automatisch eine Benachrichtigung (falls `NOTIFY_ENABLED=true`), wenn er den Service wiederbeleben muss oder die Recovery fehlschlägt.
|
||||
|
||||
## Update durchführen
|
||||
|
||||
```bash
|
||||
# Repository aktualisieren
|
||||
cd /tmp/adguard-shield
|
||||
git pull
|
||||
|
||||
# Update ausführen (Konfig wird automatisch migriert, Service neu gestartet)
|
||||
sudo bash install.sh update
|
||||
```
|
||||
|
||||
**Was passiert beim Update:**
|
||||
- Alle Scripts werden aktualisiert
|
||||
- Konfiguration wird als `adguard-shield.conf.old` gesichert
|
||||
- Neue Konfigurationsparameter werden automatisch zur bestehenden Konfig ergänzt
|
||||
- Bestehende Einstellungen bleiben erhalten
|
||||
- Service wird per `daemon-reload` neu geladen und automatisch neu gestartet
|
||||
|
||||
## Deinstallation
|
||||
|
||||
Ab Version 0.6 gibt es einen eigenständigen Uninstaller im Installationsverzeichnis. Die Deinstallation kann daher jederzeit durchgeführt werden, **ohne die originalen Installationsdateien (install.sh) behalten zu müssen**:
|
||||
|
||||
```bash
|
||||
# Empfohlen: direkt aus dem Installationsverzeichnis ausführen
|
||||
sudo bash /opt/adguard-shield/uninstall.sh
|
||||
|
||||
# Alternativ: über den Installer (sofern noch vorhanden)
|
||||
sudo bash install.sh uninstall
|
||||
```
|
||||
|
||||
Beide Wege sind gleichwertig — `install.sh uninstall` delegiert intern an `/opt/adguard-shield/uninstall.sh`.
|
||||
|
||||
Oder manuell:
|
||||
```bash
|
||||
sudo systemctl stop adguard-shield
|
||||
sudo systemctl disable adguard-shield
|
||||
sudo systemctl stop adguard-shield-watchdog.timer
|
||||
sudo systemctl disable adguard-shield-watchdog.timer
|
||||
sudo /opt/adguard-shield/iptables-helper.sh remove
|
||||
sudo rm -rf /opt/adguard-shield
|
||||
sudo rm -f /etc/systemd/system/adguard-shield.service
|
||||
sudo rm -f /etc/systemd/system/adguard-shield-watchdog.service
|
||||
sudo rm -f /etc/systemd/system/adguard-shield-watchdog.timer
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
Folgende Pakete werden für den Betrieb benötigt und bei der Installation automatisch installiert:
|
||||
|
||||
| Paket | Zweck |
|
||||
|-------|-------|
|
||||
| `curl` | API-Kommunikation mit AdGuard Home |
|
||||
| `jq` | JSON-Verarbeitung der API-Antworten |
|
||||
| `iptables` | Firewall-Regeln (IPv4 + IPv6) |
|
||||
| `gawk` | Textverarbeitung in Scripts |
|
||||
| `systemd` | Service-Management und Autostart |
|
||||
|
||||
Diese werden bei `sudo bash install.sh install` automatisch geprüft und bei Bedarf über den Paketmanager (`apt`, `dnf`, `yum`, `pacman`) nachinstalliert.
|
||||
77
docs/update.md
Normal file
77
docs/update.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Update-Anleitung
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- AdGuard Shield ist bereits installiert (`/opt/adguard-shield/`)
|
||||
- Git ist installiert (`sudo apt install git`)
|
||||
- Zugriff auf den Server per SSH mit Root-Rechten
|
||||
|
||||
## Update durchführen
|
||||
|
||||
### 1. Git-Repository aktualisieren
|
||||
|
||||
Wechsle in das Verzeichnis, in dem du das Repository geklont hast, und hole die neueste Version:
|
||||
|
||||
```bash
|
||||
cd /pfad/zum/adguard-shield
|
||||
git pull
|
||||
```
|
||||
|
||||
> **Hinweis:** Falls du das Repository z.B. nach `/opt/adguard-shield-repo` geklont hast:
|
||||
> ```bash
|
||||
> cd /opt/adguard-shield-repo
|
||||
> git pull
|
||||
> ```
|
||||
|
||||
### 2. Update-Script ausführen
|
||||
|
||||
```bash
|
||||
sudo bash install.sh update
|
||||
```
|
||||
|
||||
Das Update-Script macht automatisch folgendes:
|
||||
|
||||
1. **Abhängigkeiten prüfen** — Fehlende Pakete werden nachinstalliert
|
||||
2. **Scripts aktualisieren** — Alle `.sh`-Dateien werden nach `/opt/adguard-shield/` kopiert
|
||||
3. **Konfigurations-Migration** — Neue Parameter werden automatisch zur bestehenden Konfiguration hinzugefügt, bestehende Einstellungen bleiben **unverändert**
|
||||
4. **Backup erstellen** — Die alte Konfiguration wird als `adguard-shield.conf.old` gesichert
|
||||
5. **Service aktualisieren** — Die systemd Service-Datei und Watchdog-Dateien werden aktualisiert und `daemon-reload` ausgeführt
|
||||
6. **Watchdog aktivieren** — Der Watchdog-Timer wird automatisch aktiviert (falls noch nicht aktiv)
|
||||
7. **Service neustarten** — Der Service wird automatisch neu gestartet (falls er vorher lief)
|
||||
|
||||
### 3. Neue Parameter prüfen (optional)
|
||||
|
||||
Nach dem Update empfiehlt es sich, eventuell neu hinzugefügte Konfigurationsparameter zu prüfen:
|
||||
|
||||
```bash
|
||||
sudo nano /opt/adguard-shield/adguard-shield.conf
|
||||
```
|
||||
|
||||
Falls etwas nicht stimmt, kann das Backup wiederhergestellt werden:
|
||||
|
||||
```bash
|
||||
sudo cp /opt/adguard-shield/adguard-shield.conf.old /opt/adguard-shield/adguard-shield.conf
|
||||
sudo systemctl restart adguard-shield
|
||||
```
|
||||
|
||||
## Kurzfassung (Copy & Paste)
|
||||
|
||||
```bash
|
||||
cd /pfad/zum/adguard-shield
|
||||
git pull
|
||||
sudo bash install.sh update
|
||||
```
|
||||
|
||||
## Versionsprüfung
|
||||
|
||||
Installierte Version anzeigen:
|
||||
|
||||
```bash
|
||||
sudo /opt/adguard-shield/adguard-shield.sh status
|
||||
```
|
||||
|
||||
Oder über den Installer:
|
||||
|
||||
```bash
|
||||
sudo bash install.sh status
|
||||
```
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -53,15 +53,15 @@ log_ban_history() {
|
||||
|
||||
if [[ ! -f "$BAN_HISTORY_FILE" ]]; then
|
||||
echo "# AdGuard Shield - Ban History" > "$BAN_HISTORY_FILE"
|
||||
echo "# Format: ZEITSTEMPEL | AKTION | CLIENT-IP | DOMAIN | ANFRAGEN | SPERRDAUER | GRUND" >> "$BAN_HISTORY_FILE"
|
||||
echo "#───────────────────────────────────────────────────────────────────────────────" >> "$BAN_HISTORY_FILE"
|
||||
echo "# Format: ZEITSTEMPEL | AKTION | CLIENT-IP | DOMAIN | ANFRAGEN | SPERRDAUER | PROTOKOLL | GRUND" >> "$BAN_HISTORY_FILE"
|
||||
echo "#──────────────────────────────────────────────────────────────────────────────────────────────────" >> "$BAN_HISTORY_FILE"
|
||||
fi
|
||||
|
||||
local duration="permanent"
|
||||
[[ "$EXTERNAL_BLOCKLIST_BAN_DURATION" -gt 0 ]] && duration="${EXTERNAL_BLOCKLIST_BAN_DURATION}s"
|
||||
|
||||
printf "%-19s | %-6s | %-39s | %-30s | %-8s | %-10s | %s\n" \
|
||||
"$timestamp" "$action" "$client_ip" "-" "-" "$duration" "$reason" \
|
||||
printf "%-19s | %-6s | %-39s | %-30s | %-8s | %-10s | %-10s | %s\n" \
|
||||
"$timestamp" "$action" "$client_ip" "-" "-" "$duration" "-" "$reason" \
|
||||
>> "$BAN_HISTORY_FILE"
|
||||
}
|
||||
|
||||
@@ -82,6 +82,13 @@ is_whitelisted() {
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Externe Whitelist prüfen (aufgelöste IPs aus dem Whitelist-Worker)
|
||||
local ext_wl_file="${EXTERNAL_WHITELIST_CACHE_DIR:-/var/lib/adguard-shield/external-whitelist}/resolved_ips.txt"
|
||||
if [[ -f "$ext_wl_file" ]] && grep -qxF "$ip" "$ext_wl_file" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -115,6 +122,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 +180,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,57 +205,115 @@ 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
|
||||
}
|
||||
|
||||
# ─── Hostname-Auflösung ──────────────────────────────────────────────────────
|
||||
# Versucht den Hostnamen einer IP per Reverse-DNS aufzulösen
|
||||
resolve_hostname() {
|
||||
local ip="$1"
|
||||
local hostname=""
|
||||
|
||||
if command -v dig &>/dev/null; then
|
||||
hostname=$(dig +short -x "$ip" 2>/dev/null | head -1 | sed 's/\.$//')
|
||||
fi
|
||||
|
||||
if [[ -z "$hostname" ]] && command -v host &>/dev/null; then
|
||||
hostname=$(host "$ip" 2>/dev/null | awk '/domain name pointer/ {print $NF}' | sed 's/\.$//' | head -1)
|
||||
fi
|
||||
|
||||
if [[ -z "$hostname" ]] && command -v getent &>/dev/null; then
|
||||
hostname=$(getent hosts "$ip" 2>/dev/null | awk '{print $2}' | head -1)
|
||||
fi
|
||||
|
||||
echo "${hostname:-(unbekannt)}"
|
||||
}
|
||||
|
||||
# ─── Benachrichtigung ────────────────────────────────────────────────────────
|
||||
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 title
|
||||
local message
|
||||
local my_hostname
|
||||
my_hostname=$(hostname)
|
||||
local client_hostname
|
||||
client_hostname=$(resolve_hostname "$ip")
|
||||
|
||||
if [[ "$action" == "ban" ]]; then
|
||||
message="🚫 Externe Blocklist: IP **$ip** gesperrt."
|
||||
title="🚨 🛡️ AdGuard Shield"
|
||||
message="🚫 AdGuard Shield Ban auf ${my_hostname} (Externe Blocklist)
|
||||
---
|
||||
IP: ${ip}
|
||||
Hostname: ${client_hostname}
|
||||
|
||||
Whois: https://www.whois.com/whois/${ip}
|
||||
AbuseIPDB: https://www.abuseipdb.com/check/${ip}"
|
||||
else
|
||||
message="✅ Externe Blocklist: IP **$ip** entsperrt (aus Liste entfernt)."
|
||||
title="✅ AdGuard Shield"
|
||||
message="✅ AdGuard Shield Freigabe auf ${my_hostname} (Externe Blocklist)
|
||||
---
|
||||
IP: ${ip}
|
||||
Hostname: ${client_hostname}
|
||||
|
||||
AbuseIPDB: https://www.abuseipdb.com/check/${ip}"
|
||||
fi
|
||||
|
||||
case "${NOTIFY_TYPE:-generic}" in
|
||||
discord)
|
||||
local json_payload
|
||||
json_payload=$(jq -nc --arg msg "$message" '{content: $msg}')
|
||||
curl -s -H "Content-Type: application/json" \
|
||||
-d "{\"content\": \"$message\"}" \
|
||||
-d "$json_payload" \
|
||||
"$NOTIFY_WEBHOOK_URL" &>/dev/null &
|
||||
;;
|
||||
slack)
|
||||
local json_payload
|
||||
json_payload=$(jq -nc --arg msg "$message" '{text: $msg}')
|
||||
curl -s -H "Content-Type: application/json" \
|
||||
-d "{\"text\": \"$message\"}" \
|
||||
-d "$json_payload" \
|
||||
"$NOTIFY_WEBHOOK_URL" &>/dev/null &
|
||||
;;
|
||||
gotify)
|
||||
curl -s -X POST "$NOTIFY_WEBHOOK_URL" \
|
||||
-F "title=AdGuard Shield - Externe Blocklist" \
|
||||
-F "message=$message" \
|
||||
-F "title=${title}" \
|
||||
-F "message=${message}" \
|
||||
-F "priority=5" &>/dev/null &
|
||||
;;
|
||||
ntfy)
|
||||
local ntfy_url="${NTFY_SERVER_URL:-https://ntfy.sh}"
|
||||
local tags="rotating_light,blocklist"
|
||||
[[ "$action" != "ban" ]] && tags="white_check_mark,blocklist"
|
||||
# Ntfy fügt Emojis über Tags hinzu → Titel ohne führende Emojis setzen
|
||||
local ntfy_title
|
||||
case "$action" in
|
||||
ban) ntfy_title="🛡️ AdGuard Shield" ;;
|
||||
*) ntfy_title="AdGuard Shield" ;;
|
||||
esac
|
||||
local -a curl_args=(
|
||||
-s -X POST "${ntfy_url}/${NTFY_TOPIC}"
|
||||
-H "Title: AdGuard Shield - Externe Blocklist"
|
||||
-H "Title: ${ntfy_title}"
|
||||
-H "Priority: ${NTFY_PRIORITY:-3}"
|
||||
-H "Tags: rotating_light,blocklist"
|
||||
-d "$(echo "$message" | sed 's/\*\*//g')"
|
||||
-H "Tags: ${tags}"
|
||||
-d "${message}"
|
||||
)
|
||||
[[ -n "${NTFY_TOKEN:-}" ]] && curl_args+=(-H "Authorization: Bearer ${NTFY_TOKEN}")
|
||||
curl "${curl_args[@]}" &>/dev/null &
|
||||
;;
|
||||
generic)
|
||||
local json_payload
|
||||
json_payload=$(jq -nc --arg msg "$message" --arg act "$action" --arg cl "$ip" \
|
||||
'{message: $msg, action: $act, client: $cl, source: "external-blocklist"}')
|
||||
curl -s -H "Content-Type: application/json" \
|
||||
-d "{\"message\": \"$message\", \"action\": \"$action\", \"client\": \"$ip\", \"source\": \"external-blocklist\"}" \
|
||||
-d "$json_payload" \
|
||||
"$NOTIFY_WEBHOOK_URL" &>/dev/null &
|
||||
;;
|
||||
esac
|
||||
@@ -318,24 +393,150 @@ download_blocklist() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# ─── Eintrag-Validierung ─────────────────────────────────────────────────────
|
||||
|
||||
# Prüft IPv4-Adresse mit optionalem CIDR (z.B. 1.2.3.4 oder 1.2.3.0/24)
|
||||
_is_valid_ipv4() {
|
||||
local ip="$1" addr="$1" prefix=""
|
||||
if [[ "$ip" == */* ]]; then
|
||||
addr="${ip%/*}"
|
||||
prefix="${ip#*/}"
|
||||
{ [[ "$prefix" =~ ^[0-9]+$ ]] && [[ "$prefix" -le 32 ]]; } || return 1
|
||||
fi
|
||||
local IFS='.'
|
||||
read -ra _octets <<< "$addr"
|
||||
[[ ${#_octets[@]} -eq 4 ]] || return 1
|
||||
local o
|
||||
for o in "${_octets[@]}"; do
|
||||
[[ "$o" =~ ^[0-9]+$ ]] || return 1
|
||||
[[ "$o" -le 255 ]] || return 1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# Prüft IPv6-Adresse mit optionalem CIDR (z.B. ::1 oder 2001:db8::/32)
|
||||
# Fängt auch IPv4:Port-Kombinationen ab (z.B. 1.2.3.4:8080)
|
||||
_is_valid_ipv6() {
|
||||
local ip="$1" addr="$1"
|
||||
if [[ "$ip" == */* ]]; then
|
||||
addr="${ip%/*}"
|
||||
local prefix="${ip#*/}"
|
||||
{ [[ "$prefix" =~ ^[0-9]+$ ]] && [[ "$prefix" -le 128 ]]; } || return 1
|
||||
fi
|
||||
# IPv4:Port abfangen — enthält Punkt(e) vor dem ersten Doppelpunkt
|
||||
[[ "$addr" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9] ]] && return 1
|
||||
# Muss mindestens einen Doppelpunkt haben und nur gültige Zeichen (Hex, Doppelpunkt, Punkt für IPv4-mapped)
|
||||
[[ "$addr" == *:* ]] || return 1
|
||||
[[ "$addr" =~ ^[0-9a-fA-F:\.]+$ ]] || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
# Prüft ob ein Hostname syntaktisch plausibel ist
|
||||
# Akzeptiert: example.com, sub.example.com, example.com. (trailing dot)
|
||||
# Lehnt ab: einzelne Wörter ohne Punkt, Sonderzeichen, überlange Einträge
|
||||
_is_valid_hostname() {
|
||||
local host="$1"
|
||||
host="${host%.}" # trailing dot (FQDN) entfernen
|
||||
[[ -z "$host" ]] && return 1
|
||||
[[ ${#host} -gt 253 ]] && return 1
|
||||
[[ "$host" =~ ^[a-zA-Z0-9._-]+$ ]] || return 1
|
||||
[[ "$host" =~ ^[.\-] ]] && return 1 # darf nicht mit . oder - beginnen
|
||||
[[ "$host" == *.* ]] || return 1 # muss mindestens einen Punkt enthalten
|
||||
return 0
|
||||
}
|
||||
|
||||
# ─── IPs aus Blocklist-Datei parsen ──────────────────────────────────────────
|
||||
# Unterstützt IPv4, IPv6, CIDR-Notation und Hostnamen (werden aufgelöst).
|
||||
# Unterstützt außerdem das Hosts-Datei-Format: "0.0.0.0 hostname" oder "127.0.0.1 hostname".
|
||||
# Ungültige Einträge (URLs, IP:Port, fehlerhafte IPs, einzelne Wörter usw.) werden
|
||||
# mit WARN geloggt und übersprungen.
|
||||
# 0.0.0.0 / :: wird nie importiert (AdGuard-typische Blocking-Antwort).
|
||||
parse_blocklist_ips() {
|
||||
local cache_file="$1"
|
||||
|
||||
[[ -f "$cache_file" ]] || return
|
||||
|
||||
# Zeilen lesen, Leerzeilen und Kommentare ignorieren, IPs extrahieren
|
||||
while IFS= read -r line; do
|
||||
# Leerzeilen überspringen
|
||||
[[ -z "$line" ]] && continue
|
||||
# Kommentare überspringen (# am Anfang)
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
# Whitespace trimmen
|
||||
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
|
||||
|
||||
# Whitespace trimmen, dann Inline-Kommentare entfernen (# oder ;)
|
||||
line=$(echo "$line" | xargs)
|
||||
# Leere Zeilen nach Trim überspringen
|
||||
line=$(echo "$line" | sed 's/[[:space:]]*[#;].*$//' | xargs)
|
||||
[[ -z "$line" ]] && continue
|
||||
# CIDR-Notation oder reine IP ausgeben
|
||||
echo "$line"
|
||||
|
||||
# ── URLs ablehnen (http://, https://, ftp:// …) ──────────────────────
|
||||
if [[ "$line" =~ ^[a-zA-Z][a-zA-Z0-9+.-]*:// ]]; then
|
||||
log "WARN" "Eintrag übersprungen (URL nicht erlaubt): $line"
|
||||
continue
|
||||
fi
|
||||
|
||||
# ── Hosts-Datei-Format erkennen: "<routing-IP> <ziel>" ───────────────
|
||||
# z.B. "0.0.0.0 bad.com" oder "127.0.0.1 malware.net"
|
||||
if [[ "$line" =~ ^[^[:space:]]+[[:space:]]+[^[:space:]] ]]; then
|
||||
local _first="${line%% *}"
|
||||
local _rest="${line#* }"
|
||||
local _second="${_rest%% *}"
|
||||
if [[ "$_first" == "0.0.0.0" || "$_first" =~ ^127\. ||
|
||||
"$_first" == "::1" || "$_first" == "::0" ||
|
||||
"$_first" == "::" ]]; then
|
||||
log "DEBUG" "Hosts-Format erkannt, extrahiere Ziel: $_second"
|
||||
line="$_second"
|
||||
else
|
||||
log "WARN" "Eintrag übersprungen (Leerzeichen im Eintrag, unbekanntes Format): $line"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Klassifizieren und validieren ─────────────────────────────────────
|
||||
if [[ "$line" == *:* ]]; then
|
||||
# ── IPv6 ──────────────────────────────────────────────────────────
|
||||
if _is_valid_ipv6 "$line"; then
|
||||
echo "$line"
|
||||
else
|
||||
log "WARN" "Eintrag übersprungen (ungültige IPv6-Adresse oder IP:Port): $line"
|
||||
fi
|
||||
|
||||
elif [[ "$line" =~ ^[0-9] ]]; then
|
||||
# ── IPv4 ──────────────────────────────────────────────────────────
|
||||
[[ "$line" == "0.0.0.0"* ]] && continue
|
||||
if _is_valid_ipv4 "$line"; then
|
||||
echo "$line"
|
||||
else
|
||||
log "WARN" "Eintrag übersprungen (ungültige IPv4-Adresse oder ungültiges CIDR): $line"
|
||||
fi
|
||||
|
||||
else
|
||||
# ── Hostname → DNS-Auflösung ──────────────────────────────────────
|
||||
if ! _is_valid_hostname "$line"; then
|
||||
log "WARN" "Eintrag übersprungen (kein gültiger Hostname): $line"
|
||||
continue
|
||||
fi
|
||||
local resolved
|
||||
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
|
||||
fi
|
||||
local resolved_count=0
|
||||
while IFS= read -r resolved_ip; do
|
||||
[[ -z "$resolved_ip" ]] && continue
|
||||
[[ "$resolved_ip" == "0.0.0.0" ]] && continue # AdGuard-Blocking-Antwort
|
||||
[[ "$resolved_ip" == "::" ]] && continue # IPv6 unspecified
|
||||
[[ "$resolved_ip" == "::0" ]] && continue
|
||||
echo "$resolved_ip"
|
||||
resolved_count=$((resolved_count + 1))
|
||||
done <<< "$resolved"
|
||||
if [[ $resolved_count -gt 0 ]]; then
|
||||
log "DEBUG" "Hostname aufgelöst: $line → $resolved_count IP(s)"
|
||||
else
|
||||
log "WARN" "Hostname lieferte nur ungültige Adressen (z.B. 0.0.0.0): $line – wird übersprungen"
|
||||
fi
|
||||
fi
|
||||
done < "$cache_file"
|
||||
}
|
||||
|
||||
@@ -414,8 +615,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 ────────────────────────────────────────────
|
||||
|
||||
532
external-whitelist-worker.sh
Normal file
532
external-whitelist-worker.sh
Normal file
@@ -0,0 +1,532 @@
|
||||
#!/bin/bash
|
||||
###############################################################################
|
||||
# AdGuard Shield - Externer Whitelist-Worker
|
||||
# Lädt externe Whitelist-Dateien herunter, löst Domains zu IPs auf und
|
||||
# stellt diese dem Hauptscript als dynamische Whitelist zur Verfügung.
|
||||
# Ideal für DynDNS-Domains mit wechselnden IP-Adressen.
|
||||
# Wird als Hintergrundprozess vom Hauptscript gestartet.
|
||||
#
|
||||
# Autor: Patrick Asmus
|
||||
# E-Mail: support@techniverse.net
|
||||
# Datum: 2026-04-04
|
||||
# Lizenz: MIT
|
||||
###############################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_FILE="${SCRIPT_DIR}/adguard-shield.conf"
|
||||
|
||||
# ─── Konfiguration laden ───────────────────────────────────────────────────────
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "FEHLER: Konfigurationsdatei nicht gefunden: $CONFIG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck source=adguard-shield.conf
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
# ─── Standardwerte ────────────────────────────────────────────────────────────
|
||||
EXTERNAL_WHITELIST_CACHE_DIR="${EXTERNAL_WHITELIST_CACHE_DIR:-/var/lib/adguard-shield/external-whitelist}"
|
||||
EXTERNAL_WHITELIST_RESOLVED_FILE="${EXTERNAL_WHITELIST_CACHE_DIR}/resolved_ips.txt"
|
||||
|
||||
# ─── Worker PID-File ──────────────────────────────────────────────────────────
|
||||
WORKER_PID_FILE="/var/run/adguard-whitelist-worker.pid"
|
||||
|
||||
# ─── Logging (eigene Funktion, nutzt gleiche Log-Datei) ───────────────────────
|
||||
declare -A LOG_LEVELS=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3)
|
||||
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
local message="$*"
|
||||
local configured_level="${LOG_LEVEL:-INFO}"
|
||||
|
||||
if [[ ${LOG_LEVELS[$level]:-1} -ge ${LOG_LEVELS[$configured_level]:-1} ]]; then
|
||||
local timestamp
|
||||
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
local log_entry="[$timestamp] [$level] [WHITELIST-WORKER] $message"
|
||||
echo "$log_entry" | tee -a "$LOG_FILE" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Verzeichnisse erstellen ──────────────────────────────────────────────────
|
||||
init_directories() {
|
||||
mkdir -p "$EXTERNAL_WHITELIST_CACHE_DIR"
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
}
|
||||
|
||||
# ─── Eintrag-Validierung ─────────────────────────────────────────────────────
|
||||
|
||||
# Prüft IPv4-Adresse mit optionalem CIDR
|
||||
_is_valid_ipv4() {
|
||||
local ip="$1" addr="$1" prefix=""
|
||||
if [[ "$ip" == */* ]]; then
|
||||
addr="${ip%/*}"
|
||||
prefix="${ip#*/}"
|
||||
{ [[ "$prefix" =~ ^[0-9]+$ ]] && [[ "$prefix" -le 32 ]]; } || return 1
|
||||
fi
|
||||
local IFS='.'
|
||||
read -ra _octets <<< "$addr"
|
||||
[[ ${#_octets[@]} -eq 4 ]] || return 1
|
||||
local o
|
||||
for o in "${_octets[@]}"; do
|
||||
[[ "$o" =~ ^[0-9]+$ ]] || return 1
|
||||
[[ "$o" -le 255 ]] || return 1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# Prüft IPv6-Adresse mit optionalem CIDR
|
||||
_is_valid_ipv6() {
|
||||
local ip="$1" addr="$1"
|
||||
if [[ "$ip" == */* ]]; then
|
||||
addr="${ip%/*}"
|
||||
local prefix="${ip#*/}"
|
||||
{ [[ "$prefix" =~ ^[0-9]+$ ]] && [[ "$prefix" -le 128 ]]; } || return 1
|
||||
fi
|
||||
[[ "$addr" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9] ]] && return 1
|
||||
[[ "$addr" == *:* ]] || return 1
|
||||
[[ "$addr" =~ ^[0-9a-fA-F:\.]+$ ]] || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
# Prüft ob ein Hostname syntaktisch plausibel ist
|
||||
_is_valid_hostname() {
|
||||
local host="$1"
|
||||
host="${host%.}" # trailing dot entfernen
|
||||
[[ -z "$host" ]] && return 1
|
||||
[[ ${#host} -gt 253 ]] && return 1
|
||||
[[ "$host" =~ ^[a-zA-Z0-9._-]+$ ]] || return 1
|
||||
[[ "$host" =~ ^[.\-] ]] && return 1
|
||||
[[ "$host" == *.* ]] || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
# ─── Externe Whitelist herunterladen ─────────────────────────────────────────
|
||||
download_whitelist() {
|
||||
local url="$1"
|
||||
local index="$2"
|
||||
local cache_file="${EXTERNAL_WHITELIST_CACHE_DIR}/whitelist_${index}.txt"
|
||||
local etag_file="${EXTERNAL_WHITELIST_CACHE_DIR}/whitelist_${index}.etag"
|
||||
local tmp_file="${EXTERNAL_WHITELIST_CACHE_DIR}/whitelist_${index}.tmp"
|
||||
|
||||
log "DEBUG" "Prüfe externe Whitelist: $url"
|
||||
|
||||
local -a curl_args=(
|
||||
-s
|
||||
-L
|
||||
--connect-timeout 10
|
||||
--max-time 30
|
||||
-o "$tmp_file"
|
||||
-w "%{http_code}"
|
||||
)
|
||||
|
||||
if [[ -f "$etag_file" ]]; then
|
||||
local stored_etag
|
||||
stored_etag=$(cat "$etag_file")
|
||||
curl_args+=(-H "If-None-Match: ${stored_etag}")
|
||||
fi
|
||||
|
||||
local http_code
|
||||
http_code=$(curl "${curl_args[@]}" -D "${tmp_file}.headers" "$url" 2>/dev/null) || {
|
||||
log "WARN" "Fehler beim Download der Whitelist: $url"
|
||||
rm -f "$tmp_file" "${tmp_file}.headers"
|
||||
return 1
|
||||
}
|
||||
|
||||
if [[ "$http_code" == "304" ]]; then
|
||||
log "DEBUG" "Whitelist nicht geändert (HTTP 304): $url"
|
||||
rm -f "$tmp_file" "${tmp_file}.headers"
|
||||
# Auch bei 304 müssen wir DNS neu auflösen (dynamische IPs!)
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
log "WARN" "Whitelist Download fehlgeschlagen (HTTP $http_code): $url"
|
||||
rm -f "$tmp_file" "${tmp_file}.headers"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -f "${tmp_file}.headers" ]]; then
|
||||
local new_etag
|
||||
new_etag=$(grep -i '^etag:' "${tmp_file}.headers" | head -1 | sed 's/^[^:]*: *//;s/\r$//')
|
||||
if [[ -n "$new_etag" ]]; then
|
||||
echo "$new_etag" > "$etag_file"
|
||||
fi
|
||||
fi
|
||||
rm -f "${tmp_file}.headers"
|
||||
|
||||
if [[ -f "$cache_file" ]]; then
|
||||
if diff -q "$tmp_file" "$cache_file" &>/dev/null; then
|
||||
log "DEBUG" "Whitelist Inhalt unverändert: $url"
|
||||
rm -f "$tmp_file"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
mv "$tmp_file" "$cache_file"
|
||||
log "INFO" "Whitelist aktualisiert: $url"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ─── Einträge aus Whitelist-Datei parsen und IPs auflösen ───────────────────
|
||||
# Gibt pro Zeile eine IP-Adresse aus (aufgelöste Domains + direkte IPs)
|
||||
parse_whitelist_entries() {
|
||||
local cache_file="$1"
|
||||
|
||||
[[ -f "$cache_file" ]] || return
|
||||
|
||||
while IFS= read -r line; do
|
||||
line="${line%$'\r'}"
|
||||
line="${line#$'\xef\xbb\xbf'}"
|
||||
|
||||
[[ -z "$line" ]] && continue
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
|
||||
line=$(echo "$line" | xargs)
|
||||
line=$(echo "$line" | sed 's/[[:space:]]*[#;].*$//' | xargs)
|
||||
[[ -z "$line" ]] && continue
|
||||
|
||||
# URLs ablehnen
|
||||
if [[ "$line" =~ ^[a-zA-Z][a-zA-Z0-9+.-]*:// ]]; then
|
||||
log "WARN" "Whitelist-Eintrag übersprungen (URL nicht erlaubt): $line"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Hosts-Datei-Format erkennen
|
||||
if [[ "$line" =~ ^[^[:space:]]+[[:space:]]+[^[:space:]] ]]; then
|
||||
local _first="${line%% *}"
|
||||
local _rest="${line#* }"
|
||||
local _second="${_rest%% *}"
|
||||
if [[ "$_first" == "0.0.0.0" || "$_first" =~ ^127\. ||
|
||||
"$_first" == "::1" || "$_first" == "::0" ||
|
||||
"$_first" == "::" ]]; then
|
||||
log "DEBUG" "Whitelist Hosts-Format erkannt, extrahiere: $_second"
|
||||
line="$_second"
|
||||
else
|
||||
log "WARN" "Whitelist-Eintrag übersprungen (unbekanntes Format): $line"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Klassifizieren und validieren
|
||||
if [[ "$line" == *:* ]]; then
|
||||
# IPv6
|
||||
if _is_valid_ipv6 "$line"; then
|
||||
echo "$line"
|
||||
else
|
||||
log "WARN" "Whitelist-Eintrag übersprungen (ungültige IPv6): $line"
|
||||
fi
|
||||
|
||||
elif [[ "$line" =~ ^[0-9] ]]; then
|
||||
# IPv4
|
||||
[[ "$line" == "0.0.0.0"* ]] && continue
|
||||
if _is_valid_ipv4 "$line"; then
|
||||
echo "$line"
|
||||
else
|
||||
log "WARN" "Whitelist-Eintrag übersprungen (ungültige IPv4): $line"
|
||||
fi
|
||||
|
||||
else
|
||||
# Hostname → DNS-Auflösung (wird bei jedem Durchlauf neu aufgelöst!)
|
||||
if ! _is_valid_hostname "$line"; then
|
||||
log "WARN" "Whitelist-Eintrag übersprungen (kein gültiger Hostname): $line"
|
||||
continue
|
||||
fi
|
||||
local resolved
|
||||
resolved=$(getent ahosts "$line" 2>/dev/null | awk '{print $1}' | sort -u) || resolved=""
|
||||
if [[ -z "$resolved" ]]; then
|
||||
log "WARN" "Whitelist-Hostname konnte nicht aufgelöst werden: $line"
|
||||
continue
|
||||
fi
|
||||
local resolved_count=0
|
||||
while IFS= read -r resolved_ip; do
|
||||
[[ -z "$resolved_ip" ]] && continue
|
||||
[[ "$resolved_ip" == "0.0.0.0" ]] && continue
|
||||
[[ "$resolved_ip" == "::" ]] && continue
|
||||
[[ "$resolved_ip" == "::0" ]] && continue
|
||||
echo "$resolved_ip"
|
||||
resolved_count=$((resolved_count + 1))
|
||||
done <<< "$resolved"
|
||||
if [[ $resolved_count -gt 0 ]]; then
|
||||
log "DEBUG" "Whitelist-Hostname aufgelöst: $line → $resolved_count IP(s)"
|
||||
else
|
||||
log "WARN" "Whitelist-Hostname lieferte nur ungültige Adressen: $line"
|
||||
fi
|
||||
fi
|
||||
done < "$cache_file"
|
||||
}
|
||||
|
||||
# ─── Whitelisten synchronisieren ─────────────────────────────────────────────
|
||||
sync_whitelists() {
|
||||
# Alle URLs herunterladen
|
||||
IFS=',' read -ra urls <<< "$EXTERNAL_WHITELIST_URLS"
|
||||
local index=0
|
||||
|
||||
for url in "${urls[@]}"; do
|
||||
url=$(echo "$url" | xargs)
|
||||
[[ -z "$url" ]] && continue
|
||||
|
||||
download_whitelist "$url" "$index" || true
|
||||
index=$((index + 1))
|
||||
done
|
||||
|
||||
# Alle Einträge aus Cache-Dateien parsen und IPs auflösen
|
||||
local all_ips_file="${EXTERNAL_WHITELIST_CACHE_DIR}/.all_ips.tmp"
|
||||
> "$all_ips_file"
|
||||
|
||||
for cache_file in "${EXTERNAL_WHITELIST_CACHE_DIR}"/whitelist_*.txt; do
|
||||
[[ -f "$cache_file" ]] || continue
|
||||
parse_whitelist_entries "$cache_file" >> "$all_ips_file"
|
||||
done
|
||||
|
||||
# Duplikate entfernen und in die resolved-Datei schreiben
|
||||
local unique_count
|
||||
sort -u "$all_ips_file" > "${EXTERNAL_WHITELIST_RESOLVED_FILE}.tmp"
|
||||
mv "${EXTERNAL_WHITELIST_RESOLVED_FILE}.tmp" "$EXTERNAL_WHITELIST_RESOLVED_FILE"
|
||||
unique_count=$(wc -l < "$EXTERNAL_WHITELIST_RESOLVED_FILE" | xargs)
|
||||
|
||||
rm -f "$all_ips_file"
|
||||
|
||||
log "DEBUG" "Externe Whitelist: $unique_count eindeutige IPs aufgelöst"
|
||||
|
||||
# Prüfe ob gesperrte IPs jetzt auf der Whitelist stehen und entsperrt werden müssen
|
||||
check_banned_whitelist_ips
|
||||
}
|
||||
|
||||
# ─── Gesperrte IPs prüfen die jetzt gewhitelistet sind ──────────────────────
|
||||
# Wenn eine IP nach einer Whitelist-Aktualisierung nun auf der externen
|
||||
# Whitelist steht, wird sie automatisch entsperrt.
|
||||
check_banned_whitelist_ips() {
|
||||
local state_dir="${STATE_DIR:-/var/lib/adguard-shield}"
|
||||
[[ -d "$state_dir" ]] || return
|
||||
[[ -f "$EXTERNAL_WHITELIST_RESOLVED_FILE" ]] || return
|
||||
|
||||
for state_file in "${state_dir}"/*.ban "${state_dir}"/ext_*.ban; do
|
||||
[[ -f "$state_file" ]] || continue
|
||||
local client_ip
|
||||
client_ip=$(grep '^CLIENT_IP=' "$state_file" | cut -d= -f2)
|
||||
[[ -z "$client_ip" ]] && continue
|
||||
|
||||
if grep -qxF "$client_ip" "$EXTERNAL_WHITELIST_RESOLVED_FILE" 2>/dev/null; then
|
||||
log "INFO" "Gesperrte IP $client_ip ist jetzt auf externer Whitelist – entsperre automatisch"
|
||||
|
||||
# iptables-Regel entfernen
|
||||
if [[ "$client_ip" == *:* ]]; then
|
||||
ip6tables -D "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true
|
||||
else
|
||||
iptables -D "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true
|
||||
fi
|
||||
|
||||
rm -f "$state_file"
|
||||
|
||||
# Ban-History Eintrag
|
||||
if [[ -f "${BAN_HISTORY_FILE:-/var/log/adguard-shield-bans.log}" ]]; then
|
||||
local timestamp
|
||||
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
printf "%-19s | %-6s | %-39s | %-30s | %-8s | %-10s | %-10s | %s\n" \
|
||||
"$timestamp" "UNBAN" "$client_ip" "-" "-" "-" "-" "external-whitelist" \
|
||||
>> "${BAN_HISTORY_FILE:-/var/log/adguard-shield-bans.log}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ─── PID-Management ──────────────────────────────────────────────────────────
|
||||
write_pid() {
|
||||
echo $$ > "$WORKER_PID_FILE"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
log "INFO" "Externer Whitelist-Worker wird beendet..."
|
||||
rm -f "$WORKER_PID_FILE"
|
||||
exit 0
|
||||
}
|
||||
|
||||
check_already_running() {
|
||||
if [[ -f "$WORKER_PID_FILE" ]]; then
|
||||
local old_pid
|
||||
old_pid=$(cat "$WORKER_PID_FILE")
|
||||
if kill -0 "$old_pid" 2>/dev/null; then
|
||||
log "DEBUG" "Whitelist-Worker läuft bereits (PID: $old_pid)"
|
||||
return 1
|
||||
else
|
||||
rm -f "$WORKER_PID_FILE"
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ─── Status anzeigen ─────────────────────────────────────────────────────────
|
||||
show_status() {
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo " Externer Whitelist-Worker - Status"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
if [[ "$EXTERNAL_WHITELIST_ENABLED" != "true" ]]; then
|
||||
echo " ⚠️ Externer Whitelist-Worker ist deaktiviert"
|
||||
echo " Aktivieren: EXTERNAL_WHITELIST_ENABLED=true in $CONFIG_FILE"
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
|
||||
# Worker-Prozess Status
|
||||
if [[ -f "$WORKER_PID_FILE" ]]; then
|
||||
local pid
|
||||
pid=$(cat "$WORKER_PID_FILE")
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
echo " ✅ Worker läuft (PID: $pid)"
|
||||
else
|
||||
echo " ❌ Worker nicht aktiv (veraltete PID-Datei)"
|
||||
fi
|
||||
else
|
||||
echo " ❌ Worker nicht aktiv"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Konfigurierte URLs
|
||||
echo " Konfigurierte Whitelisten:"
|
||||
IFS=',' read -ra urls <<< "$EXTERNAL_WHITELIST_URLS"
|
||||
local index=0
|
||||
for url in "${urls[@]}"; do
|
||||
url=$(echo "$url" | xargs)
|
||||
[[ -z "$url" ]] && continue
|
||||
local cache_file="${EXTERNAL_WHITELIST_CACHE_DIR}/whitelist_${index}.txt"
|
||||
if [[ -f "$cache_file" ]]; then
|
||||
local entry_count
|
||||
entry_count=$(grep -cv '^\s*#\|^\s*$' "$cache_file" 2>/dev/null || echo "0")
|
||||
local last_modified
|
||||
last_modified=$(date -r "$cache_file" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "unbekannt")
|
||||
echo " [$index] $url"
|
||||
echo " Einträge: $entry_count | Zuletzt aktualisiert: $last_modified"
|
||||
else
|
||||
echo " [$index] $url (noch nicht heruntergeladen)"
|
||||
fi
|
||||
index=$((index + 1))
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# Aufgelöste IPs
|
||||
if [[ -f "$EXTERNAL_WHITELIST_RESOLVED_FILE" ]]; then
|
||||
local resolved_count
|
||||
resolved_count=$(wc -l < "$EXTERNAL_WHITELIST_RESOLVED_FILE" | xargs)
|
||||
local last_resolved
|
||||
last_resolved=$(date -r "$EXTERNAL_WHITELIST_RESOLVED_FILE" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "unbekannt")
|
||||
echo " Aufgelöste IPs: $resolved_count"
|
||||
echo " Letzte Auflösung: $last_resolved"
|
||||
|
||||
if [[ "$resolved_count" -gt 0 && "$resolved_count" -le 20 ]]; then
|
||||
echo ""
|
||||
echo " Aktuelle IPs:"
|
||||
while IFS= read -r ip; do
|
||||
echo " ✅ $ip"
|
||||
done < "$EXTERNAL_WHITELIST_RESOLVED_FILE"
|
||||
elif [[ "$resolved_count" -gt 20 ]]; then
|
||||
echo ""
|
||||
echo " Erste 20 IPs:"
|
||||
head -20 "$EXTERNAL_WHITELIST_RESOLVED_FILE" | while IFS= read -r ip; do
|
||||
echo " ✅ $ip"
|
||||
done
|
||||
echo " ... ($((resolved_count - 20)) weitere)"
|
||||
fi
|
||||
else
|
||||
echo " Aufgelöste IPs: 0 (noch keine Synchronisation durchgeführt)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Prüfintervall: ${EXTERNAL_WHITELIST_INTERVAL}s"
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
}
|
||||
|
||||
# ─── Einmalig synchronisieren ────────────────────────────────────────────────
|
||||
run_once() {
|
||||
init_directories
|
||||
|
||||
if [[ -z "${EXTERNAL_WHITELIST_URLS:-}" ]]; then
|
||||
log "ERROR" "Keine externen Whitelist-URLs konfiguriert (EXTERNAL_WHITELIST_URLS)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "INFO" "Einmalige Whitelist-Synchronisation..."
|
||||
sync_whitelists
|
||||
log "INFO" "Whitelist-Synchronisation abgeschlossen"
|
||||
}
|
||||
|
||||
# ─── Hauptschleife ──────────────────────────────────────────────────────────
|
||||
main_loop() {
|
||||
init_directories
|
||||
|
||||
if [[ -z "${EXTERNAL_WHITELIST_URLS:-}" ]]; then
|
||||
log "ERROR" "Keine externen Whitelist-URLs konfiguriert (EXTERNAL_WHITELIST_URLS)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "INFO" "═══════════════════════════════════════════════════════════"
|
||||
log "INFO" "Externer Whitelist-Worker gestartet"
|
||||
log "INFO" " URLs: ${EXTERNAL_WHITELIST_URLS}"
|
||||
log "INFO" " Prüfintervall: ${EXTERNAL_WHITELIST_INTERVAL}s"
|
||||
log "INFO" "═══════════════════════════════════════════════════════════"
|
||||
|
||||
while true; do
|
||||
sync_whitelists
|
||||
sleep "$EXTERNAL_WHITELIST_INTERVAL"
|
||||
done
|
||||
}
|
||||
|
||||
# ─── Signal-Handler ──────────────────────────────────────────────────────────
|
||||
trap cleanup SIGTERM SIGINT SIGHUP
|
||||
|
||||
# ─── Kommandozeilen-Argumente ────────────────────────────────────────────────
|
||||
case "${1:-start}" in
|
||||
start)
|
||||
if ! check_already_running; then
|
||||
exit 0
|
||||
fi
|
||||
write_pid
|
||||
main_loop
|
||||
;;
|
||||
stop)
|
||||
if [[ -f "$WORKER_PID_FILE" ]]; then
|
||||
kill "$(cat "$WORKER_PID_FILE")" 2>/dev/null || true
|
||||
rm -f "$WORKER_PID_FILE"
|
||||
echo "Whitelist-Worker gestoppt"
|
||||
else
|
||||
echo "Whitelist-Worker läuft nicht"
|
||||
fi
|
||||
;;
|
||||
sync)
|
||||
run_once
|
||||
;;
|
||||
status)
|
||||
init_directories
|
||||
show_status
|
||||
;;
|
||||
flush)
|
||||
init_directories
|
||||
echo "Entferne aufgelöste externe Whitelist-IPs..."
|
||||
rm -f "$EXTERNAL_WHITELIST_RESOLVED_FILE"
|
||||
echo "Externe Whitelist-IPs entfernt"
|
||||
;;
|
||||
*)
|
||||
cat << USAGE
|
||||
AdGuard Shield - Externer Whitelist-Worker
|
||||
|
||||
Nutzung: $0 {start|stop|sync|status|flush}
|
||||
|
||||
Befehle:
|
||||
start Startet den Worker (Dauerbetrieb)
|
||||
stop Stoppt den Worker
|
||||
sync Einmalige Synchronisation (DNS-Auflösung)
|
||||
status Zeigt Status und aufgelöste IPs
|
||||
flush Entfernt alle aufgelösten Whitelist-IPs
|
||||
|
||||
Konfiguration: $CONFIG_FILE
|
||||
|
||||
USAGE
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
932
geoip-worker.sh
Normal file
932
geoip-worker.sh
Normal file
@@ -0,0 +1,932 @@
|
||||
#!/bin/bash
|
||||
###############################################################################
|
||||
# AdGuard Shield - GeoIP Worker
|
||||
# Prüft Client-IPs auf Herkunftsland und sperrt/erlaubt basierend auf Konfig.
|
||||
# Wird als Hintergrundprozess vom Hauptscript gestartet.
|
||||
#
|
||||
# Autor: Patrick Asmus
|
||||
# E-Mail: support@techniverse.net
|
||||
# Lizenz: MIT
|
||||
###############################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_FILE="${SCRIPT_DIR}/adguard-shield.conf"
|
||||
|
||||
# ─── Konfiguration laden ───────────────────────────────────────────────────────
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "FEHLER: Konfigurationsdatei nicht gefunden: $CONFIG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck source=adguard-shield.conf
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
# ─── Worker PID-File ──────────────────────────────────────────────────────────
|
||||
WORKER_PID_FILE="/var/run/adguard-geoip-worker.pid"
|
||||
|
||||
# ─── GeoIP Cache ──────────────────────────────────────────────────────────────
|
||||
GEOIP_CACHE_DIR="${STATE_DIR}/geoip-cache"
|
||||
|
||||
# ─── MaxMind Auto-Download Verzeichnis ────────────────────────────────────────
|
||||
GEOIP_DB_DIR="${SCRIPT_DIR}/geoip"
|
||||
GEOIP_AUTO_DB="${GEOIP_DB_DIR}/GeoLite2-Country.mmdb"
|
||||
GEOIP_DB_UPDATE_INTERVAL=86400 # 24 Stunden (fest)
|
||||
|
||||
# ─── Logging (eigene Funktion, nutzt gleiche Log-Datei) ───────────────────────
|
||||
declare -A LOG_LEVELS=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3)
|
||||
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
local message="$*"
|
||||
local configured_level="${LOG_LEVEL:-INFO}"
|
||||
|
||||
if [[ ${LOG_LEVELS[$level]:-1} -ge ${LOG_LEVELS[$configured_level]:-1} ]]; then
|
||||
local timestamp
|
||||
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
local log_entry="[$timestamp] [$level] [GEOIP-WORKER] $message"
|
||||
echo "$log_entry" | tee -a "$LOG_FILE" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Ban-History ─────────────────────────────────────────────────────────────
|
||||
log_ban_history() {
|
||||
local action="$1"
|
||||
local client_ip="$2"
|
||||
local country="${3:-}"
|
||||
local reason="${4:-geoip}"
|
||||
local timestamp
|
||||
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
|
||||
if [[ ! -f "$BAN_HISTORY_FILE" ]]; then
|
||||
echo "# AdGuard Shield - Ban History" > "$BAN_HISTORY_FILE"
|
||||
echo "# Format: ZEITSTEMPEL | AKTION | CLIENT-IP | DOMAIN | ANFRAGEN | SPERRDAUER | PROTOKOLL | GRUND" >> "$BAN_HISTORY_FILE"
|
||||
echo "#──────────────────────────────────────────────────────────────────────────────────────────────────" >> "$BAN_HISTORY_FILE"
|
||||
fi
|
||||
|
||||
local duration="permanent"
|
||||
|
||||
printf "%-19s | %-6s | %-39s | %-30s | %-8s | %-10s | %-10s | %s\n" \
|
||||
"$timestamp" "$action" "$client_ip" "Land: ${country:-?}" "-" "$duration" "-" "$reason" \
|
||||
>> "$BAN_HISTORY_FILE"
|
||||
}
|
||||
|
||||
# ─── Verzeichnisse erstellen ──────────────────────────────────────────────────
|
||||
init_directories() {
|
||||
mkdir -p "$GEOIP_CACHE_DIR"
|
||||
mkdir -p "$GEOIP_DB_DIR"
|
||||
mkdir -p "$STATE_DIR"
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
}
|
||||
|
||||
# ─── Private IP-Adressen erkennen ────────────────────────────────────────────
|
||||
is_private_ip() {
|
||||
local ip="$1"
|
||||
|
||||
# IPv6 Loopback und Link-Local
|
||||
if [[ "$ip" == "::1" || "$ip" == fe80:* || "$ip" == fc00:* || "$ip" == fd00:* ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# IPv4 private Bereiche
|
||||
if [[ "$ip" =~ ^10\. || "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. || "$ip" =~ ^192\.168\. || "$ip" =~ ^127\. || "$ip" == "0.0.0.0" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# IPv4 CGNAT
|
||||
if [[ "$ip" =~ ^100\.(6[4-9]|[7-9][0-9]|1[0-1][0-9]|12[0-7])\. ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# ─── Whitelist Prüfung ───────────────────────────────────────────────────────
|
||||
is_whitelisted() {
|
||||
local ip="$1"
|
||||
IFS=',' read -ra wl_entries <<< "$WHITELIST"
|
||||
for entry in "${wl_entries[@]}"; do
|
||||
entry=$(echo "$entry" | xargs) # trim
|
||||
if [[ "$ip" == "$entry" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Externe Whitelist prüfen
|
||||
local ext_wl_file="${EXTERNAL_WHITELIST_CACHE_DIR:-/var/lib/adguard-shield/external-whitelist}/resolved_ips.txt"
|
||||
if [[ -f "$ext_wl_file" ]] && grep -qxF "$ip" "$ext_wl_file" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# ─── MaxMind GeoLite2 Auto-Download & Update ─────────────────────────────────
|
||||
# Lädt die GeoLite2-Country.mmdb herunter, wenn GEOIP_LICENSE_KEY gesetzt ist
|
||||
# und kein eigener GEOIP_MMDB_PATH angegeben wurde.
|
||||
# Aktualisiert automatisch alle 24 Stunden.
|
||||
update_maxmind_db() {
|
||||
local license_key="${GEOIP_LICENSE_KEY:-}"
|
||||
|
||||
# Kein License-Key → nichts zu tun
|
||||
if [[ -z "$license_key" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# User hat eigenen Pfad gesetzt → kein Auto-Download
|
||||
if [[ -n "${GEOIP_MMDB_PATH:-}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Prüfen ob Update nötig (alle 24h)
|
||||
if [[ -f "$GEOIP_AUTO_DB" ]]; then
|
||||
local db_age
|
||||
db_age=$(( $(date '+%s') - $(stat -c '%Y' "$GEOIP_AUTO_DB" 2>/dev/null || stat -f '%m' "$GEOIP_AUTO_DB" 2>/dev/null || echo "0") ))
|
||||
if [[ "$db_age" -lt "$GEOIP_DB_UPDATE_INTERVAL" ]]; then
|
||||
log "DEBUG" "MaxMind DB ist aktuell (Alter: $((db_age / 3600))h, nächstes Update in $(( (GEOIP_DB_UPDATE_INTERVAL - db_age) / 3600 ))h)"
|
||||
return 0
|
||||
fi
|
||||
log "INFO" "MaxMind DB ist älter als 24h – starte Update..."
|
||||
else
|
||||
log "INFO" "MaxMind DB nicht vorhanden – starte Erstdownload..."
|
||||
fi
|
||||
|
||||
# Download-URL zusammenbauen (MaxMind Permalink)
|
||||
local download_url="https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${license_key}&suffix=tar.gz"
|
||||
local tmp_file="${GEOIP_DB_DIR}/GeoLite2-Country.tar.gz"
|
||||
local tmp_extract="${GEOIP_DB_DIR}/extract_tmp"
|
||||
|
||||
# Herunterladen
|
||||
local http_code
|
||||
http_code=$(curl -s -o "$tmp_file" -w "%{http_code}" \
|
||||
--connect-timeout 10 \
|
||||
--max-time 60 \
|
||||
"$download_url" 2>/dev/null) || true
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
rm -f "$tmp_file"
|
||||
case "$http_code" in
|
||||
401) log "ERROR" "MaxMind Download fehlgeschlagen: Ungültiger License-Key (HTTP 401)" ;;
|
||||
403) log "ERROR" "MaxMind Download fehlgeschlagen: Zugriff verweigert (HTTP 403) – License-Key prüfen" ;;
|
||||
*) log "ERROR" "MaxMind Download fehlgeschlagen (HTTP ${http_code:-timeout})" ;;
|
||||
esac
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Entpacken
|
||||
rm -rf "$tmp_extract"
|
||||
mkdir -p "$tmp_extract"
|
||||
|
||||
if ! tar -xzf "$tmp_file" -C "$tmp_extract" 2>/dev/null; then
|
||||
log "ERROR" "MaxMind DB: tar-Archiv konnte nicht entpackt werden"
|
||||
rm -f "$tmp_file"
|
||||
rm -rf "$tmp_extract"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# .mmdb Datei finden und verschieben
|
||||
local mmdb_file
|
||||
mmdb_file=$(find "$tmp_extract" -name 'GeoLite2-Country.mmdb' -type f 2>/dev/null | head -1)
|
||||
|
||||
if [[ -z "$mmdb_file" || ! -f "$mmdb_file" ]]; then
|
||||
log "ERROR" "MaxMind DB: GeoLite2-Country.mmdb nicht im Archiv gefunden"
|
||||
rm -f "$tmp_file"
|
||||
rm -rf "$tmp_extract"
|
||||
return 1
|
||||
fi
|
||||
|
||||
mv "$mmdb_file" "$GEOIP_AUTO_DB"
|
||||
rm -f "$tmp_file"
|
||||
rm -rf "$tmp_extract"
|
||||
|
||||
log "INFO" "MaxMind GeoLite2-Country DB erfolgreich aktualisiert: $GEOIP_AUTO_DB"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ─── Effektiven MMDB-Pfad ermitteln ──────────────────────────────────────────
|
||||
# Priorität: GEOIP_MMDB_PATH (User) > Auto-Download > leer (Fallback auf geoiplookup)
|
||||
resolve_mmdb_path() {
|
||||
# User hat eigenen Pfad gesetzt
|
||||
if [[ -n "${GEOIP_MMDB_PATH:-}" && -f "${GEOIP_MMDB_PATH:-}" ]]; then
|
||||
echo "$GEOIP_MMDB_PATH"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Auto-Download DB vorhanden
|
||||
if [[ -f "$GEOIP_AUTO_DB" ]]; then
|
||||
echo "$GEOIP_AUTO_DB"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Kein MMDB verfügbar
|
||||
echo ""
|
||||
return 1
|
||||
}
|
||||
|
||||
# ─── GeoIP Lookup ────────────────────────────────────────────────────────────
|
||||
# Gibt den ISO 3166-1 Alpha-2 Ländercode zurück (z.B. "DE", "US", "CN")
|
||||
# Nutzt Cache um wiederholte Lookups zu vermeiden
|
||||
geoip_lookup() {
|
||||
local ip="$1"
|
||||
local cache_file="${GEOIP_CACHE_DIR}/${ip//[:\/]/_}.country"
|
||||
|
||||
# Cache prüfen (max 24 Stunden alt)
|
||||
if [[ -f "$cache_file" ]]; then
|
||||
local cache_age
|
||||
cache_age=$(( $(date '+%s') - $(stat -c '%Y' "$cache_file" 2>/dev/null || stat -f '%m' "$cache_file" 2>/dev/null || echo "0") ))
|
||||
if [[ "$cache_age" -lt 86400 ]]; then
|
||||
cat "$cache_file"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
local country_code=""
|
||||
|
||||
# Effektiven MMDB-Pfad ermitteln (User-Pfad oder Auto-Download)
|
||||
local effective_mmdb
|
||||
effective_mmdb=$(resolve_mmdb_path 2>/dev/null) || true
|
||||
|
||||
# Methode 1: MaxMind mmdbinspect (bevorzugt, genauer)
|
||||
if [[ -n "$effective_mmdb" && -f "$effective_mmdb" ]] && command -v mmdbinspect &>/dev/null; then
|
||||
country_code=$(mmdbinspect -db "$effective_mmdb" -ip "$ip" 2>/dev/null \
|
||||
| jq -r '.[0].Records[0].Record.country.iso_code // empty' 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
# Methode 2: geoiplookup (GeoIP Legacy)
|
||||
if [[ -z "$country_code" ]] && command -v geoiplookup &>/dev/null; then
|
||||
if [[ "$ip" == *:* ]]; then
|
||||
# IPv6
|
||||
if command -v geoiplookup6 &>/dev/null; then
|
||||
country_code=$(geoiplookup6 "$ip" 2>/dev/null \
|
||||
| grep -oP '(?<=: )[A-Z]{2}(?=,)' | head -1 || true)
|
||||
fi
|
||||
else
|
||||
# IPv4
|
||||
country_code=$(geoiplookup "$ip" 2>/dev/null \
|
||||
| grep -oP '(?<=: )[A-Z]{2}(?=,)' | head -1 || true)
|
||||
fi
|
||||
fi
|
||||
|
||||
# Methode 3: mmdblookup (libmaxminddb)
|
||||
if [[ -z "$country_code" && -n "$effective_mmdb" && -f "$effective_mmdb" ]] && command -v mmdblookup &>/dev/null; then
|
||||
country_code=$(mmdblookup --file "$effective_mmdb" --ip "$ip" country iso_code 2>/dev/null \
|
||||
| grep -oP '"[A-Z]{2}"' | tr -d '"' | head -1 || true)
|
||||
fi
|
||||
|
||||
if [[ -n "$country_code" ]]; then
|
||||
echo "$country_code" > "$cache_file"
|
||||
echo "$country_code"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Unbekannt – nicht cachen (könnte temporärer Fehler sein)
|
||||
echo ""
|
||||
return 1
|
||||
}
|
||||
|
||||
# ─── GeoIP Prüfung: Soll eine IP gesperrt werden? ────────────────────────────
|
||||
# Return 0 = sperren, Return 1 = erlauben
|
||||
should_block_by_geoip() {
|
||||
local country_code="$1"
|
||||
local mode="${GEOIP_MODE:-blocklist}"
|
||||
local countries="${GEOIP_COUNTRIES:-}"
|
||||
|
||||
[[ -z "$country_code" || -z "$countries" ]] && return 1
|
||||
|
||||
# Länder-Liste in Array umwandeln
|
||||
IFS=',' read -ra country_list <<< "$countries"
|
||||
|
||||
local found=false
|
||||
for c in "${country_list[@]}"; do
|
||||
c=$(echo "$c" | xargs | tr '[:lower:]' '[:upper:]') # trim + uppercase
|
||||
if [[ "$country_code" == "$c" ]]; then
|
||||
found=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$mode" == "blocklist" ]]; then
|
||||
# Blocklist-Modus: Sperren wenn Land in der Liste
|
||||
[[ "$found" == "true" ]] && return 0 || return 1
|
||||
elif [[ "$mode" == "allowlist" ]]; then
|
||||
# Allowlist-Modus: Sperren wenn Land NICHT in der Liste
|
||||
[[ "$found" == "true" ]] && return 1 || return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# ─── IP via iptables sperren ─────────────────────────────────────────────────
|
||||
ban_ip_geoip() {
|
||||
local client_ip="$1"
|
||||
local country_code="$2"
|
||||
local mode="${GEOIP_MODE:-blocklist}"
|
||||
|
||||
# Prüfen ob bereits gesperrt
|
||||
local state_file="${STATE_DIR}/${client_ip//[:\/]/_}.ban"
|
||||
if [[ -f "$state_file" ]]; then
|
||||
log "DEBUG" "GeoIP: $client_ip ist bereits gesperrt"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# GeoIP-Sperren sind immer permanent
|
||||
local ban_until=0
|
||||
local ban_until_display="PERMANENT"
|
||||
|
||||
local reason_text
|
||||
if [[ "$mode" == "blocklist" ]]; then
|
||||
reason_text="geoip-blocklist (Land: $country_code)"
|
||||
else
|
||||
reason_text="geoip-allowlist (Land: $country_code)"
|
||||
fi
|
||||
|
||||
log "WARN" "GeoIP SPERRE: $client_ip (Land: $country_code, Modus: $mode) PERMANENT"
|
||||
|
||||
# iptables Regel setzen
|
||||
if [[ "$client_ip" == *:* ]]; then
|
||||
ip6tables -I "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true
|
||||
else
|
||||
iptables -I "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# State-Datei erstellen
|
||||
cat > "$state_file" << EOF
|
||||
CLIENT_IP=$client_ip
|
||||
DOMAIN=GeoIP:${country_code}
|
||||
COUNT=-
|
||||
BAN_TIME=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
BAN_UNTIL_EPOCH=0
|
||||
BAN_UNTIL=PERMANENT
|
||||
BAN_DURATION=0
|
||||
OFFENSE_LEVEL=0
|
||||
IS_PERMANENT=true
|
||||
REASON=geoip
|
||||
PROTOCOL=-
|
||||
GEOIP_COUNTRY=$country_code
|
||||
GEOIP_MODE=$mode
|
||||
EOF
|
||||
|
||||
# Ban-History
|
||||
log_ban_history "BAN" "$client_ip" "$country_code" "$reason_text"
|
||||
|
||||
# Benachrichtigung senden
|
||||
if [[ "${GEOIP_NOTIFY:-true}" == "true" && "${NOTIFY_ENABLED:-false}" == "true" ]]; then
|
||||
send_geoip_notification "ban" "$client_ip" "$country_code" "PERMANENT" "$mode"
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── GeoIP Benachrichtigung ──────────────────────────────────────────────────
|
||||
send_geoip_notification() {
|
||||
local action="$1"
|
||||
local client_ip="$2"
|
||||
local country_code="$3"
|
||||
local duration="${4:-PERMANENT}"
|
||||
local mode="${5:-blocklist}"
|
||||
local my_hostname
|
||||
my_hostname=$(hostname)
|
||||
|
||||
local title="🌍 🛡️ AdGuard Shield"
|
||||
local mode_label
|
||||
[[ "$mode" == "blocklist" ]] && mode_label="Blocklist" || mode_label="Allowlist"
|
||||
|
||||
local message="🌍 AdGuard Shield GeoIP-Sperre auf ${my_hostname}
|
||||
---
|
||||
IP: ${client_ip}
|
||||
Land: ${country_code}
|
||||
Modus: ${mode_label}
|
||||
Dauer: ${duration}
|
||||
|
||||
Whois: https://www.whois.com/whois/${client_ip}
|
||||
AbuseIPDB: https://www.abuseipdb.com/check/${client_ip}"
|
||||
|
||||
case "${NOTIFY_TYPE:-}" in
|
||||
discord)
|
||||
local json_payload
|
||||
json_payload=$(jq -nc --arg msg "$message" '{content: $msg}')
|
||||
curl -s -H "Content-Type: application/json" \
|
||||
-d "$json_payload" \
|
||||
"$NOTIFY_WEBHOOK_URL" &>/dev/null &
|
||||
;;
|
||||
slack)
|
||||
local json_payload
|
||||
json_payload=$(jq -nc --arg msg "$message" '{text: $msg}')
|
||||
curl -s -H "Content-Type: application/json" \
|
||||
-d "$json_payload" \
|
||||
"$NOTIFY_WEBHOOK_URL" &>/dev/null &
|
||||
;;
|
||||
gotify)
|
||||
local clean_message
|
||||
clean_message=$(echo "$message" | sed 's/\*\*//g')
|
||||
curl -s -X POST "$NOTIFY_WEBHOOK_URL" \
|
||||
-F "title=${title}" \
|
||||
-F "message=${clean_message}" \
|
||||
-F "priority=5" &>/dev/null &
|
||||
;;
|
||||
ntfy)
|
||||
local ntfy_url="${NTFY_SERVER_URL:-https://ntfy.sh}"
|
||||
local -a curl_args=(
|
||||
-s -X POST
|
||||
"${ntfy_url}/${NTFY_TOPIC}"
|
||||
-H "Title: 🛡️ AdGuard Shield GeoIP"
|
||||
-H "Priority: ${NTFY_PRIORITY:-4}"
|
||||
-H "Tags: globe_with_meridians,ban"
|
||||
-d "$message"
|
||||
)
|
||||
[[ -n "${NTFY_TOKEN:-}" ]] && curl_args+=(-H "Authorization: Bearer ${NTFY_TOKEN}")
|
||||
curl "${curl_args[@]}" &>/dev/null &
|
||||
;;
|
||||
generic)
|
||||
local json_payload
|
||||
json_payload=$(jq -nc --arg msg "$message" --arg cl "$client_ip" --arg cc "$country_code" \
|
||||
'{message: $msg, action: "geoip-ban", client: $cl, country: $cc}')
|
||||
curl -s -H "Content-Type: application/json" \
|
||||
-d "$json_payload" \
|
||||
"$NOTIFY_WEBHOOK_URL" &>/dev/null &
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── iptables Chain Setup ────────────────────────────────────────────────────
|
||||
setup_iptables_chain() {
|
||||
if ! iptables -n -L "$IPTABLES_CHAIN" &>/dev/null; then
|
||||
iptables -N "$IPTABLES_CHAIN"
|
||||
for port in $BLOCKED_PORTS; do
|
||||
iptables -I INPUT -p tcp --dport "$port" -j "$IPTABLES_CHAIN"
|
||||
iptables -I INPUT -p udp --dport "$port" -j "$IPTABLES_CHAIN"
|
||||
done
|
||||
fi
|
||||
if ! ip6tables -n -L "$IPTABLES_CHAIN" &>/dev/null; then
|
||||
ip6tables -N "$IPTABLES_CHAIN"
|
||||
for port in $BLOCKED_PORTS; do
|
||||
ip6tables -I INPUT -p tcp --dport "$port" -j "$IPTABLES_CHAIN"
|
||||
ip6tables -I INPUT -p udp --dport "$port" -j "$IPTABLES_CHAIN"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── GeoIP-Tools Verfügbarkeit prüfen ────────────────────────────────────────
|
||||
check_geoip_tools() {
|
||||
# Effektiven MMDB-Pfad ermitteln
|
||||
local effective_mmdb
|
||||
effective_mmdb=$(resolve_mmdb_path 2>/dev/null) || true
|
||||
|
||||
# mmdbinspect + MMDB
|
||||
if [[ -n "$effective_mmdb" && -f "$effective_mmdb" ]]; then
|
||||
if command -v mmdbinspect &>/dev/null; then
|
||||
echo "mmdbinspect"
|
||||
return 0
|
||||
elif command -v mmdblookup &>/dev/null; then
|
||||
echo "mmdblookup"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# geoiplookup (Legacy GeoIP)
|
||||
if command -v geoiplookup &>/dev/null; then
|
||||
echo "geoiplookup"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "none"
|
||||
return 1
|
||||
}
|
||||
|
||||
# ─── Client-IPs aus AdGuard API extrahieren ──────────────────────────────────
|
||||
get_active_clients() {
|
||||
local response
|
||||
response=$(curl -s -u "${ADGUARD_USER}:${ADGUARD_PASS}" \
|
||||
--connect-timeout 5 \
|
||||
--max-time 10 \
|
||||
-k "${ADGUARD_URL}/control/querylog?limit=${API_QUERY_LIMIT:-500}&response_status=all" 2>/dev/null)
|
||||
|
||||
if [[ -z "$response" || "$response" == "null" ]]; then
|
||||
log "ERROR" "Keine Antwort von AdGuard Home API"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Eindeutige Client-IPs extrahieren
|
||||
echo "$response" | jq -r '.data // [] | [.[].client // .[].client_info.ip] | unique | .[]' 2>/dev/null | sort -u
|
||||
}
|
||||
|
||||
# ─── Auto-Unban: GeoIP-Sperren aufheben bei Konfigurationsänderung ────────────
|
||||
# Prüft alle bestehenden GeoIP-Sperren und hebt sie auf, wenn:
|
||||
# - Das Land nicht mehr in GEOIP_COUNTRIES steht
|
||||
# - Der Modus gewechselt wurde (blocklist ↔ allowlist)
|
||||
# - GeoIP deaktiviert wurde
|
||||
auto_unban_geoip() {
|
||||
local unban_count=0
|
||||
|
||||
for f in "${STATE_DIR}"/*.ban; do
|
||||
[[ -f "$f" ]] || continue
|
||||
|
||||
local reason
|
||||
reason=$(grep '^REASON=' "$f" | cut -d= -f2 || true)
|
||||
[[ "$reason" != "geoip" ]] && continue
|
||||
|
||||
local client_ip country_code old_mode
|
||||
client_ip=$(grep '^CLIENT_IP=' "$f" | cut -d= -f2 || true)
|
||||
country_code=$(grep '^GEOIP_COUNTRY=' "$f" | cut -d= -f2 || true)
|
||||
old_mode=$(grep '^GEOIP_MODE=' "$f" | cut -d= -f2 || true)
|
||||
|
||||
local should_unban=false
|
||||
|
||||
# GeoIP deaktiviert → alle GeoIP-Sperren aufheben
|
||||
if [[ "${GEOIP_ENABLED:-false}" != "true" ]]; then
|
||||
should_unban=true
|
||||
# Modus gewechselt → alle GeoIP-Sperren aufheben und neu prüfen
|
||||
elif [[ -n "$old_mode" && "$old_mode" != "${GEOIP_MODE:-blocklist}" ]]; then
|
||||
should_unban=true
|
||||
# Prüfen ob das Land nach aktueller Konfiguration noch gesperrt sein soll
|
||||
elif [[ -n "$country_code" ]] && ! should_block_by_geoip "$country_code"; then
|
||||
should_unban=true
|
||||
fi
|
||||
|
||||
if [[ "$should_unban" == "true" ]]; then
|
||||
log "INFO" "GeoIP Auto-Unban: $client_ip (Land: ${country_code:-?}, war: ${old_mode:-?})"
|
||||
|
||||
# iptables Regel entfernen
|
||||
if [[ "$client_ip" == *:* ]]; then
|
||||
ip6tables -D "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true
|
||||
else
|
||||
iptables -D "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true
|
||||
fi
|
||||
|
||||
rm -f "$f"
|
||||
log_ban_history "UNBAN" "$client_ip" "$country_code" "geoip-auto-unban"
|
||||
unban_count=$((unban_count + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $unban_count -gt 0 ]]; then
|
||||
log "INFO" "GeoIP Auto-Unban: $unban_count Sperren aufgehoben (Länderliste/Modus geändert)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Einmaliger GeoIP-Sync ──────────────────────────────────────────────────
|
||||
sync_geoip() {
|
||||
# Auto-Unban zuerst: bestehende Sperren prüfen, die nicht mehr zur Config passen
|
||||
auto_unban_geoip
|
||||
|
||||
if [[ "${GEOIP_ENABLED:-false}" != "true" ]]; then
|
||||
log "INFO" "GeoIP ist deaktiviert"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# MaxMind DB automatisch herunterladen/aktualisieren (falls License-Key gesetzt)
|
||||
update_maxmind_db || true
|
||||
|
||||
local countries="${GEOIP_COUNTRIES:-}"
|
||||
if [[ -z "$countries" ]]; then
|
||||
log "WARN" "GeoIP: Keine Länder konfiguriert (GEOIP_COUNTRIES ist leer)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local tool
|
||||
tool=$(check_geoip_tools) || {
|
||||
log "ERROR" "GeoIP: Kein GeoIP-Tool verfügbar. Installiere geoip-bin oder mmdbinspect."
|
||||
return 1
|
||||
}
|
||||
log "INFO" "GeoIP-Sync gestartet (Tool: $tool, Modus: ${GEOIP_MODE:-blocklist}, Länder: $countries)"
|
||||
|
||||
# Client-IPs aus der API holen
|
||||
local clients
|
||||
clients=$(get_active_clients) || {
|
||||
log "ERROR" "GeoIP: Konnte aktive Clients nicht ermitteln"
|
||||
return 1
|
||||
}
|
||||
|
||||
local checked=0
|
||||
local blocked=0
|
||||
local skipped=0
|
||||
|
||||
while IFS= read -r client_ip; do
|
||||
[[ -z "$client_ip" || "$client_ip" == "null" ]] && continue
|
||||
|
||||
# Private IPs überspringen
|
||||
if [[ "${GEOIP_SKIP_PRIVATE:-true}" == "true" ]] && is_private_ip "$client_ip"; then
|
||||
log "DEBUG" "GeoIP: Private IP übersprungen: $client_ip"
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Whitelist prüfen
|
||||
if is_whitelisted "$client_ip"; then
|
||||
log "DEBUG" "GeoIP: Whitelisted IP übersprungen: $client_ip"
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Bereits gesperrt?
|
||||
local state_file="${STATE_DIR}/${client_ip//[:\/]/_}.ban"
|
||||
if [[ -f "$state_file" ]]; then
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
checked=$((checked + 1))
|
||||
|
||||
# GeoIP Lookup
|
||||
local country_code
|
||||
country_code=$(geoip_lookup "$client_ip") || true
|
||||
|
||||
if [[ -z "$country_code" ]]; then
|
||||
log "DEBUG" "GeoIP: Kein Ergebnis für $client_ip"
|
||||
continue
|
||||
fi
|
||||
|
||||
log "DEBUG" "GeoIP: $client_ip → $country_code"
|
||||
|
||||
# Prüfen ob gesperrt werden soll
|
||||
if should_block_by_geoip "$country_code"; then
|
||||
ban_ip_geoip "$client_ip" "$country_code"
|
||||
blocked=$((blocked + 1))
|
||||
fi
|
||||
done <<< "$clients"
|
||||
|
||||
log "INFO" "GeoIP-Sync abgeschlossen: $checked geprüft, $blocked gesperrt, $skipped übersprungen"
|
||||
}
|
||||
|
||||
# ─── Worker-Hauptschleife ────────────────────────────────────────────────────
|
||||
start_worker() {
|
||||
if [[ "${GEOIP_ENABLED:-false}" != "true" ]]; then
|
||||
log "DEBUG" "GeoIP-Worker ist deaktiviert"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# PID schreiben
|
||||
echo $$ > "$WORKER_PID_FILE"
|
||||
trap 'rm -f "$WORKER_PID_FILE"; exit 0' SIGTERM SIGINT SIGHUP
|
||||
|
||||
local interval="${GEOIP_CHECK_INTERVAL:-0}"
|
||||
[[ "$interval" -le 0 ]] && interval="${CHECK_INTERVAL:-10}"
|
||||
|
||||
log "INFO" "GeoIP-Worker gestartet (PID: $$, Intervall: ${interval}s)"
|
||||
|
||||
# Beim Start: MaxMind DB herunterladen/aktualisieren (falls License-Key gesetzt)
|
||||
update_maxmind_db || true
|
||||
|
||||
while true; do
|
||||
sync_geoip
|
||||
sleep "$interval"
|
||||
done
|
||||
}
|
||||
|
||||
# ─── Status anzeigen ─────────────────────────────────────────────────────────
|
||||
show_status() {
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo " AdGuard Shield - GeoIP Status"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
if [[ "${GEOIP_ENABLED:-false}" != "true" ]]; then
|
||||
echo " ℹ️ GeoIP ist deaktiviert"
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
|
||||
echo " Modus: ${GEOIP_MODE:-blocklist}"
|
||||
echo " Länder: ${GEOIP_COUNTRIES:-<keine>}"
|
||||
echo " Sperrdauer: PERMANENT (Auto-Unban bei Änderung der Länderliste)"
|
||||
echo " Private IPs überspringen: ${GEOIP_SKIP_PRIVATE:-true}"
|
||||
echo ""
|
||||
|
||||
# MaxMind DB Info
|
||||
local eff_mmdb
|
||||
eff_mmdb=$(resolve_mmdb_path)
|
||||
if [[ -n "${GEOIP_MMDB_PATH:-}" ]]; then
|
||||
echo " MMDB-Pfad: ${GEOIP_MMDB_PATH} (manuell konfiguriert)"
|
||||
elif [[ -n "${GEOIP_LICENSE_KEY:-}" ]]; then
|
||||
echo " MMDB-Pfad: ${GEOIP_AUTO_DB} (Auto-Download)"
|
||||
if [[ -f "${GEOIP_AUTO_DB}" ]]; then
|
||||
local db_age db_age_h
|
||||
db_age=$(( $(date +%s) - $(stat -c %Y "${GEOIP_AUTO_DB}" 2>/dev/null || echo 0) ))
|
||||
db_age_h=$(( db_age / 3600 ))
|
||||
echo " DB-Alter: ${db_age_h}h (Update alle 24h)"
|
||||
else
|
||||
echo " DB-Status: ⚠️ Noch nicht heruntergeladen"
|
||||
fi
|
||||
elif [[ -n "$eff_mmdb" ]]; then
|
||||
echo " MMDB-Pfad: ${eff_mmdb}"
|
||||
else
|
||||
echo " MMDB-Pfad: <nicht konfiguriert> (Fallback auf geoiplookup)"
|
||||
fi
|
||||
echo " License-Key: $(if [[ -n "${GEOIP_LICENSE_KEY:-}" ]]; then echo "✅ konfiguriert"; else echo "❌ nicht gesetzt (kein Auto-Download)"; fi)"
|
||||
echo ""
|
||||
|
||||
# GeoIP Tools prüfen
|
||||
echo " GeoIP Tools:"
|
||||
local tool
|
||||
tool=$(check_geoip_tools 2>/dev/null) || tool="none"
|
||||
case "$tool" in
|
||||
mmdbinspect) echo " ✅ mmdbinspect mit MaxMind DB" ;;
|
||||
mmdblookup) echo " ✅ mmdblookup mit MaxMind DB" ;;
|
||||
geoiplookup) echo " ✅ geoiplookup (Legacy GeoIP)" ;;
|
||||
none) echo " ❌ Kein GeoIP-Tool gefunden!" ;;
|
||||
esac
|
||||
echo ""
|
||||
|
||||
# Worker-Status
|
||||
if [[ -f "$WORKER_PID_FILE" ]]; then
|
||||
local wpid
|
||||
wpid=$(cat "$WORKER_PID_FILE")
|
||||
if kill -0 "$wpid" 2>/dev/null; then
|
||||
echo " Worker: Läuft (PID: $wpid)"
|
||||
else
|
||||
echo " Worker: Abgestürzt (PID: $wpid existiert nicht mehr)"
|
||||
fi
|
||||
else
|
||||
echo " Worker: Nicht gestartet"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# GeoIP-Sperren anzeigen
|
||||
local geoip_bans=0
|
||||
if [[ -d "$STATE_DIR" ]]; then
|
||||
for f in "${STATE_DIR}"/*.ban; do
|
||||
[[ -f "$f" ]] || continue
|
||||
local reason
|
||||
reason=$(grep '^REASON=' "$f" | cut -d= -f2 || true)
|
||||
if [[ "$reason" == "geoip" ]]; then
|
||||
geoip_bans=$((geoip_bans + 1))
|
||||
local s_ip s_country s_until
|
||||
s_ip=$(grep '^CLIENT_IP=' "$f" | cut -d= -f2 || true)
|
||||
s_country=$(grep '^GEOIP_COUNTRY=' "$f" | cut -d= -f2 || true)
|
||||
s_until=$(grep '^BAN_UNTIL=' "$f" | cut -d= -f2 || true)
|
||||
echo " 🌍 $s_ip → Land: ${s_country:-?} (bis: ${s_until:-?})"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ $geoip_bans -eq 0 ]]; then
|
||||
echo " Keine aktiven GeoIP-Sperren"
|
||||
else
|
||||
echo ""
|
||||
echo " Gesamt: $geoip_bans aktive GeoIP-Sperren"
|
||||
fi
|
||||
|
||||
# Cache-Statistik
|
||||
if [[ -d "$GEOIP_CACHE_DIR" ]]; then
|
||||
local cache_count
|
||||
cache_count=$(find "$GEOIP_CACHE_DIR" -name '*.country' -type f 2>/dev/null | wc -l)
|
||||
echo ""
|
||||
echo " Cache: $cache_count IP-Lookups zwischengespeichert"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
}
|
||||
|
||||
# ─── Einzelne IP nachschlagen ────────────────────────────────────────────────
|
||||
lookup_ip() {
|
||||
local ip="$1"
|
||||
|
||||
local eff_mmdb
|
||||
eff_mmdb=$(resolve_mmdb_path)
|
||||
|
||||
local tool
|
||||
tool=$(check_geoip_tools 2>/dev/null) || tool="none"
|
||||
|
||||
if [[ "$tool" == "none" ]]; then
|
||||
echo "❌ Kein GeoIP-Tool verfügbar."
|
||||
echo " Installiere geoip-bin: sudo apt install geoip-bin geoip-database"
|
||||
echo " Oder mmdbinspect mit MaxMind GeoLite2 DB"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local country_code
|
||||
country_code=$(geoip_lookup "$ip") || true
|
||||
|
||||
if [[ -z "$country_code" ]]; then
|
||||
echo "IP: $ip → Land: unbekannt (kein GeoIP-Ergebnis)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "IP: $ip → Land: $country_code (Tool: $tool)"
|
||||
[[ -n "$eff_mmdb" ]] && echo " MMDB: $eff_mmdb"
|
||||
|
||||
# Prüfen ob diese IP gesperrt werden würde
|
||||
if [[ "${GEOIP_ENABLED:-false}" == "true" && -n "${GEOIP_COUNTRIES:-}" ]]; then
|
||||
if should_block_by_geoip "$country_code"; then
|
||||
echo "→ Würde GESPERRT werden (Modus: ${GEOIP_MODE:-blocklist}, Länder: ${GEOIP_COUNTRIES})"
|
||||
else
|
||||
echo "→ Würde ERLAUBT werden (Modus: ${GEOIP_MODE:-blocklist}, Länder: ${GEOIP_COUNTRIES})"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Cache leeren ────────────────────────────────────────────────────────────
|
||||
flush_cache() {
|
||||
if [[ -d "$GEOIP_CACHE_DIR" ]]; then
|
||||
local count
|
||||
count=$(find "$GEOIP_CACHE_DIR" -name '*.country' -type f 2>/dev/null | wc -l)
|
||||
rm -f "${GEOIP_CACHE_DIR}"/*.country 2>/dev/null || true
|
||||
echo "✅ GeoIP-Cache geleert ($count Einträge entfernt)"
|
||||
log "INFO" "GeoIP-Cache geleert ($count Einträge)"
|
||||
else
|
||||
echo "ℹ️ GeoIP-Cache-Verzeichnis existiert nicht"
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── GeoIP-Sperren aufheben ─────────────────────────────────────────────────
|
||||
flush_geoip_bans() {
|
||||
local count=0
|
||||
if [[ -d "$STATE_DIR" ]]; then
|
||||
for f in "${STATE_DIR}"/*.ban; do
|
||||
[[ -f "$f" ]] || continue
|
||||
local reason
|
||||
reason=$(grep '^REASON=' "$f" | cut -d= -f2 || true)
|
||||
if [[ "$reason" == "geoip" ]]; then
|
||||
local client_ip
|
||||
client_ip=$(grep '^CLIENT_IP=' "$f" | cut -d= -f2 || true)
|
||||
|
||||
# iptables Regel entfernen
|
||||
if [[ "$client_ip" == *:* ]]; then
|
||||
ip6tables -D "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true
|
||||
else
|
||||
iptables -D "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true
|
||||
fi
|
||||
|
||||
rm -f "$f"
|
||||
log_ban_history "UNBAN" "$client_ip" "" "geoip-flush"
|
||||
count=$((count + 1))
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo "✅ $count GeoIP-Sperren aufgehoben"
|
||||
log "INFO" "$count GeoIP-Sperren aufgehoben (flush)"
|
||||
}
|
||||
|
||||
# ─── Hauptprogramm ──────────────────────────────────────────────────────────
|
||||
case "${1:-help}" in
|
||||
start)
|
||||
init_directories
|
||||
setup_iptables_chain
|
||||
start_worker
|
||||
;;
|
||||
sync)
|
||||
init_directories
|
||||
setup_iptables_chain
|
||||
sync_geoip
|
||||
;;
|
||||
status)
|
||||
init_directories
|
||||
show_status
|
||||
;;
|
||||
lookup)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Nutzung: $0 lookup <IP-Adresse>" >&2
|
||||
exit 1
|
||||
fi
|
||||
init_directories
|
||||
lookup_ip "$2"
|
||||
;;
|
||||
flush)
|
||||
init_directories
|
||||
flush_geoip_bans
|
||||
;;
|
||||
flush-cache)
|
||||
init_directories
|
||||
flush_cache
|
||||
;;
|
||||
stop)
|
||||
if [[ -f "$WORKER_PID_FILE" ]]; then
|
||||
local wpid
|
||||
wpid=$(cat "$WORKER_PID_FILE")
|
||||
if kill -0 "$wpid" 2>/dev/null; then
|
||||
kill "$wpid" 2>/dev/null || true
|
||||
rm -f "$WORKER_PID_FILE"
|
||||
echo "GeoIP-Worker gestoppt"
|
||||
else
|
||||
rm -f "$WORKER_PID_FILE"
|
||||
echo "GeoIP-Worker war nicht aktiv"
|
||||
fi
|
||||
else
|
||||
echo "GeoIP-Worker läuft nicht"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
cat << USAGE
|
||||
AdGuard Shield - GeoIP Worker
|
||||
|
||||
Nutzung: $0 {start|stop|sync|status|lookup|flush|flush-cache}
|
||||
|
||||
Befehle:
|
||||
start Startet den GeoIP-Worker (Hintergrundprozess)
|
||||
stop Stoppt den GeoIP-Worker
|
||||
sync Einmalige GeoIP-Prüfung aller aktiven Clients
|
||||
status Zeigt GeoIP-Status und aktive Sperren
|
||||
lookup <IP> GeoIP-Lookup für eine einzelne IP
|
||||
flush Alle GeoIP-Sperren aufheben
|
||||
flush-cache GeoIP-Lookup-Cache leeren
|
||||
|
||||
Konfiguration in: $CONFIG_FILE
|
||||
GEOIP_ENABLED=true/false
|
||||
GEOIP_MODE=blocklist/allowlist
|
||||
GEOIP_COUNTRIES="CN,RU,..."
|
||||
|
||||
USAGE
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
234
install.sh
234
install.sh
@@ -6,7 +6,7 @@
|
||||
# Lizenz: MIT
|
||||
###############################################################################
|
||||
|
||||
VERSION="0.3.0"
|
||||
VERSION="v0.8.1"
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -39,7 +39,13 @@ print_header() {
|
||||
echo -e "${NC}"
|
||||
echo -e "${GREEN} Version: ${VERSION}${NC}"
|
||||
echo -e "${BLUE} Autor: Patrick Asmus${NC}"
|
||||
echo -e
|
||||
echo -e "${BLUE} E-Mail: support@techniverse.net${NC}"
|
||||
echo -e "${BLUE} Web: https://www.patrick-asmus.de${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}───────────────────────────────────────────────────────────────────────────────────────────────────────────────${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE} Repo: https://git.techniverse.net/scriptos/adguard-shield${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════════════════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
@@ -64,6 +70,9 @@ print_help() {
|
||||
echo -e " ${GREEN}uninstall${NC} Vollständige Deinstallation"
|
||||
echo -e " Stoppt den Service, entfernt iptables-Regeln und"
|
||||
echo -e " löscht alle Dateien (optional Konfiguration behalten)."
|
||||
echo -e " Delegiert automatisch an den im Installationsverzeichnis"
|
||||
echo -e " liegenden Uninstaller — kein Behalten der Installationsdateien nötig."
|
||||
echo -e " Direkt ausführbar: ${CYAN}sudo bash $INSTALL_DIR/uninstall.sh${NC}"
|
||||
echo ""
|
||||
echo -e " ${GREEN}status${NC} Installationsstatus anzeigen"
|
||||
echo -e " Zeigt ob AdGuard Shield installiert ist, welche Version"
|
||||
@@ -77,8 +86,15 @@ print_help() {
|
||||
echo -e " ${CYAN}sudo bash install.sh uninstall${NC} # Deinstallation"
|
||||
echo -e " ${CYAN}sudo bash install.sh status${NC} # Status prüfen"
|
||||
echo ""
|
||||
echo -e "${BOLD}Service-Befehle:${NC}"
|
||||
echo -e " ${CYAN}sudo systemctl start adguard-shield${NC} # Service starten"
|
||||
echo -e " ${CYAN}sudo systemctl stop adguard-shield${NC} # Service stoppen"
|
||||
echo -e " ${CYAN}sudo systemctl restart adguard-shield${NC} # Service neustarten"
|
||||
echo -e " ${CYAN}sudo systemctl status adguard-shield${NC} # Service-Status"
|
||||
echo -e " ${CYAN}sudo journalctl -u adguard-shield -f${NC} # Logs live verfolgen"
|
||||
echo ""
|
||||
echo -e "${BOLD}Monitor-Befehle (nach Installation):${NC}"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh start${NC} # Monitor starten"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh start${NC} # Monitor im Vordergrund starten"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh stop${NC} # Monitor stoppen"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh status${NC} # Status & aktive Sperren"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh history${NC} # Ban-History anzeigen"
|
||||
@@ -87,6 +103,11 @@ print_help() {
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh test${NC} # API-Verbindung testen"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh dry-run${NC} # Testmodus (nur loggen)"
|
||||
echo ""
|
||||
echo -e "${BOLD}Externe Whitelist-Befehle:${NC}"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh whitelist-status${NC} # Status der externen Whitelisten"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh whitelist-sync${NC} # Einmalige Synchronisation"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh whitelist-flush${NC} # Aufgelöste IPs entfernen"
|
||||
echo ""
|
||||
echo -e "${BOLD}iptables-Befehle:${NC}"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/iptables-helper.sh status${NC} # Firewall-Regeln anzeigen"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/iptables-helper.sh ban IP${NC} # IP manuell sperren"
|
||||
@@ -97,18 +118,32 @@ print_help() {
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/iptables-helper.sh save${NC} # Regeln speichern"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/iptables-helper.sh restore${NC} # Regeln wiederherstellen"
|
||||
echo ""
|
||||
echo -e "${BOLD}Service-Befehle:${NC}"
|
||||
echo -e " ${CYAN}sudo systemctl start adguard-shield${NC} # Service starten"
|
||||
echo -e " ${CYAN}sudo systemctl stop adguard-shield${NC} # Service stoppen"
|
||||
echo -e " ${CYAN}sudo systemctl restart adguard-shield${NC} # Service neustarten"
|
||||
echo -e " ${CYAN}sudo systemctl status adguard-shield${NC} # Service-Status"
|
||||
echo -e " ${CYAN}sudo journalctl -u adguard-shield -f${NC} # Logs live verfolgen"
|
||||
echo -e "${BOLD}Report-Befehle:${NC}"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/report-generator.sh status${NC} # Report-Konfiguration anzeigen"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/report-generator.sh send${NC} # Report sofort senden"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/report-generator.sh generate${NC} # Report als Datei generieren"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/report-generator.sh install${NC} # Cron-Job einrichten"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/report-generator.sh remove${NC} # Cron-Job entfernen"
|
||||
echo ""
|
||||
echo -e "${BOLD}Watchdog-Befehle:${NC}"
|
||||
echo -e " ${CYAN}sudo systemctl status adguard-shield-watchdog.timer${NC} # Watchdog-Status"
|
||||
echo -e " ${CYAN}sudo systemctl list-timers adguard-shield-watchdog.timer${NC} # Nächste Ausführung"
|
||||
echo -e " ${CYAN}sudo systemctl enable adguard-shield-watchdog.timer${NC} # Watchdog aktivieren"
|
||||
echo -e " ${CYAN}sudo systemctl disable adguard-shield-watchdog.timer${NC} # Watchdog deaktivieren"
|
||||
echo -e " ${CYAN}sudo journalctl -u adguard-shield-watchdog.service${NC} # Watchdog-Logs"
|
||||
echo ""
|
||||
echo -e "${BOLD}GeoIP-Befehle:${NC}"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh geoip-status${NC} # GeoIP-Status anzeigen"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh geoip-sync${NC} # Einmalige GeoIP-Prüfung"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh geoip-flush${NC} # Alle GeoIP-Sperren aufheben"
|
||||
echo -e " ${CYAN}sudo /opt/adguard-shield/adguard-shield.sh geoip-lookup IP${NC} # GeoIP-Lookup einer IP"
|
||||
echo ""
|
||||
echo -e "${BOLD}Voraussetzungen:${NC}"
|
||||
echo " - Linux Server (Debian/Ubuntu empfohlen)"
|
||||
echo " - Root-Zugriff (sudo)"
|
||||
echo " - AdGuard Home installiert und erreichbar"
|
||||
echo " - Pakete: curl, jq, iptables, gawk (werden bei Installation automatisch installiert)"
|
||||
echo " - GeoIP (optional): geoip-bin + geoip-database oder MaxMind GeoLite2 DB"
|
||||
echo ""
|
||||
echo -e "${BOLD}Dokumentation:${NC}"
|
||||
echo " https://git.techniverse.net/scriptos/adguard-shield"
|
||||
@@ -126,7 +161,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
|
||||
@@ -225,12 +260,29 @@ install_files() {
|
||||
cp "$SCRIPT_DIR/iptables-helper.sh" "$INSTALL_DIR/"
|
||||
cp "$SCRIPT_DIR/unban-expired.sh" "$INSTALL_DIR/"
|
||||
cp "$SCRIPT_DIR/external-blocklist-worker.sh" "$INSTALL_DIR/"
|
||||
cp "$SCRIPT_DIR/external-whitelist-worker.sh" "$INSTALL_DIR/"
|
||||
cp "$SCRIPT_DIR/report-generator.sh" "$INSTALL_DIR/"
|
||||
cp "$SCRIPT_DIR/adguard-shield-watchdog.sh" "$INSTALL_DIR/"
|
||||
cp "$SCRIPT_DIR/uninstall.sh" "$INSTALL_DIR/"
|
||||
cp "$SCRIPT_DIR/geoip-worker.sh" "$INSTALL_DIR/"
|
||||
cp "$SCRIPT_DIR/offense-cleanup-worker.sh" "$INSTALL_DIR/"
|
||||
|
||||
# Templates kopieren
|
||||
mkdir -p "$INSTALL_DIR/templates"
|
||||
cp "$SCRIPT_DIR/templates/report.html" "$INSTALL_DIR/templates/"
|
||||
cp "$SCRIPT_DIR/templates/report.txt" "$INSTALL_DIR/templates/"
|
||||
|
||||
# Ausführbar machen
|
||||
chmod +x "$INSTALL_DIR/adguard-shield.sh"
|
||||
chmod +x "$INSTALL_DIR/iptables-helper.sh"
|
||||
chmod +x "$INSTALL_DIR/unban-expired.sh"
|
||||
chmod +x "$INSTALL_DIR/external-blocklist-worker.sh"
|
||||
chmod +x "$INSTALL_DIR/external-whitelist-worker.sh"
|
||||
chmod +x "$INSTALL_DIR/report-generator.sh"
|
||||
chmod +x "$INSTALL_DIR/adguard-shield-watchdog.sh"
|
||||
chmod +x "$INSTALL_DIR/uninstall.sh"
|
||||
chmod +x "$INSTALL_DIR/geoip-worker.sh"
|
||||
chmod +x "$INSTALL_DIR/offense-cleanup-worker.sh"
|
||||
|
||||
echo -e " ✅ Dateien installiert"
|
||||
echo ""
|
||||
@@ -301,7 +353,7 @@ migrate_config() {
|
||||
echo -n "$current_comment_block" >> "$existing_conf"
|
||||
echo "$line" >> "$existing_conf"
|
||||
echo -e " ➕ Neuer Parameter hinzugefügt: ${GREEN}$key${NC}"
|
||||
((new_keys_added++))
|
||||
new_keys_added=$((new_keys_added + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -324,18 +376,22 @@ install_service() {
|
||||
echo -e "${YELLOW}Installiere systemd Service...${NC}"
|
||||
|
||||
cp "$SCRIPT_DIR/adguard-shield.service" "$SERVICE_FILE"
|
||||
cp "$SCRIPT_DIR/adguard-shield-watchdog.service" /etc/systemd/system/adguard-shield-watchdog.service
|
||||
cp "$SCRIPT_DIR/adguard-shield-watchdog.timer" /etc/systemd/system/adguard-shield-watchdog.timer
|
||||
systemctl daemon-reload
|
||||
|
||||
echo -e " ✅ Service-Datei installiert"
|
||||
echo -e " ✅ Service-Dateien installiert (inkl. Watchdog)"
|
||||
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"
|
||||
systemctl enable adguard-shield-watchdog.timer
|
||||
echo -e " ✅ Autostart aktiviert (inkl. Watchdog-Timer)"
|
||||
else
|
||||
systemctl disable adguard-shield.service 2>/dev/null || true
|
||||
systemctl disable adguard-shield-watchdog.timer 2>/dev/null || true
|
||||
echo -e " ℹ️ Autostart nicht aktiviert"
|
||||
echo -e " ${YELLOW}Später aktivieren mit: sudo systemctl enable adguard-shield${NC}"
|
||||
fi
|
||||
@@ -349,17 +405,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
|
||||
@@ -367,17 +423,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"
|
||||
|
||||
@@ -391,17 +447,60 @@ test_connection() {
|
||||
|
||||
source "$INSTALL_DIR/adguard-shield.conf"
|
||||
|
||||
local response
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
-u "${ADGUARD_USER}:${ADGUARD_PASS}" \
|
||||
--connect-timeout 5 \
|
||||
"${ADGUARD_URL}/control/querylog?limit=1" 2>/dev/null)
|
||||
# ── Schritt 1: Base-URL Erreichbarkeit prüfen ────────────────────────
|
||||
echo -e " ${CYAN}1)${NC} Prüfe Erreichbarkeit von ${BOLD}${ADGUARD_URL}${NC} ..."
|
||||
|
||||
if [[ "$response" == "200" ]]; then
|
||||
echo -e " ✅ Verbindung erfolgreich! (HTTP $response)"
|
||||
local base_http_code
|
||||
local base_curl_exit
|
||||
base_http_code=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
--connect-timeout 5 --max-time 10 \
|
||||
-k "${ADGUARD_URL}" 2>/dev/null) || base_curl_exit=$?
|
||||
base_curl_exit=${base_curl_exit:-0}
|
||||
|
||||
if [[ "$base_curl_exit" -ne 0 ]]; then
|
||||
# curl konnte keine Verbindung aufbauen
|
||||
echo -e " ❌ Base-URL nicht erreichbar! (curl Exit-Code: $base_curl_exit)"
|
||||
case "$base_curl_exit" in
|
||||
6) echo -e " ${YELLOW}→ DNS-Auflösung fehlgeschlagen. Hostname prüfen!${NC}" ;;
|
||||
7) echo -e " ${YELLOW}→ Verbindung abgelehnt. Läuft AdGuard Home? Port korrekt?${NC}" ;;
|
||||
28) echo -e " ${YELLOW}→ Timeout. Host nicht erreichbar oder Firewall blockiert.${NC}" ;;
|
||||
35|51|60) echo -e " ${YELLOW}→ SSL/TLS-Fehler. Zertifikat oder HTTPS-Konfiguration prüfen.${NC}" ;;
|
||||
*) echo -e " ${YELLOW}→ Unbekannter Fehler. Manuell testen: curl -v ${ADGUARD_URL}${NC}" ;;
|
||||
esac
|
||||
echo ""
|
||||
echo -e " ${YELLOW}Troubleshooting:${NC}"
|
||||
echo -e " curl -ikv ${ADGUARD_URL}"
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$base_http_code" == "000" ]]; then
|
||||
echo -e " ❌ Base-URL nicht erreichbar (keine HTTP-Antwort)"
|
||||
echo -e " ${YELLOW}→ Manuell testen: curl -ikv ${ADGUARD_URL}${NC}"
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo -e " ✅ Base-URL erreichbar (HTTP $base_http_code)"
|
||||
|
||||
# ── Schritt 2: API-Endpunkt mit Authentifizierung testen ─────────────
|
||||
echo -e " ${CYAN}2)${NC} Teste API-Authentifizierung ..."
|
||||
|
||||
local api_response
|
||||
api_response=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
-u "${ADGUARD_USER}:${ADGUARD_PASS}" \
|
||||
--connect-timeout 5 --max-time 10 \
|
||||
-k "${ADGUARD_URL}/control/querylog?limit=1" 2>/dev/null)
|
||||
|
||||
if [[ "$api_response" == "200" ]]; then
|
||||
echo -e " ✅ API-Authentifizierung erfolgreich! (HTTP $api_response)"
|
||||
elif [[ "$api_response" == "401" || "$api_response" == "403" ]]; then
|
||||
echo -e " ❌ Authentifizierung fehlgeschlagen (HTTP $api_response)"
|
||||
echo -e " ${YELLOW}→ Benutzername oder Passwort falsch!${NC}"
|
||||
echo -e " ${YELLOW}→ Prüfe ADGUARD_USER und ADGUARD_PASS in: $INSTALL_DIR/adguard-shield.conf${NC}"
|
||||
else
|
||||
echo -e " ❌ Verbindung fehlgeschlagen (HTTP $response)"
|
||||
echo -e " ${YELLOW}Bitte prüfe URL und Zugangsdaten in: $INSTALL_DIR/adguard-shield.conf${NC}"
|
||||
echo -e " ❌ API-Verbindung fehlgeschlagen (HTTP $api_response)"
|
||||
echo -e " ${YELLOW}→ Bitte prüfe URL und Zugangsdaten in: $INSTALL_DIR/adguard-shield.conf${NC}"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
@@ -425,6 +524,15 @@ print_summary() {
|
||||
echo " Konfiguration: $INSTALL_DIR/adguard-shield.conf"
|
||||
echo " Service: adguard-shield.service ($svc_status)"
|
||||
echo " Autostart: $autostart_status"
|
||||
|
||||
# Watchdog-Status
|
||||
local watchdog_status="deaktiviert"
|
||||
if systemctl is-active adguard-shield-watchdog.timer &>/dev/null 2>&1; then
|
||||
watchdog_status="aktiv ✅"
|
||||
elif systemctl is-enabled adguard-shield-watchdog.timer &>/dev/null 2>&1; then
|
||||
watchdog_status="aktiviert (Timer nicht gestartet)"
|
||||
fi
|
||||
echo " Watchdog: $watchdog_status"
|
||||
echo " Log-Datei: /var/log/adguard-shield.log"
|
||||
echo ""
|
||||
echo " Nützliche Befehle:"
|
||||
@@ -448,9 +556,17 @@ print_summary() {
|
||||
echo " sudo $INSTALL_DIR/adguard-shield.sh flush"
|
||||
echo " sudo $INSTALL_DIR/adguard-shield.sh unban <IP>"
|
||||
echo ""
|
||||
echo " E-Mail Report:"
|
||||
echo " sudo $INSTALL_DIR/report-generator.sh status"
|
||||
echo " sudo $INSTALL_DIR/report-generator.sh install"
|
||||
echo " sudo $INSTALL_DIR/report-generator.sh send"
|
||||
echo ""
|
||||
echo " Hilfe anzeigen:"
|
||||
echo " sudo bash install.sh --help"
|
||||
echo ""
|
||||
echo " Deinstallieren (auch ohne Installationsdateien):"
|
||||
echo " sudo bash $INSTALL_DIR/uninstall.sh"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ─── Status anzeigen ─────────────────────────────────────────────────────────
|
||||
@@ -496,6 +612,15 @@ do_status() {
|
||||
echo -e " ❌ Konfiguration: fehlt!"
|
||||
fi
|
||||
|
||||
# Watchdog-Status
|
||||
if systemctl is-active adguard-shield-watchdog.timer &>/dev/null 2>&1; then
|
||||
echo -e " ✅ Watchdog-Timer: aktiv"
|
||||
elif systemctl is-enabled adguard-shield-watchdog.timer &>/dev/null 2>&1; then
|
||||
echo -e " ⚠️ Watchdog-Timer: aktiviert aber nicht gestartet"
|
||||
else
|
||||
echo -e " ❌ Watchdog-Timer: nicht installiert/deaktiviert"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
@@ -507,7 +632,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
|
||||
@@ -532,10 +657,11 @@ 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"
|
||||
systemctl start adguard-shield-watchdog.timer 2>/dev/null || true
|
||||
echo -e " ✅ Service gestartet (inkl. Watchdog-Timer)"
|
||||
else
|
||||
echo -e " ℹ️ Service nicht gestartet"
|
||||
echo -e " ${YELLOW}Später starten mit: sudo systemctl start adguard-shield${NC}"
|
||||
@@ -568,18 +694,28 @@ do_update() {
|
||||
# Service-Datei aktualisieren
|
||||
echo -e "${YELLOW}Aktualisiere systemd Service...${NC}"
|
||||
cp "$SCRIPT_DIR/adguard-shield.service" "$SERVICE_FILE"
|
||||
cp "$SCRIPT_DIR/adguard-shield-watchdog.service" /etc/systemd/system/adguard-shield-watchdog.service
|
||||
cp "$SCRIPT_DIR/adguard-shield-watchdog.timer" /etc/systemd/system/adguard-shield-watchdog.timer
|
||||
systemctl daemon-reload
|
||||
echo -e " ✅ Service-Datei aktualisiert"
|
||||
echo -e " ✅ Service-Dateien aktualisiert (inkl. Watchdog)"
|
||||
echo ""
|
||||
|
||||
# Interaktiv: Autostart beim Booten?
|
||||
if systemctl is-enabled adguard-shield &>/dev/null; then
|
||||
echo -e " ℹ️ Autostart ist bereits aktiviert"
|
||||
# Watchdog-Timer auch aktivieren falls noch nicht aktiv
|
||||
if ! systemctl is-enabled adguard-shield-watchdog.timer &>/dev/null 2>&1; then
|
||||
systemctl enable adguard-shield-watchdog.timer
|
||||
systemctl start adguard-shield-watchdog.timer
|
||||
echo -e " ✅ Watchdog-Timer aktiviert"
|
||||
fi
|
||||
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"
|
||||
systemctl enable adguard-shield-watchdog.timer
|
||||
systemctl start adguard-shield-watchdog.timer
|
||||
echo -e " ✅ Autostart aktiviert (inkl. Watchdog-Timer)"
|
||||
else
|
||||
echo -e " ℹ️ Autostart bleibt deaktiviert"
|
||||
fi
|
||||
@@ -593,7 +729,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"
|
||||
@@ -602,7 +738,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"
|
||||
@@ -637,18 +773,22 @@ do_uninstall() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}Deinstalliere AdGuard Shield...${NC}"
|
||||
# An den im Installationsverzeichnis liegenden Uninstaller delegieren
|
||||
if [[ -f "$INSTALL_DIR/uninstall.sh" ]]; then
|
||||
exec bash "$INSTALL_DIR/uninstall.sh"
|
||||
fi
|
||||
|
||||
# Fallback für ältere Installationen ohne uninstall.sh
|
||||
echo -e "${YELLOW}Deinstalliere AdGuard Shield (Fallback-Modus)...${NC}"
|
||||
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
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Service stoppen und deaktivieren
|
||||
if systemctl is-active adguard-shield &>/dev/null; then
|
||||
systemctl stop adguard-shield
|
||||
echo " ✅ Service gestoppt"
|
||||
@@ -661,18 +801,24 @@ do_uninstall() {
|
||||
systemctl daemon-reload
|
||||
echo " ✅ Service-Datei entfernt"
|
||||
|
||||
# iptables Chain aufräumen
|
||||
if [[ -f "$INSTALL_DIR/iptables-helper.sh" ]]; then
|
||||
bash "$INSTALL_DIR/iptables-helper.sh" remove || true
|
||||
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"
|
||||
rm -f "$INSTALL_DIR/unban-expired.sh"
|
||||
rm -f "$INSTALL_DIR/external-blocklist-worker.sh"
|
||||
rm -f "$INSTALL_DIR/external-whitelist-worker.sh"
|
||||
rm -f "$INSTALL_DIR/offense-cleanup-worker.sh"
|
||||
rm -f "$INSTALL_DIR/geoip-worker.sh"
|
||||
rm -f "$INSTALL_DIR/report-generator.sh"
|
||||
rm -f "$INSTALL_DIR/adguard-shield-watchdog.sh"
|
||||
rm -f "$INSTALL_DIR/uninstall.sh"
|
||||
rm -rf "$INSTALL_DIR/templates"
|
||||
rm -rf "$INSTALL_DIR/geoip"
|
||||
echo " ✅ Scripts entfernt (Konfiguration und Logs behalten)"
|
||||
else
|
||||
rm -rf "$INSTALL_DIR"
|
||||
@@ -698,7 +844,7 @@ main() {
|
||||
do_update
|
||||
;;
|
||||
uninstall)
|
||||
print_header
|
||||
# print_header wird vom delegierten uninstall.sh übernommen
|
||||
do_uninstall
|
||||
;;
|
||||
status)
|
||||
|
||||
268
offense-cleanup-worker.sh
Normal file
268
offense-cleanup-worker.sh
Normal file
@@ -0,0 +1,268 @@
|
||||
#!/bin/bash
|
||||
###############################################################################
|
||||
# AdGuard Shield - Offense-Cleanup-Worker
|
||||
# Räumt abgelaufene Offense-Zähler (progressive Sperren) automatisch auf.
|
||||
# Entfernt .offenses-Dateien, deren letztes Vergehen länger als
|
||||
# PROGRESSIVE_BAN_RESET_AFTER zurückliegt.
|
||||
# Wird als Hintergrundprozess vom Hauptscript gestartet.
|
||||
#
|
||||
# Autor: Patrick Asmus
|
||||
# E-Mail: support@techniverse.net
|
||||
# Datum: 2026-04-16
|
||||
# Lizenz: MIT
|
||||
###############################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_FILE="${SCRIPT_DIR}/adguard-shield.conf"
|
||||
|
||||
# ─── Konfiguration laden ───────────────────────────────────────────────────────
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "FEHLER: Konfigurationsdatei nicht gefunden: $CONFIG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck source=adguard-shield.conf
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
# ─── Niedrigste Priorität setzen (CPU + I/O) ─────────────────────────────────
|
||||
# Stellt sicher, dass der Worker auch bei manuellem Start nie andere Dienste
|
||||
# verdrängt. nice 19 = niedrigste CPU-Priorität, ionice idle = nur bei freier I/O.
|
||||
renice -n 19 $$ >/dev/null 2>&1 || true
|
||||
ionice -c 3 -p $$ >/dev/null 2>&1 || true
|
||||
|
||||
# ─── Worker PID-File ──────────────────────────────────────────────────────────
|
||||
WORKER_PID_FILE="/var/run/adguard-offense-cleanup-worker.pid"
|
||||
|
||||
# ─── Prüfintervall ───────────────────────────────────────────────────────────
|
||||
# Prüft einmal pro Stunde – das ist völlig ausreichend für diese Aufgabe
|
||||
OFFENSE_CLEANUP_INTERVAL=3600
|
||||
|
||||
# ─── Logging (eigene Funktion, nutzt gleiche Log-Datei) ───────────────────────
|
||||
declare -A LOG_LEVELS=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3)
|
||||
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
local message="$*"
|
||||
local configured_level="${LOG_LEVEL:-INFO}"
|
||||
|
||||
if [[ ${LOG_LEVELS[$level]:-1} -ge ${LOG_LEVELS[$configured_level]:-1} ]]; then
|
||||
local timestamp
|
||||
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
local log_entry="[$timestamp] [$level] [OFFENSE-CLEANUP] $message"
|
||||
echo "$log_entry" | tee -a "$LOG_FILE" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Hilfsfunktionen ─────────────────────────────────────────────────────────
|
||||
format_duration() {
|
||||
local seconds="$1"
|
||||
if [[ "$seconds" -eq 0 ]]; then
|
||||
echo "PERMANENT"
|
||||
return
|
||||
fi
|
||||
if [[ "$seconds" -ge 86400 ]]; then
|
||||
echo "$((seconds / 86400))d $((seconds % 86400 / 3600))h"
|
||||
elif [[ "$seconds" -ge 3600 ]]; then
|
||||
echo "$((seconds / 3600))h $((seconds % 3600 / 60))m"
|
||||
elif [[ "$seconds" -ge 60 ]]; then
|
||||
echo "$((seconds / 60))m $((seconds % 60))s"
|
||||
else
|
||||
echo "${seconds}s"
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Verzeichnisse erstellen ──────────────────────────────────────────────────
|
||||
init_directories() {
|
||||
mkdir -p "${STATE_DIR}"
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
}
|
||||
|
||||
# ─── Abgelaufene Offense-Zähler aufräumen ────────────────────────────────────
|
||||
cleanup_expired_offenses() {
|
||||
local reset_after="${PROGRESSIVE_BAN_RESET_AFTER:-86400}"
|
||||
local now
|
||||
now=$(date '+%s')
|
||||
local cleaned=0
|
||||
|
||||
local batch_count=0
|
||||
for offense_file in "${STATE_DIR}"/*.offenses; do
|
||||
[[ -f "$offense_file" ]] || continue
|
||||
|
||||
local last_offense_epoch client_ip offense_level
|
||||
last_offense_epoch=$(grep '^LAST_OFFENSE_EPOCH=' "$offense_file" | cut -d= -f2 || true)
|
||||
client_ip=$(grep '^CLIENT_IP=' "$offense_file" | cut -d= -f2 || true)
|
||||
offense_level=$(grep '^OFFENSE_LEVEL=' "$offense_file" | cut -d= -f2 || true)
|
||||
|
||||
# Kein Zeitstempel vorhanden → überspringen
|
||||
if [[ -z "$last_offense_epoch" ]]; then
|
||||
log "DEBUG" "Offense-Datei ohne Zeitstempel übersprungen: $offense_file"
|
||||
continue
|
||||
fi
|
||||
|
||||
local elapsed=$((now - last_offense_epoch))
|
||||
|
||||
if [[ $elapsed -gt $reset_after ]]; then
|
||||
log "INFO" "Offense-Zähler abgelaufen: $client_ip (Stufe $offense_level, letztes Vergehen vor $(format_duration $elapsed)) → entfernt"
|
||||
rm -f "$offense_file"
|
||||
cleaned=$((cleaned + 1))
|
||||
fi
|
||||
|
||||
# Alle 10 Dateien kurz pausieren, um I/O-Bursts zu vermeiden
|
||||
batch_count=$((batch_count + 1))
|
||||
if (( batch_count % 10 == 0 )); then
|
||||
sleep 0.1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $cleaned -gt 0 ]]; then
|
||||
log "INFO" "Offense-Cleanup: $cleaned abgelaufene Zähler entfernt"
|
||||
else
|
||||
log "DEBUG" "Offense-Cleanup: keine abgelaufenen Zähler gefunden"
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── PID-Management ──────────────────────────────────────────────────────────
|
||||
write_pid() {
|
||||
echo $$ > "$WORKER_PID_FILE"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
log "INFO" "Offense-Cleanup-Worker wird beendet..."
|
||||
rm -f "$WORKER_PID_FILE"
|
||||
exit 0
|
||||
}
|
||||
|
||||
check_already_running() {
|
||||
if [[ -f "$WORKER_PID_FILE" ]]; then
|
||||
local old_pid
|
||||
old_pid=$(cat "$WORKER_PID_FILE")
|
||||
if kill -0 "$old_pid" 2>/dev/null; then
|
||||
log "DEBUG" "Offense-Cleanup-Worker läuft bereits (PID: $old_pid)"
|
||||
return 1
|
||||
else
|
||||
rm -f "$WORKER_PID_FILE"
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ─── Status anzeigen ─────────────────────────────────────────────────────────
|
||||
show_status() {
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo " Offense-Cleanup-Worker - Status"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" != "true" ]]; then
|
||||
echo " ⚠️ Progressive Sperren sind deaktiviert"
|
||||
echo " Aktivieren: PROGRESSIVE_BAN_ENABLED=true in $CONFIG_FILE"
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
|
||||
# Worker-Prozess Status
|
||||
if [[ -f "$WORKER_PID_FILE" ]]; then
|
||||
local pid
|
||||
pid=$(cat "$WORKER_PID_FILE")
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
echo " 🟢 Worker läuft (PID: $pid)"
|
||||
else
|
||||
echo " 🔴 Worker nicht aktiv (veraltete PID-Datei)"
|
||||
fi
|
||||
else
|
||||
echo " 🔴 Worker nicht aktiv"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Reset-Zeitraum: $(format_duration "${PROGRESSIVE_BAN_RESET_AFTER:-86400}")"
|
||||
echo " Prüfintervall: $(format_duration "$OFFENSE_CLEANUP_INTERVAL")"
|
||||
|
||||
# Aktuelle Offense-Dateien zählen
|
||||
local total=0
|
||||
local expired=0
|
||||
local now
|
||||
now=$(date '+%s')
|
||||
local reset_after="${PROGRESSIVE_BAN_RESET_AFTER:-86400}"
|
||||
|
||||
for offense_file in "${STATE_DIR}"/*.offenses; do
|
||||
[[ -f "$offense_file" ]] || continue
|
||||
total=$((total + 1))
|
||||
local last_epoch
|
||||
last_epoch=$(grep '^LAST_OFFENSE_EPOCH=' "$offense_file" | cut -d= -f2 || true)
|
||||
if [[ -n "$last_epoch" && $((now - last_epoch)) -gt $reset_after ]]; then
|
||||
expired=$((expired + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo " Offense-Zähler gesamt: $total"
|
||||
echo " Davon abgelaufen: $expired"
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
}
|
||||
|
||||
# ─── Hauptschleife ──────────────────────────────────────────────────────────
|
||||
main_loop() {
|
||||
init_directories
|
||||
|
||||
log "INFO" "═══════════════════════════════════════════════════════════"
|
||||
log "INFO" "Offense-Cleanup-Worker gestartet"
|
||||
log "INFO" " Reset-Zeitraum: $(format_duration "${PROGRESSIVE_BAN_RESET_AFTER:-86400}")"
|
||||
log "INFO" " Prüfintervall: $(format_duration "$OFFENSE_CLEANUP_INTERVAL")"
|
||||
log "INFO" "═══════════════════════════════════════════════════════════"
|
||||
|
||||
while true; do
|
||||
cleanup_expired_offenses
|
||||
sleep "$OFFENSE_CLEANUP_INTERVAL"
|
||||
done
|
||||
}
|
||||
|
||||
# ─── Signal-Handler ──────────────────────────────────────────────────────────
|
||||
trap cleanup SIGTERM SIGINT SIGHUP
|
||||
|
||||
# ─── Kommandozeilen-Argumente ────────────────────────────────────────────────
|
||||
case "${1:-start}" in
|
||||
start)
|
||||
if ! check_already_running; then
|
||||
exit 0
|
||||
fi
|
||||
write_pid
|
||||
main_loop
|
||||
;;
|
||||
stop)
|
||||
if [[ -f "$WORKER_PID_FILE" ]]; then
|
||||
kill "$(cat "$WORKER_PID_FILE")" 2>/dev/null || true
|
||||
rm -f "$WORKER_PID_FILE"
|
||||
echo "Offense-Cleanup-Worker gestoppt"
|
||||
else
|
||||
echo "Offense-Cleanup-Worker läuft nicht"
|
||||
fi
|
||||
;;
|
||||
run-once)
|
||||
init_directories
|
||||
log "INFO" "Einmaliger Offense-Cleanup..."
|
||||
cleanup_expired_offenses
|
||||
log "INFO" "Cleanup abgeschlossen"
|
||||
;;
|
||||
status)
|
||||
init_directories
|
||||
show_status
|
||||
;;
|
||||
*)
|
||||
cat << USAGE
|
||||
AdGuard Shield - Offense-Cleanup-Worker
|
||||
|
||||
Nutzung: $0 {start|stop|run-once|status}
|
||||
|
||||
Befehle:
|
||||
start Startet den Worker (Dauerbetrieb)
|
||||
stop Stoppt den Worker
|
||||
run-once Einmaliger Cleanup-Durchlauf
|
||||
status Zeigt Status und aktuelle Offense-Zähler
|
||||
|
||||
Konfiguration: $CONFIG_FILE
|
||||
USAGE
|
||||
;;
|
||||
esac
|
||||
1289
report-generator.sh
Normal file
1289
report-generator.sh
Normal file
File diff suppressed because it is too large
Load Diff
370
templates/report.html
Normal file
370
templates/report.html
Normal file
@@ -0,0 +1,370 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AdGuard Shield – Report</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
background-color: #f0f2f5;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #1a1a2e;
|
||||
}
|
||||
.container {
|
||||
max-width: 700px;
|
||||
margin: 30px auto;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
.header {
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
|
||||
color: #ffffff;
|
||||
padding: 30px 35px;
|
||||
text-align: center;
|
||||
}
|
||||
.header h1 {
|
||||
margin: 0 0 6px 0;
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.header .subtitle {
|
||||
font-size: 14px;
|
||||
color: #a8b2d1;
|
||||
margin: 0;
|
||||
}
|
||||
.header .period {
|
||||
display: inline-block;
|
||||
margin-top: 14px;
|
||||
padding: 6px 18px;
|
||||
background: rgba(255,255,255,0.12);
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
color: #ccd6f6;
|
||||
}
|
||||
.content {
|
||||
padding: 30px 35px;
|
||||
}
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.stat-card {
|
||||
background: #f8f9fc;
|
||||
border-radius: 10px;
|
||||
padding: 18px 20px;
|
||||
border-left: 4px solid #0f3460;
|
||||
}
|
||||
.stat-card.danger {
|
||||
border-left-color: #e74c3c;
|
||||
}
|
||||
.stat-card.warning {
|
||||
border-left-color: #f39c12;
|
||||
}
|
||||
.stat-card.success {
|
||||
border-left-color: #27ae60;
|
||||
}
|
||||
.stat-card.info {
|
||||
border-left-color: #3498db;
|
||||
}
|
||||
.stat-card .stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #1a1a2e;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.stat-card .stat-label {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
color: #1a1a2e;
|
||||
margin: 28px 0 14px 0;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 2px solid #f0f2f5;
|
||||
}
|
||||
h2:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
th {
|
||||
background: #f8f9fc;
|
||||
color: #1a1a2e;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
padding: 10px 14px;
|
||||
border-bottom: 2px solid #e8ecf1;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
td {
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
color: #495057;
|
||||
}
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
tr:hover td {
|
||||
background: #fafbfd;
|
||||
}
|
||||
.rank {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
background: #e8ecf1;
|
||||
border-radius: 50%;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
}
|
||||
.rank.top3 {
|
||||
background: #0f3460;
|
||||
color: #ffffff;
|
||||
}
|
||||
.ip-cell {
|
||||
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
||||
font-size: 13px;
|
||||
color: #1a1a2e;
|
||||
}
|
||||
.bar-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.bar {
|
||||
height: 8px;
|
||||
background: linear-gradient(90deg, #0f3460, #3498db);
|
||||
border-radius: 4px;
|
||||
min-width: 4px;
|
||||
}
|
||||
.bar-value {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1a1a2e;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.protocol-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
background: #e8ecf1;
|
||||
color: #495057;
|
||||
margin: 2px;
|
||||
}
|
||||
.protocol-badge.dns { background: #dff0d8; color: #3c763d; }
|
||||
.protocol-badge.doh { background: #d9edf7; color: #31708f; }
|
||||
.protocol-badge.dot { background: #fcf8e3; color: #8a6d3b; }
|
||||
.protocol-badge.doq { background: #f2dede; color: #a94442; }
|
||||
.reason-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.reason-badge.rate-limit { background: #fcf8e3; color: #8a6d3b; }
|
||||
.reason-badge.subdomain-flood { background: #f2dede; color: #a94442; }
|
||||
.reason-badge.external { background: #d9edf7; color: #31708f; }
|
||||
.no-data {
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
color: #adb5bd;
|
||||
font-style: italic;
|
||||
}
|
||||
.footer {
|
||||
background: #f8f9fc;
|
||||
padding: 24px 35px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
border-top: 1px solid #e8ecf1;
|
||||
}
|
||||
.footer a {
|
||||
color: #0f3460;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.footer .links {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.footer .separator {
|
||||
margin: 0 8px;
|
||||
color: #ced4da;
|
||||
}
|
||||
.version-tag {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
font-size: 11px;
|
||||
color: #adb5bd;
|
||||
}
|
||||
.update-notice {
|
||||
display: inline-block;
|
||||
margin-top: 10px;
|
||||
padding: 7px 14px;
|
||||
background: #fff8e1;
|
||||
border: 1px solid #ffc107;
|
||||
border-radius: 8px;
|
||||
color: #7a5700;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.update-notice a {
|
||||
color: #7a5700;
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
}
|
||||
.update-notice a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.period-today td {
|
||||
background: #eef4ff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.period-today td:first-child {
|
||||
color: #0f3460;
|
||||
}
|
||||
.period-gestern td {
|
||||
background: #f0faf3;
|
||||
font-weight: 600;
|
||||
}
|
||||
.period-gestern td:first-child {
|
||||
color: #27ae60;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
table { font-size: 12px; }
|
||||
th, td { padding: 8px 8px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Header -->
|
||||
<div class="header">
|
||||
<h1>🛡️ AdGuard Shield</h1>
|
||||
<p class="subtitle">Sicherheits-Report</p>
|
||||
<div class="period">{{REPORT_PERIOD}}</div>
|
||||
</div>
|
||||
|
||||
<!-- Statistik-Übersicht -->
|
||||
<div class="content">
|
||||
<!-- Zeitraum-Schnellübersicht -->
|
||||
<h2>📅 Zeitraum-Schnellübersicht</h2>
|
||||
{{PERIOD_OVERVIEW_TABLE}}
|
||||
|
||||
<!-- Gesamt-Übersicht des Berichtszeitraums -->
|
||||
<h2>📊 Übersicht</h2>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card danger">
|
||||
<div class="stat-value">{{TOTAL_BANS}}</div>
|
||||
<div class="stat-label">Sperren gesamt</div>
|
||||
</div>
|
||||
<div class="stat-card success">
|
||||
<div class="stat-value">{{TOTAL_UNBANS}}</div>
|
||||
<div class="stat-label">Entsperrungen</div>
|
||||
</div>
|
||||
<div class="stat-card warning">
|
||||
<div class="stat-value">{{UNIQUE_IPS}}</div>
|
||||
<div class="stat-label">Eindeutige IPs</div>
|
||||
</div>
|
||||
<div class="stat-card info">
|
||||
<div class="stat-value">{{PERMANENT_BANS}}</div>
|
||||
<div class="stat-label">Permanente Sperren</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ACTIVE_BANS}}</div>
|
||||
<div class="stat-label">Aktuell aktive Sperren</div>
|
||||
</div>
|
||||
<div class="stat-card info">
|
||||
<div class="stat-value">{{ABUSEIPDB_REPORTS}}</div>
|
||||
<div class="stat-label">AbuseIPDB Reports</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Angriffsarten -->
|
||||
<h2>⚔️ Angriffsarten</h2>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card warning">
|
||||
<div class="stat-value">{{RATELIMIT_BANS}}</div>
|
||||
<div class="stat-label">Rate-Limit Sperren</div>
|
||||
</div>
|
||||
<div class="stat-card danger">
|
||||
<div class="stat-value">{{SUBDOMAIN_FLOOD_BANS}}</div>
|
||||
<div class="stat-label">Subdomain-Flood Sperren</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{EXTERNAL_BLOCKLIST_BANS}}</div>
|
||||
<div class="stat-label">Externe Blocklist</div>
|
||||
</div>
|
||||
<div class="stat-card success">
|
||||
<div class="stat-value">{{BUSIEST_DAY}}</div>
|
||||
<div class="stat-label">{{BUSIEST_DAY_LABEL}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Top 10 IPs -->
|
||||
<h2>🏴☠️ Top 10 – Auffälligste IPs</h2>
|
||||
{{TOP10_IPS_TABLE}}
|
||||
|
||||
<!-- Top 10 Domains -->
|
||||
<h2>🌐 Top 10 – Meistbetroffene Domains</h2>
|
||||
{{TOP10_DOMAINS_TABLE}}
|
||||
|
||||
<!-- Protokoll-Verteilung -->
|
||||
<h2>📡 Protokoll-Verteilung</h2>
|
||||
{{PROTOCOL_TABLE}}
|
||||
|
||||
<!-- Letzte Sperren -->
|
||||
<h2>🕐 Letzte 10 Sperren</h2>
|
||||
{{RECENT_BANS_TABLE}}
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer">
|
||||
<div class="links">
|
||||
<span>
|
||||
<a href="https://www.patrick-asmus.de">Patrick-Asmus.de</a>
|
||||
<span class="separator">|</span>
|
||||
<a href="https://www.cleveradmin.de">CleverAdmin.de</a>
|
||||
</span>
|
||||
<span>
|
||||
<a href="https://git.techniverse.net/scriptos/adguard-shield.git">AdGuard Shield auf Gitea</a>
|
||||
<span class="separator">|</span>
|
||||
<a href="https://git.techniverse.net/scriptos/adguard-shield/src/branch/main/docs">docs</a>
|
||||
</span>
|
||||
</div>
|
||||
<br>
|
||||
Dieser Report wurde automatisch von <strong>AdGuard Shield</strong> generiert.<br>
|
||||
Generiert am: {{REPORT_DATE}}
|
||||
<div class="version-tag">AdGuard Shield {{VERSION}} · {{HOSTNAME}}</div>
|
||||
{{UPDATE_NOTICE}}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
68
templates/report.txt
Normal file
68
templates/report.txt
Normal file
@@ -0,0 +1,68 @@
|
||||
═══════════════════════════════════════════════════════════════
|
||||
🛡️ AdGuard Shield – Sicherheits-Report
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
Zeitraum: {{REPORT_PERIOD}}
|
||||
Erstellt: {{REPORT_DATE}}
|
||||
Host: {{HOSTNAME}}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
<20> ZEITRAUM-SCHNELLÜBERSICHT
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
{{PERIOD_OVERVIEW_TEXT}}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
📊 ÜBERSICHT (Berichtszeitraum)
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
Sperren gesamt: {{TOTAL_BANS}}
|
||||
Entsperrungen: {{TOTAL_UNBANS}}
|
||||
Eindeutige IPs: {{UNIQUE_IPS}}
|
||||
Permanente Sperren: {{PERMANENT_BANS}}
|
||||
Aktuell aktive Sperren: {{ACTIVE_BANS}}
|
||||
AbuseIPDB Reports: {{ABUSEIPDB_REPORTS}}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
⚔️ ANGRIFFSARTEN
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
Rate-Limit Sperren: {{RATELIMIT_BANS}}
|
||||
Subdomain-Flood Sperren: {{SUBDOMAIN_FLOOD_BANS}}
|
||||
Externe Blocklist: {{EXTERNAL_BLOCKLIST_BANS}}
|
||||
{{BUSIEST_DAY_LABEL}}: {{BUSIEST_DAY}}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
🏴☠️ TOP 10 – AUFFÄLLIGSTE IPs
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
{{TOP10_IPS_TEXT}}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
🌐 TOP 10 – MEISTBETROFFENE DOMAINS
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
{{TOP10_DOMAINS_TEXT}}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
📡 PROTOKOLL-VERTEILUNG
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
{{PROTOCOL_TEXT}}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
🕐 LETZTE 10 SPERREN
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
{{RECENT_BANS_TEXT}}
|
||||
|
||||
{{UPDATE_NOTICE_TXT}}
|
||||
═══════════════════════════════════════════════════════════════
|
||||
Dieser Report wurde automatisch von AdGuard Shield generiert.
|
||||
AdGuard Shield {{VERSION}}
|
||||
|
||||
Web: https://www.patrick-asmus.de
|
||||
Blog: https://www.cleveradmin.de
|
||||
Repo: https://git.techniverse.net/scriptos/adguard-shield.git
|
||||
Docs: https://git.techniverse.net/scriptos/adguard-shield/src/branch/main/docs
|
||||
═══════════════════════════════════════════════════════════════
|
||||
@@ -29,17 +29,20 @@ log_ban_history() {
|
||||
local domain="${3:-}"
|
||||
local count="${4:-}"
|
||||
local reason="${5:-}"
|
||||
local protocol="${6:-}"
|
||||
local timestamp
|
||||
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
|
||||
if [[ ! -f "$BAN_HISTORY_FILE" ]]; then
|
||||
echo "# AdGuard Shield - Ban History" > "$BAN_HISTORY_FILE"
|
||||
echo "# Format: ZEITSTEMPEL | AKTION | CLIENT-IP | DOMAIN | ANFRAGEN | SPERRDAUER | GRUND" >> "$BAN_HISTORY_FILE"
|
||||
echo "#─────────────────────────────────────────────────────────────────────────────────" >> "$BAN_HISTORY_FILE"
|
||||
echo "# Format: ZEITSTEMPEL | AKTION | CLIENT-IP | DOMAIN | ANFRAGEN | SPERRDAUER | PROTOKOLL | GRUND" >> "$BAN_HISTORY_FILE"
|
||||
echo "#────────────────────────────────────────────────────────────────────────────────────────────────" >> "$BAN_HISTORY_FILE"
|
||||
fi
|
||||
|
||||
printf "%-19s | %-6s | %-39s | %-30s | %-8s | %-10s | %s\n" \
|
||||
"$timestamp" "$action" "$client_ip" "${domain:--}" "${count:--}" "-" "${reason:-expired}" \
|
||||
[[ -z "$protocol" ]] && protocol="-"
|
||||
|
||||
printf "%-19s | %-6s | %-39s | %-30s | %-8s | %-10s | %-10s | %s\n" \
|
||||
"$timestamp" "$action" "$client_ip" "${domain:--}" "${count:--}" "-" "$protocol" "${reason:-expired}" \
|
||||
>> "$BAN_HISTORY_FILE"
|
||||
}
|
||||
|
||||
@@ -51,6 +54,13 @@ for state_file in "${STATE_DIR}"/*.ban; do
|
||||
ban_until_epoch=$(grep '^BAN_UNTIL_EPOCH=' "$state_file" | cut -d= -f2)
|
||||
client_ip=$(grep '^CLIENT_IP=' "$state_file" | cut -d= -f2)
|
||||
domain=$(grep '^DOMAIN=' "$state_file" | cut -d= -f2)
|
||||
is_permanent=$(grep '^IS_PERMANENT=' "$state_file" | cut -d= -f2)
|
||||
protocol=$(grep '^PROTOCOL=' "$state_file" | cut -d= -f2)
|
||||
|
||||
# Permanente Sperren nicht automatisch aufheben
|
||||
if [[ "$is_permanent" == "true" || "$ban_until_epoch" == "0" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ -n "$ban_until_epoch" && "$NOW" -ge "$ban_until_epoch" ]]; then
|
||||
echo "$LOG_PREFIX Entsperre abgelaufene Sperre: $client_ip" >> "$LOG_FILE"
|
||||
@@ -63,7 +73,7 @@ for state_file in "${STATE_DIR}"/*.ban; do
|
||||
fi
|
||||
|
||||
# Ban-History Eintrag
|
||||
log_ban_history "UNBAN" "$client_ip" "$domain" "-" "expired-cron"
|
||||
log_ban_history "UNBAN" "$client_ip" "$domain" "-" "expired-cron" "${protocol:-}"
|
||||
|
||||
rm -f "$state_file"
|
||||
unban_count=$((unban_count + 1))
|
||||
|
||||
150
uninstall.sh
Normal file
150
uninstall.sh
Normal file
@@ -0,0 +1,150 @@
|
||||
#!/bin/bash
|
||||
###############################################################################
|
||||
# AdGuard Shield - Uninstaller
|
||||
# Autor: Patrick Asmus
|
||||
# E-Mail: support@techniverse.net
|
||||
# Lizenz: MIT
|
||||
#
|
||||
# Dieses Script befindet sich im Installationsverzeichnis und kann daher
|
||||
# ohne die originalen Installationsdateien ausgeführt werden:
|
||||
# sudo bash /opt/adguard-shield/uninstall.sh
|
||||
###############################################################################
|
||||
|
||||
# INSTALL_DIR ergibt sich aus dem Verzeichnis, in dem dieses Script liegt
|
||||
INSTALL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SERVICE_FILE="/etc/systemd/system/adguard-shield.service"
|
||||
WATCHDOG_SERVICE_FILE="/etc/systemd/system/adguard-shield-watchdog.service"
|
||||
WATCHDOG_TIMER_FILE="/etc/systemd/system/adguard-shield-watchdog.timer"
|
||||
|
||||
# Farben
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_header() {
|
||||
echo ""
|
||||
echo -e "${BLUE}"
|
||||
echo " ▄▄▄ ▓█████▄ ▄████ █ ██ ▄▄▄ ██▀███ ▓█████▄ ██████ ██░ ██ ██▓▓█████ ██▓ ▓█████▄ "
|
||||
echo "▒████▄ ▒██▀ ██▌ ██▒ ▀█▒ ██ ▓██▒▒████▄ ▓██ ▒ ██▒▒██▀ ██▌ ▒██ ▒ ▓██░ ██▒▓██▒▓█ ▀ ▓██▒ ▒██▀ ██▌"
|
||||
echo "▒██ ▀█▄ ░██ █▌▒██░▄▄▄░▓██ ▒██░▒██ ▀█▄ ▓██ ░▄█ ▒░██ █▌ ░ ▓██▄ ▒██▀▀██░▒██▒▒███ ▒██░ ░██ █▌"
|
||||
echo "░██▄▄▄▄██ ░▓█▄ ▌░▓█ ██▓▓▓█ ░██░░██▄▄▄▄██ ▒██▀▀█▄ ░▓█▄ ▌ ▒ ██▒░▓█ ░██ ░██░▒▓█ ▄ ▒██░ ░▓█▄ ▌"
|
||||
echo " ▓█ ▓██▒░▒████▓ ░▒▓███▀▒▒▒█████▓ ▓█ ▓██▒░██▓ ▒██▒░▒████▓ ▒██████▒▒░▓█▒░██▓░██░░▒████▒░██████▒░▒████▓ "
|
||||
echo " ▒▒ ▓▒█░ ▒▒▓ ▒ ░▒ ▒ ░▒▓▒ ▒ ▒ ▒▒ ▓▒█░░ ▒▓ ░▒▓░ ▒▒▓ ▒ ▒ ▒▓▒ ▒ ░ ▒ ░░▒░▒░▓ ░░ ▒░ ░░ ▒░▓ ░ ▒▒▓ ▒ "
|
||||
echo " ▒ ▒▒ ░ ░ ▒ ▒ ░ ░ ░░▒░ ░ ░ ▒ ▒▒ ░ ░▒ ░ ▒░ ░ ▒ ▒ ░ ░▒ ░ ░ ▒ ░▒░ ░ ▒ ░ ░ ░ ░░ ░ ▒ ░ ░ ▒ ▒ "
|
||||
echo " ░ ▒ ░ ░ ░ ░ ░ ░ ░░░ ░ ░ ░ ▒ ░░ ░ ░ ░ ░ ░ ░ ░ ░ ░░ ░ ▒ ░ ░ ░ ░ ░ ░ ░ "
|
||||
echo " ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ "
|
||||
echo " ░ ░ ░ "
|
||||
echo -e "${NC}"
|
||||
echo -e "${GREEN} Uninstaller${NC}"
|
||||
echo -e "${BLUE} Autor: Patrick Asmus${NC}"
|
||||
echo -e
|
||||
echo -e "${BLUE} E-Mail: support@techniverse.net${NC}"
|
||||
echo -e "${BLUE} Web: https://www.patrick-asmus.de${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}───────────────────────────────────────────────────────────────────────────────────────────────────────────────${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE} Repo: https://git.techniverse.net/scriptos/adguard-shield${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════════════════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo -e "${RED}Dieses Script muss als root ausgeführt werden!${NC}" >&2
|
||||
echo "Bitte mit 'sudo $0' ausführen."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
do_uninstall() {
|
||||
check_root
|
||||
|
||||
# Prüfen ob installiert
|
||||
if [[ ! -d "$INSTALL_DIR" ]]; then
|
||||
echo -e "${RED}AdGuard Shield ist nicht installiert (Verzeichnis nicht gefunden: $INSTALL_DIR)!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}Deinstalliere AdGuard Shield aus: ${BOLD}$INSTALL_DIR${NC}"
|
||||
echo ""
|
||||
|
||||
# Sicherheitsabfrage
|
||||
read -rep " Wirklich deinstallieren? [j/N]: " confirm
|
||||
if [[ "${confirm,,}" != "j" ]]; then
|
||||
echo -e "${GREEN}Deinstallation abgebrochen.${NC}"
|
||||
exit 0
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Watchdog-Timer stoppen und deaktivieren
|
||||
if systemctl is-active adguard-shield-watchdog.timer &>/dev/null 2>&1; then
|
||||
systemctl stop adguard-shield-watchdog.timer
|
||||
echo " ✅ Watchdog-Timer gestoppt"
|
||||
fi
|
||||
if systemctl is-enabled adguard-shield-watchdog.timer &>/dev/null 2>&1; then
|
||||
systemctl disable adguard-shield-watchdog.timer
|
||||
echo " ✅ Watchdog-Timer deaktiviert"
|
||||
fi
|
||||
|
||||
# Service stoppen und deaktivieren
|
||||
if systemctl is-active adguard-shield &>/dev/null; then
|
||||
systemctl stop adguard-shield
|
||||
echo " ✅ Service gestoppt"
|
||||
fi
|
||||
if systemctl is-enabled adguard-shield &>/dev/null; then
|
||||
systemctl disable adguard-shield
|
||||
echo " ✅ Service deaktiviert"
|
||||
fi
|
||||
if [[ -f "$SERVICE_FILE" ]]; then
|
||||
rm -f "$SERVICE_FILE"
|
||||
echo " ✅ Service-Datei entfernt"
|
||||
fi
|
||||
rm -f "$WATCHDOG_SERVICE_FILE" "$WATCHDOG_TIMER_FILE"
|
||||
if [[ -f "$WATCHDOG_SERVICE_FILE" ]] || [[ -f "$WATCHDOG_TIMER_FILE" ]]; then
|
||||
echo " ✅ Watchdog-Dateien entfernt"
|
||||
fi
|
||||
systemctl daemon-reload
|
||||
|
||||
# iptables Chain aufräumen
|
||||
if [[ -f "$INSTALL_DIR/iptables-helper.sh" ]]; then
|
||||
bash "$INSTALL_DIR/iptables-helper.sh" remove || true
|
||||
fi
|
||||
|
||||
# Dateien entfernen
|
||||
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"
|
||||
rm -f "$INSTALL_DIR/unban-expired.sh"
|
||||
rm -f "$INSTALL_DIR/external-blocklist-worker.sh"
|
||||
rm -f "$INSTALL_DIR/external-whitelist-worker.sh"
|
||||
rm -f "$INSTALL_DIR/offense-cleanup-worker.sh"
|
||||
rm -f "$INSTALL_DIR/report-generator.sh"
|
||||
rm -f "$INSTALL_DIR/adguard-shield-watchdog.sh"
|
||||
rm -f "$INSTALL_DIR/geoip-worker.sh"
|
||||
rm -f "$INSTALL_DIR/uninstall.sh"
|
||||
rm -rf "$INSTALL_DIR/templates"
|
||||
rm -rf "$INSTALL_DIR/geoip"
|
||||
echo " ✅ Scripts entfernt (Konfiguration und Logs behalten)"
|
||||
echo ""
|
||||
echo -e "${YELLOW} Konfiguration verbleibt in: $INSTALL_DIR/adguard-shield.conf${NC}"
|
||||
echo -e "${YELLOW} Logs verbleiben in: /var/log/adguard-shield*.log${NC}"
|
||||
else
|
||||
rm -rf "$INSTALL_DIR"
|
||||
rm -rf /var/lib/adguard-shield
|
||||
rm -f /var/log/adguard-shield.log*
|
||||
rm -f /var/log/adguard-shield-bans.log
|
||||
echo " ✅ Alles entfernt"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Deinstallation abgeschlossen.${NC}"
|
||||
}
|
||||
|
||||
print_header
|
||||
do_uninstall
|
||||
Reference in New Issue
Block a user