feat: Progressive Sperren (Recidive) – Wiederholungstäter werden stufenweise länger gesperrt
- Neues Recidive-System: Sperrdauer steigt exponentiell bei wiederholten Verstößen (wie fail2ban) - Konfigurierbar: Multiplikator, Max-Stufe mit permanenter Sperre, Zähler-Reset-Zeit - Offense-Tracking via *.offenses State-Dateien - Permanente Sperren werden nicht automatisch aufgehoben - Neuer Befehl: reset-offenses [IP] zum Zurücksetzen der Offense-Zähler - Status-Anzeige zeigt Stufe, Dauer und Wiederholungstäter-Übersicht - Benachrichtigungen enthalten Stufe und progressive Sperrdauer - unban-expired.sh berücksichtigt permanente Sperren - Dokumentation vollständig aktualisiert (Konfig, Befehle, Architektur, Troubleshooting, README)
This commit is contained in:
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"files.eol": "\n",
|
||||||
"chat.tools.terminal.autoApprove": {
|
"chat.tools.terminal.autoApprove": {
|
||||||
"Rename-Item": true
|
"Rename-Item": true,
|
||||||
|
"ForEach-Object": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@ Wenn ein Client eine bestimmte Domain zu oft anfragt (z.B. >30x pro Minute), wir
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Automatische Erkennung und Sperre bei Rate-Limit-Verstößen
|
- Automatische Erkennung und Sperre bei Rate-Limit-Verstößen
|
||||||
|
- **Progressive Sperren (Recidive)** — Wiederholungstäter werden stufenweise länger gesperrt (wie bei fail2ban)
|
||||||
- Unterstützt **alle DNS-Protokolle**: DNS (53), DoH (443), DoT (853), DoQ (784/853/8853)
|
- Unterstützt **alle DNS-Protokolle**: DNS (53), DoH (443), DoT (853), DoQ (784/853/8853)
|
||||||
- **IPv4 + IPv6**
|
- **IPv4 + IPv6**
|
||||||
- Eigene iptables Chain — greift nicht in bestehende Regeln ein
|
- Eigene iptables Chain — greift nicht in bestehende Regeln ein
|
||||||
@@ -79,6 +80,7 @@ sudo /opt/adguard-shield/adguard-shield.sh status # Aktive Sperren a
|
|||||||
sudo /opt/adguard-shield/adguard-shield.sh history # Ban-History 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 unban IP # Einzelne IP entsperren
|
||||||
sudo /opt/adguard-shield/adguard-shield.sh flush # Alle Sperren aufheben
|
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 test # API-Verbindung testen
|
||||||
sudo /opt/adguard-shield/adguard-shield.sh blocklist-status # Externe Blocklisten Status
|
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 blocklist-sync # Blocklisten manuell synchronisieren
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
# --- AdGuard Home API Einstellungen ---
|
# --- AdGuard Home API Einstellungen ---
|
||||||
# URL der AdGuard Home Web-Oberfläche (ohne trailing slash)
|
# URL der AdGuard Home Web-Oberfläche (ohne trailing slash)
|
||||||
ADGUARD_URL="https://dns1.techniverse.net"
|
ADGUARD_URL="https://dns1.domain.com"
|
||||||
|
|
||||||
# AdGuard Home Zugangsdaten (Web-UI Login)
|
# AdGuard Home Zugangsdaten (Web-UI Login)
|
||||||
ADGUARD_USER="adg-admin"
|
ADGUARD_USER="admin"
|
||||||
ADGUARD_PASS='changeme'
|
ADGUARD_PASS='changeme'
|
||||||
|
|
||||||
# --- Rate-Limit Einstellungen ---
|
# --- Rate-Limit Einstellungen ---
|
||||||
@@ -28,13 +28,11 @@ BAN_DURATION=3600
|
|||||||
# iptables Chain-Name für die Sperren
|
# iptables Chain-Name für die Sperren
|
||||||
IPTABLES_CHAIN="ADGUARD_SHIELD"
|
IPTABLES_CHAIN="ADGUARD_SHIELD"
|
||||||
|
|
||||||
# Welche Ports gesperrt werden sollen (DNS, DoT, DoH, DNSv5/QUIC)
|
# Welche Ports gesperrt werden sollen (IPv4 + IPv6)
|
||||||
# Port 53 = DNS (UDP + TCP)
|
# Port 53 = DNS (UDP + TCP)
|
||||||
# Port 443 = DNS-over-HTTPS (DoH)
|
# Port 443 = DNS-over-HTTPS (DoH)
|
||||||
# Port 853 = DNS-over-TLS (DoT) / DNS-over-QUIC
|
# Port 853 = DNS-over-TLS (tls://...:853) / DNS-over-QUIC (quic://...:853)
|
||||||
# Port 784 = DNS-over-QUIC (alternativ)
|
BLOCKED_PORTS="53 443 853"
|
||||||
# Port 8853 = DNS-over-QUIC (alternativ)
|
|
||||||
BLOCKED_PORTS="53 443 853 784 8853"
|
|
||||||
|
|
||||||
# --- Whitelist ---
|
# --- Whitelist ---
|
||||||
# IP-Adressen die NIEMALS gesperrt werden (kommagetrennt)
|
# IP-Adressen die NIEMALS gesperrt werden (kommagetrennt)
|
||||||
@@ -55,17 +53,17 @@ LOG_MAX_SIZE_MB=50
|
|||||||
BAN_HISTORY_FILE="/var/log/adguard-shield-bans.log"
|
BAN_HISTORY_FILE="/var/log/adguard-shield-bans.log"
|
||||||
|
|
||||||
# --- Benachrichtigungen (optional) ---
|
# --- Benachrichtigungen (optional) ---
|
||||||
# Aktiviert Benachrichtigungen bei Sperren
|
# Aktiviert Benachrichtigungen bei Sperren/Entsperrungen
|
||||||
NOTIFY_ENABLED=false
|
NOTIFY_ENABLED=false
|
||||||
|
|
||||||
# Webhook-URL für Benachrichtigungen (z.B. Discord, Slack, Gotify)
|
# Benachrichtigungs-Typ: "ntfy", "discord", "slack", "gotify", "generic"
|
||||||
|
NOTIFY_TYPE="ntfy"
|
||||||
|
|
||||||
|
# Webhook-URL (nur für discord, slack, gotify, generic – bei ntfy nicht nötig)
|
||||||
# Discord: https://discord.com/api/webhooks/xxx/yyy
|
# Discord: https://discord.com/api/webhooks/xxx/yyy
|
||||||
# Gotify: https://gotify.example.com/message?token=xxx
|
# Gotify: https://gotify.example.com/message?token=xxx
|
||||||
NOTIFY_WEBHOOK_URL=""
|
NOTIFY_WEBHOOK_URL=""
|
||||||
|
|
||||||
# Benachrichtigungs-Typ: "discord", "slack", "gotify", "ntfy", "generic"
|
|
||||||
NOTIFY_TYPE="generic"
|
|
||||||
|
|
||||||
# --- Ntfy Einstellungen (nur bei NOTIFY_TYPE="ntfy") ---
|
# --- Ntfy Einstellungen (nur bei NOTIFY_TYPE="ntfy") ---
|
||||||
# Server-URL der Ntfy-Instanz (ohne trailing slash)
|
# Server-URL der Ntfy-Instanz (ohne trailing slash)
|
||||||
NTFY_SERVER_URL="https://ntfy.sh"
|
NTFY_SERVER_URL="https://ntfy.sh"
|
||||||
@@ -77,7 +75,7 @@ NTFY_TOPIC=""
|
|||||||
NTFY_TOKEN=""
|
NTFY_TOKEN=""
|
||||||
|
|
||||||
# Priorität der Ntfy-Nachrichten (1=min, 3=default, 5=max)
|
# Priorität der Ntfy-Nachrichten (1=min, 3=default, 5=max)
|
||||||
NTFY_PRIORITY="1"
|
NTFY_PRIORITY="4"
|
||||||
|
|
||||||
# --- Externe Blocklist (optional) ---
|
# --- Externe Blocklist (optional) ---
|
||||||
# Aktiviert den externen Blocklist-Worker
|
# Aktiviert den externen Blocklist-Worker
|
||||||
@@ -100,6 +98,26 @@ EXTERNAL_BLOCKLIST_AUTO_UNBAN=true
|
|||||||
# Lokaler Cache-Pfad für die heruntergeladene Blocklist
|
# Lokaler Cache-Pfad für die heruntergeladene Blocklist
|
||||||
EXTERNAL_BLOCKLIST_CACHE_DIR="/var/lib/adguard-shield/external-blocklist"
|
EXTERNAL_BLOCKLIST_CACHE_DIR="/var/lib/adguard-shield/external-blocklist"
|
||||||
|
|
||||||
|
# --- Progressive Sperren (Recidive) ---
|
||||||
|
# Wiederholungstäter werden stufenweise länger gesperrt (wie bei fail2ban)
|
||||||
|
# Aktiviert das progressive Sperrsystem
|
||||||
|
PROGRESSIVE_BAN_ENABLED=true
|
||||||
|
|
||||||
|
# Multiplikator pro Wiederholung (2 = Verdopplung der Sperrdauer)
|
||||||
|
# Stufe 1: BAN_DURATION × 1 (Standard-Sperrdauer)
|
||||||
|
# Stufe 2: BAN_DURATION × 2
|
||||||
|
# Stufe 3: BAN_DURATION × 4
|
||||||
|
# Stufe 4: BAN_DURATION × 8 ... usw.
|
||||||
|
PROGRESSIVE_BAN_MULTIPLIER=2
|
||||||
|
|
||||||
|
# Ab dieser Stufe wird die IP permanent gesperrt (0 = nie permanent sperren)
|
||||||
|
# Beispiel: 5 = nach dem 5. Vergehen wird die IP dauerhaft gesperrt
|
||||||
|
PROGRESSIVE_BAN_MAX_LEVEL=5
|
||||||
|
|
||||||
|
# Nach wie vielen Sekunden ohne erneutes Vergehen wird der Zähler zurückgesetzt
|
||||||
|
# (86400 = 24 Stunden, 604800 = 7 Tage)
|
||||||
|
PROGRESSIVE_BAN_RESET_AFTER=86400
|
||||||
|
|
||||||
# --- Erweiterte Einstellungen ---
|
# --- Erweiterte Einstellungen ---
|
||||||
# Pfad zur State-Datei (speichert aktive Sperren)
|
# Pfad zur State-Datei (speichert aktive Sperren)
|
||||||
STATE_DIR="/var/lib/adguard-shield"
|
STATE_DIR="/var/lib/adguard-shield"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
# Lizenz: MIT
|
# Lizenz: MIT
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
VERSION="0.3.0"
|
VERSION="0.4.0"
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -78,6 +78,7 @@ log_ban_history() {
|
|||||||
local domain="${3:-}"
|
local domain="${3:-}"
|
||||||
local count="${4:-}"
|
local count="${4:-}"
|
||||||
local reason="${5:-}"
|
local reason="${5:-}"
|
||||||
|
local duration="${6:-}"
|
||||||
local timestamp
|
local timestamp
|
||||||
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
|
||||||
@@ -88,14 +89,130 @@ log_ban_history() {
|
|||||||
echo "#───────────────────────────────────────────────────────────────────────────────" >> "$BAN_HISTORY_FILE"
|
echo "#───────────────────────────────────────────────────────────────────────────────" >> "$BAN_HISTORY_FILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local duration="-"
|
if [[ -z "$duration" && "$action" == "BAN" ]]; then
|
||||||
[[ "$action" == "BAN" ]] && duration="${BAN_DURATION}s"
|
duration="${BAN_DURATION}s"
|
||||||
|
fi
|
||||||
|
[[ -z "$duration" ]] && duration="-"
|
||||||
|
|
||||||
printf "%-19s | %-6s | %-39s | %-30s | %-8s | %-10s | %s\n" \
|
printf "%-19s | %-6s | %-39s | %-30s | %-8s | %-10s | %s\n" \
|
||||||
"$timestamp" "$action" "$client_ip" "${domain:--}" "${count:--}" "$duration" "${reason:-rate-limit}" \
|
"$timestamp" "$action" "$client_ip" "${domain:--}" "${count:--}" "$duration" "${reason:-rate-limit}" \
|
||||||
>> "$BAN_HISTORY_FILE"
|
>> "$BAN_HISTORY_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ─── Progressive Ban (Recidive) ─────────────────────────────────────────────
|
||||||
|
# Liest die aktuelle Offense-Stufe einer IP aus der Offense-Datei
|
||||||
|
get_offense_level() {
|
||||||
|
local client_ip="$1"
|
||||||
|
local offense_file="${STATE_DIR}/${client_ip//[:\/]/_}.offenses"
|
||||||
|
|
||||||
|
if [[ ! -f "$offense_file" ]]; then
|
||||||
|
echo "0"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local level last_offense now reset_after
|
||||||
|
level=$(grep '^OFFENSE_LEVEL=' "$offense_file" | cut -d= -f2)
|
||||||
|
last_offense=$(grep '^LAST_OFFENSE_EPOCH=' "$offense_file" | cut -d= -f2)
|
||||||
|
now=$(date '+%s')
|
||||||
|
reset_after="${PROGRESSIVE_BAN_RESET_AFTER:-86400}"
|
||||||
|
|
||||||
|
# Prüfen ob der Zähler abgelaufen ist (Reset nach Zeitraum ohne Vergehen)
|
||||||
|
if [[ -n "$last_offense" && $((now - last_offense)) -gt "$reset_after" ]]; then
|
||||||
|
log "INFO" "Progressive Ban: Offense-Zähler für $client_ip zurückgesetzt (>${reset_after}s ohne Vergehen)"
|
||||||
|
rm -f "$offense_file"
|
||||||
|
echo "0"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${level:-0}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Erhöht die Offense-Stufe einer IP und gibt die neue Stufe zurück
|
||||||
|
increment_offense_level() {
|
||||||
|
local client_ip="$1"
|
||||||
|
local offense_file="${STATE_DIR}/${client_ip//[:\/]/_}.offenses"
|
||||||
|
local current_level
|
||||||
|
current_level=$(get_offense_level "$client_ip")
|
||||||
|
local new_level=$((current_level + 1))
|
||||||
|
local now
|
||||||
|
now=$(date '+%s')
|
||||||
|
local now_readable
|
||||||
|
now_readable=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
# Erstes Vergehen merken (bevor Datei überschrieben wird)
|
||||||
|
local first_offense
|
||||||
|
first_offense=$(grep '^FIRST_OFFENSE=' "$offense_file" 2>/dev/null | cut -d= -f2)
|
||||||
|
[[ -z "$first_offense" ]] && first_offense="$now_readable"
|
||||||
|
|
||||||
|
cat > "$offense_file" << EOF
|
||||||
|
CLIENT_IP=$client_ip
|
||||||
|
OFFENSE_LEVEL=$new_level
|
||||||
|
LAST_OFFENSE_EPOCH=$now
|
||||||
|
LAST_OFFENSE=$now_readable
|
||||||
|
FIRST_OFFENSE=$first_offense
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "$new_level"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Berechnet die Sperrdauer basierend auf der Offense-Stufe
|
||||||
|
calculate_ban_duration() {
|
||||||
|
local offense_level="$1"
|
||||||
|
local base_duration="${BAN_DURATION:-3600}"
|
||||||
|
local multiplier="${PROGRESSIVE_BAN_MULTIPLIER:-2}"
|
||||||
|
local max_level="${PROGRESSIVE_BAN_MAX_LEVEL:-5}"
|
||||||
|
|
||||||
|
# Progressive Bans deaktiviert? → Standard-Dauer
|
||||||
|
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" != "true" ]]; then
|
||||||
|
echo "$base_duration"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Permanente Sperre ab max_level (0 = nie permanent)
|
||||||
|
if [[ "$max_level" -gt 0 && "$offense_level" -ge "$max_level" ]]; then
|
||||||
|
echo "0" # 0 = permanent
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Exponentielle Steigerung: base × multiplier^(level-1)
|
||||||
|
# Stufe 1: base × 1, Stufe 2: base × mult, Stufe 3: base × mult², ...
|
||||||
|
if [[ "$offense_level" -le 1 ]]; then
|
||||||
|
echo "$base_duration"
|
||||||
|
else
|
||||||
|
local power=$((offense_level - 1))
|
||||||
|
local factor=1
|
||||||
|
for ((i=0; i<power; i++)); do
|
||||||
|
factor=$((factor * multiplier))
|
||||||
|
done
|
||||||
|
echo $((base_duration * factor))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Formatiert Sekunden in eine lesbare Dauer-Angabe
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
# Setzt den Offense-Zähler einer IP zurück
|
||||||
|
reset_offense_level() {
|
||||||
|
local client_ip="$1"
|
||||||
|
local offense_file="${STATE_DIR}/${client_ip//[:\/]/_}.offenses"
|
||||||
|
rm -f "$offense_file"
|
||||||
|
}
|
||||||
|
|
||||||
# ─── Verzeichnisse erstellen ──────────────────────────────────────────────────
|
# ─── Verzeichnisse erstellen ──────────────────────────────────────────────────
|
||||||
init_directories() {
|
init_directories() {
|
||||||
mkdir -p "$STATE_DIR"
|
mkdir -p "$STATE_DIR"
|
||||||
@@ -173,8 +290,6 @@ ban_client() {
|
|||||||
local client_ip="$1"
|
local client_ip="$1"
|
||||||
local domain="$2"
|
local domain="$2"
|
||||||
local count="$3"
|
local count="$3"
|
||||||
local ban_until
|
|
||||||
ban_until=$(date -d "+${BAN_DURATION} seconds" '+%s' 2>/dev/null || date -v "+${BAN_DURATION}S" '+%s')
|
|
||||||
|
|
||||||
# Prüfen ob bereits gesperrt
|
# Prüfen ob bereits gesperrt
|
||||||
local state_file="${STATE_DIR}/${client_ip//[:\/]/_}.ban"
|
local state_file="${STATE_DIR}/${client_ip//[:\/]/_}.ban"
|
||||||
@@ -183,13 +298,48 @@ ban_client() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Progressive Ban: Offense-Level ermitteln und Sperrdauer berechnen
|
||||||
|
local offense_level=0
|
||||||
|
local effective_duration="$BAN_DURATION"
|
||||||
|
local is_permanent=false
|
||||||
|
|
||||||
|
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" ]]; then
|
||||||
|
offense_level=$(increment_offense_level "$client_ip")
|
||||||
|
effective_duration=$(calculate_ban_duration "$offense_level")
|
||||||
|
|
||||||
|
if [[ "$effective_duration" -eq 0 ]]; then
|
||||||
|
is_permanent=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local ban_until
|
||||||
|
local ban_until_display
|
||||||
|
if [[ "$is_permanent" == "true" ]]; then
|
||||||
|
ban_until=0 # 0 = permanent
|
||||||
|
ban_until_display="PERMANENT"
|
||||||
|
else
|
||||||
|
ban_until=$(date -d "+${effective_duration} seconds" '+%s' 2>/dev/null || date -v "+${effective_duration}S" '+%s')
|
||||||
|
ban_until_display=$(date -d "@$ban_until" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date -r "$ban_until" '+%Y-%m-%d %H:%M:%S')
|
||||||
|
fi
|
||||||
|
|
||||||
|
local duration_display
|
||||||
|
duration_display=$(format_duration "$effective_duration")
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
log "WARN" "[DRY-RUN] WÜRDE sperren: $client_ip (${count}x $domain in ${RATE_LIMIT_WINDOW}s)"
|
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" ]]; then
|
||||||
log_ban_history "DRY" "$client_ip" "$domain" "$count" "dry-run"
|
log "WARN" "[DRY-RUN] WÜRDE sperren: $client_ip (${count}x $domain in ${RATE_LIMIT_WINDOW}s) für ${duration_display} [Stufe $offense_level]"
|
||||||
|
else
|
||||||
|
log "WARN" "[DRY-RUN] WÜRDE sperren: $client_ip (${count}x $domain in ${RATE_LIMIT_WINDOW}s)"
|
||||||
|
fi
|
||||||
|
log_ban_history "DRY" "$client_ip" "$domain" "$count" "dry-run" "${duration_display}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "WARN" "SPERRE Client: $client_ip (${count}x $domain in ${RATE_LIMIT_WINDOW}s) für ${BAN_DURATION}s"
|
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" ]]; then
|
||||||
|
log "WARN" "SPERRE Client: $client_ip (${count}x $domain in ${RATE_LIMIT_WINDOW}s) für ${duration_display} [Stufe ${offense_level}/${PROGRESSIVE_BAN_MAX_LEVEL:-0}]"
|
||||||
|
else
|
||||||
|
log "WARN" "SPERRE Client: $client_ip (${count}x $domain in ${RATE_LIMIT_WINDOW}s) für ${duration_display}"
|
||||||
|
fi
|
||||||
|
|
||||||
# IPv4 oder IPv6 erkennen
|
# IPv4 oder IPv6 erkennen
|
||||||
if [[ "$client_ip" == *:* ]]; then
|
if [[ "$client_ip" == *:* ]]; then
|
||||||
@@ -207,15 +357,20 @@ DOMAIN=$domain
|
|||||||
COUNT=$count
|
COUNT=$count
|
||||||
BAN_TIME=$(date '+%Y-%m-%d %H:%M:%S')
|
BAN_TIME=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
BAN_UNTIL_EPOCH=$ban_until
|
BAN_UNTIL_EPOCH=$ban_until
|
||||||
BAN_UNTIL=$(date -d "@$ban_until" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date -r "$ban_until" '+%Y-%m-%d %H:%M:%S')
|
BAN_UNTIL=$ban_until_display
|
||||||
|
BAN_DURATION=${effective_duration}
|
||||||
|
OFFENSE_LEVEL=$offense_level
|
||||||
|
IS_PERMANENT=$is_permanent
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Ban-History Eintrag
|
# Ban-History Eintrag
|
||||||
log_ban_history "BAN" "$client_ip" "$domain" "$count" "rate-limit"
|
local history_duration="${duration_display}"
|
||||||
|
[[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" ]] && history_duration="${duration_display} (Stufe ${offense_level})"
|
||||||
|
log_ban_history "BAN" "$client_ip" "$domain" "$count" "rate-limit" "$history_duration"
|
||||||
|
|
||||||
# Benachrichtigung senden
|
# Benachrichtigung senden
|
||||||
if [[ "$NOTIFY_ENABLED" == "true" ]]; then
|
if [[ "$NOTIFY_ENABLED" == "true" ]]; then
|
||||||
send_notification "ban" "$client_ip" "$domain" "$count"
|
send_notification "ban" "$client_ip" "$domain" "$count" "$offense_level" "$duration_display"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,6 +416,14 @@ check_expired_bans() {
|
|||||||
ban_until_epoch=$(grep '^BAN_UNTIL_EPOCH=' "$state_file" | cut -d= -f2)
|
ban_until_epoch=$(grep '^BAN_UNTIL_EPOCH=' "$state_file" | cut -d= -f2)
|
||||||
local client_ip
|
local client_ip
|
||||||
client_ip=$(grep '^CLIENT_IP=' "$state_file" | cut -d= -f2)
|
client_ip=$(grep '^CLIENT_IP=' "$state_file" | cut -d= -f2)
|
||||||
|
local is_permanent
|
||||||
|
is_permanent=$(grep '^IS_PERMANENT=' "$state_file" | cut -d= -f2)
|
||||||
|
|
||||||
|
# Permanente Sperren nicht automatisch aufheben
|
||||||
|
if [[ "$is_permanent" == "true" || "$ban_until_epoch" == "0" ]]; then
|
||||||
|
log "DEBUG" "Client $client_ip ist permanent gesperrt – überspringe"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -n "$ban_until_epoch" && "$now" -ge "$ban_until_epoch" ]]; then
|
if [[ -n "$ban_until_epoch" && "$now" -ge "$ban_until_epoch" ]]; then
|
||||||
unban_client "$client_ip" "expired"
|
unban_client "$client_ip" "expired"
|
||||||
@@ -274,12 +437,23 @@ send_notification() {
|
|||||||
local client_ip="$2"
|
local client_ip="$2"
|
||||||
local domain="$3"
|
local domain="$3"
|
||||||
local count="$4"
|
local count="$4"
|
||||||
|
local offense_level="${5:-}"
|
||||||
|
local duration_display="${6:-}"
|
||||||
|
|
||||||
[[ -z "$NOTIFY_WEBHOOK_URL" ]] && return
|
# Ntfy benötigt keine Webhook-URL (nutzt NTFY_SERVER_URL + NTFY_TOPIC)
|
||||||
|
if [[ "$NOTIFY_TYPE" != "ntfy" && -z "$NOTIFY_WEBHOOK_URL" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
local message
|
local message
|
||||||
if [[ "$action" == "ban" ]]; then
|
if [[ "$action" == "ban" ]]; then
|
||||||
message="🚫 AdGuard Shield: Client **$client_ip** gesperrt (${count}x $domain in ${RATE_LIMIT_WINDOW}s). Sperre für ${BAN_DURATION}s."
|
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" && -n "$offense_level" ]]; then
|
||||||
|
message="🚫 AdGuard Shield: Client **$client_ip** gesperrt (${count}x $domain in ${RATE_LIMIT_WINDOW}s). Sperre für **${duration_display}** [Stufe ${offense_level}/${PROGRESSIVE_BAN_MAX_LEVEL:-0}]."
|
||||||
|
else
|
||||||
|
local simple_dur
|
||||||
|
simple_dur=$(format_duration "${BAN_DURATION}")
|
||||||
|
message="🚫 AdGuard Shield: Client **$client_ip** gesperrt (${count}x $domain in ${RATE_LIMIT_WINDOW}s). Sperre für ${simple_dur}."
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
message="✅ AdGuard Shield: Client **$client_ip** wurde entsperrt."
|
message="✅ AdGuard Shield: Client **$client_ip** wurde entsperrt."
|
||||||
fi
|
fi
|
||||||
@@ -470,26 +644,73 @@ show_status() {
|
|||||||
echo "═══════════════════════════════════════════════════════════════"
|
echo "═══════════════════════════════════════════════════════════════"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# Progressive Ban Info
|
||||||
|
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" ]]; then
|
||||||
|
echo " 📈 Progressive Sperren: AKTIV"
|
||||||
|
echo " Multiplikator: ×${PROGRESSIVE_BAN_MULTIPLIER:-2}"
|
||||||
|
echo " Max-Stufe: ${PROGRESSIVE_BAN_MAX_LEVEL:-0} (0=kein Limit)"
|
||||||
|
echo " Zähler-Reset: $(format_duration "${PROGRESSIVE_BAN_RESET_AFTER:-86400}") ohne Vergehen"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
# Aktive Sperren
|
# Aktive Sperren
|
||||||
local ban_count=0
|
local ban_count=0
|
||||||
if [[ -d "$STATE_DIR" ]]; then
|
if [[ -d "$STATE_DIR" ]]; then
|
||||||
for state_file in "${STATE_DIR}"/*.ban; do
|
for state_file in "${STATE_DIR}"/*.ban; do
|
||||||
[[ -f "$state_file" ]] || continue
|
[[ -f "$state_file" ]] || continue
|
||||||
ban_count=$((ban_count + 1))
|
ban_count=$((ban_count + 1))
|
||||||
echo " 🚫 Gesperrt:"
|
local s_ip s_domain s_level s_perm s_dur s_until
|
||||||
while IFS='=' read -r key value; do
|
s_ip=$(grep '^CLIENT_IP=' "$state_file" | cut -d= -f2)
|
||||||
printf " %-20s %s\n" "$key:" "$value"
|
s_domain=$(grep '^DOMAIN=' "$state_file" | cut -d= -f2)
|
||||||
done < "$state_file"
|
s_level=$(grep '^OFFENSE_LEVEL=' "$state_file" | cut -d= -f2)
|
||||||
echo ""
|
s_perm=$(grep '^IS_PERMANENT=' "$state_file" | cut -d= -f2)
|
||||||
|
s_dur=$(grep '^BAN_DURATION=' "$state_file" | cut -d= -f2)
|
||||||
|
s_until=$(grep '^BAN_UNTIL=' "$state_file" | cut -d= -f2)
|
||||||
|
|
||||||
|
if [[ "$s_perm" == "true" ]]; then
|
||||||
|
echo " 🚫 Gesperrt: $s_ip → $s_domain [PERMANENT, Stufe ${s_level:-?}]"
|
||||||
|
elif [[ -n "$s_level" && "$s_level" -gt 0 ]]; then
|
||||||
|
echo " 🚫 Gesperrt: $s_ip → $s_domain [Stufe ${s_level}, $(format_duration "${s_dur:-$BAN_DURATION}"), bis $s_until]"
|
||||||
|
else
|
||||||
|
echo " 🚫 Gesperrt: $s_ip → $s_domain [bis $s_until]"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
if [[ $ban_count -eq 0 ]]; then
|
if [[ $ban_count -eq 0 ]]; then
|
||||||
echo " ✅ Keine aktiven Sperren"
|
echo " ✅ Keine aktiven Sperren"
|
||||||
else
|
else
|
||||||
echo " Gesamt: $ban_count aktive Sperren"
|
echo " Gesamt: $ban_count aktive Sperren"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Offense-Informationen anzeigen (Wiederholungstäter)
|
||||||
|
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" && -d "$STATE_DIR" ]]; then
|
||||||
|
local offense_count=0
|
||||||
|
local offense_output=""
|
||||||
|
for offense_file in "${STATE_DIR}"/*.offenses; do
|
||||||
|
[[ -f "$offense_file" ]] || continue
|
||||||
|
local o_ip o_level o_last
|
||||||
|
o_ip=$(grep '^CLIENT_IP=' "$offense_file" | cut -d= -f2)
|
||||||
|
o_level=$(grep '^OFFENSE_LEVEL=' "$offense_file" | cut -d= -f2)
|
||||||
|
o_last=$(grep '^LAST_OFFENSE=' "$offense_file" | cut -d= -f2)
|
||||||
|
offense_count=$((offense_count + 1))
|
||||||
|
local next_dur
|
||||||
|
next_dur=$(calculate_ban_duration "$((o_level + 1))")
|
||||||
|
if [[ "$next_dur" -eq 0 ]]; then
|
||||||
|
offense_output+=" ⚠ $o_ip: Stufe $o_level (letztes Vergehen: $o_last) → nächste Sperre: PERMANENT\n"
|
||||||
|
else
|
||||||
|
offense_output+=" ⚠ $o_ip: Stufe $o_level (letztes Vergehen: $o_last) → nächste Sperre: $(format_duration "$next_dur")\n"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $offense_count -gt 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo " 📋 Wiederholungstäter ($offense_count IPs mit Vorgeschichte):"
|
||||||
|
echo -e "$offense_output"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# iptables Regeln anzeigen
|
# iptables Regeln anzeigen
|
||||||
@@ -557,6 +778,21 @@ flush_all_bans() {
|
|||||||
log "INFO" "Alle Sperren aufgehoben"
|
log "INFO" "Alle Sperren aufgehoben"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ─── Alle Offense-Zähler zurücksetzen ────────────────────────────────────────
|
||||||
|
flush_all_offenses() {
|
||||||
|
local count=0
|
||||||
|
for offense_file in "${STATE_DIR}"/*.offenses; do
|
||||||
|
[[ -f "$offense_file" ]] || continue
|
||||||
|
local o_ip
|
||||||
|
o_ip=$(grep '^CLIENT_IP=' "$offense_file" | cut -d= -f2)
|
||||||
|
log "INFO" "Offense-Zähler zurückgesetzt: $o_ip"
|
||||||
|
rm -f "$offense_file"
|
||||||
|
count=$((count + 1))
|
||||||
|
done
|
||||||
|
log "INFO" "$count Offense-Zähler zurückgesetzt"
|
||||||
|
echo "$count Offense-Zähler zurückgesetzt"
|
||||||
|
}
|
||||||
|
|
||||||
# ─── Externer Blocklist-Worker starten ───────────────────────────────────────
|
# ─── Externer Blocklist-Worker starten ───────────────────────────────────────
|
||||||
start_blocklist_worker() {
|
start_blocklist_worker() {
|
||||||
if [[ "${EXTERNAL_BLOCKLIST_ENABLED:-false}" != "true" ]]; then
|
if [[ "${EXTERNAL_BLOCKLIST_ENABLED:-false}" != "true" ]]; then
|
||||||
@@ -595,11 +831,16 @@ main_loop() {
|
|||||||
log "INFO" "═══════════════════════════════════════════════════════════"
|
log "INFO" "═══════════════════════════════════════════════════════════"
|
||||||
log "INFO" "AdGuard Shield v${VERSION} gestartet"
|
log "INFO" "AdGuard Shield v${VERSION} gestartet"
|
||||||
log "INFO" " Limit: ${RATE_LIMIT_MAX_REQUESTS} Anfragen pro ${RATE_LIMIT_WINDOW}s"
|
log "INFO" " Limit: ${RATE_LIMIT_MAX_REQUESTS} Anfragen pro ${RATE_LIMIT_WINDOW}s"
|
||||||
log "INFO" " Sperrdauer: ${BAN_DURATION}s"
|
log "INFO" " Sperrdauer: $(format_duration "${BAN_DURATION}")"
|
||||||
log "INFO" " Prüfintervall: ${CHECK_INTERVAL}s"
|
log "INFO" " Prüfintervall: ${CHECK_INTERVAL}s"
|
||||||
log "INFO" " Dry-Run: ${DRY_RUN}"
|
log "INFO" " Dry-Run: ${DRY_RUN}"
|
||||||
log "INFO" " Whitelist: ${WHITELIST}"
|
log "INFO" " Whitelist: ${WHITELIST}"
|
||||||
log "INFO" " Externe Blocklist: ${EXTERNAL_BLOCKLIST_ENABLED:-false}"
|
log "INFO" " Externe Blocklist: ${EXTERNAL_BLOCKLIST_ENABLED:-false}"
|
||||||
|
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" ]]; then
|
||||||
|
log "INFO" " Progressive Sperren: AKTIV (×${PROGRESSIVE_BAN_MULTIPLIER:-2}, Max-Stufe: ${PROGRESSIVE_BAN_MAX_LEVEL:-0}, Reset: $(format_duration "${PROGRESSIVE_BAN_RESET_AFTER:-86400}"))"
|
||||||
|
else
|
||||||
|
log "INFO" " Progressive Sperren: deaktiviert"
|
||||||
|
fi
|
||||||
log "INFO" "═══════════════════════════════════════════════════════════"
|
log "INFO" "═══════════════════════════════════════════════════════════"
|
||||||
|
|
||||||
# Blocklist-Worker als Hintergrundprozess starten
|
# Blocklist-Worker als Hintergrundprozess starten
|
||||||
@@ -680,6 +921,15 @@ case "${1:-start}" in
|
|||||||
flush_all_bans
|
flush_all_bans
|
||||||
echo "Alle Sperren aufgehoben"
|
echo "Alle Sperren aufgehoben"
|
||||||
;;
|
;;
|
||||||
|
reset-offenses)
|
||||||
|
init_directories
|
||||||
|
if [[ -n "${2:-}" ]]; then
|
||||||
|
reset_offense_level "$2"
|
||||||
|
echo "Offense-Zähler für $2 zurückgesetzt"
|
||||||
|
else
|
||||||
|
flush_all_offenses
|
||||||
|
fi
|
||||||
|
;;
|
||||||
unban)
|
unban)
|
||||||
if [[ -z "${2:-}" ]]; then
|
if [[ -z "${2:-}" ]]; then
|
||||||
echo "Nutzung: $0 unban <IP-Adresse>" >&2
|
echo "Nutzung: $0 unban <IP-Adresse>" >&2
|
||||||
@@ -718,15 +968,16 @@ case "${1:-start}" in
|
|||||||
cat << USAGE
|
cat << USAGE
|
||||||
AdGuard Shield v${VERSION}
|
AdGuard Shield v${VERSION}
|
||||||
|
|
||||||
Nutzung: $0 {start|stop|status|history|flush|unban|test|dry-run|blocklist-status|blocklist-sync|blocklist-flush}
|
Nutzung: $0 {start|stop|status|history|flush|unban|reset-offenses|test|dry-run|blocklist-status|blocklist-sync|blocklist-flush}
|
||||||
|
|
||||||
Befehle:
|
Befehle:
|
||||||
start Startet den Monitor (inkl. Blocklist-Worker)
|
start Startet den Monitor (inkl. Blocklist-Worker)
|
||||||
stop Stoppt den Monitor
|
stop Stoppt den Monitor
|
||||||
status Zeigt aktive Sperren und Regeln
|
status Zeigt aktive Sperren, Regeln und Wiederholungstäter
|
||||||
history [N] Zeigt die letzten N Ban-Einträge (Standard: 50)
|
history [N] Zeigt die letzten N Ban-Einträge (Standard: 50)
|
||||||
flush Hebt alle Sperren auf
|
flush Hebt alle Sperren auf
|
||||||
unban IP Entsperrt eine bestimmte IP-Adresse
|
unban IP Entsperrt eine bestimmte IP-Adresse
|
||||||
|
reset-offenses [IP] Setzt Offense-Zähler zurück (alle oder eine bestimmte IP)
|
||||||
test Testet die Verbindung zur AdGuard Home API
|
test Testet die Verbindung zur AdGuard Home API
|
||||||
dry-run Startet im Testmodus (keine echten Sperren)
|
dry-run Startet im Testmodus (keine echten Sperren)
|
||||||
blocklist-status Zeigt Status der externen Blocklisten
|
blocklist-status Zeigt Status der externen Blocklisten
|
||||||
|
|||||||
@@ -34,11 +34,13 @@
|
|||||||
3. Die Anfragen werden pro Client+Domain-Kombination gezählt
|
3. Die Anfragen werden pro Client+Domain-Kombination gezählt
|
||||||
4. Monitor erkennt: 45 > 30 (Limit überschritten)
|
4. Monitor erkennt: 45 > 30 (Limit überschritten)
|
||||||
5. Prüfung: Ist der Client auf der Whitelist? → Nein
|
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
|
6. **Progressive Sperren:** Offense-Level wird geprüft/erhöht, Sperrdauer berechnet
|
||||||
7. State-Datei wird angelegt: `/var/lib/adguard-shield/192.168.1.50.ban`
|
7. iptables-Regel wird erstellt: `DROP` für `192.168.1.50` auf allen DNS-Ports
|
||||||
8. Ban-History Eintrag wird in `/var/log/adguard-shield-bans.log` geschrieben
|
8. State-Datei wird angelegt: `/var/lib/adguard-shield/192.168.1.50.ban`
|
||||||
9. Log-Eintrag + optionale Webhook-Benachrichtigung
|
9. Offense-Datei wird aktualisiert: `/var/lib/adguard-shield/192.168.1.50.offenses`
|
||||||
10. Nach 3600 Sekunden (1 Stunde): automatische Entsperrung + History-Eintrag
|
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
|
||||||
|
|
||||||
## iptables Strategie
|
## iptables Strategie
|
||||||
|
|
||||||
@@ -82,12 +84,31 @@ COUNT=45
|
|||||||
BAN_TIME=2026-03-03 14:30:00
|
BAN_TIME=2026-03-03 14:30:00
|
||||||
BAN_UNTIL_EPOCH=1741012200
|
BAN_UNTIL_EPOCH=1741012200
|
||||||
BAN_UNTIL=2026-03-03 15:30:00
|
BAN_UNTIL=2026-03-03 15:30:00
|
||||||
|
BAN_DURATION=3600
|
||||||
|
OFFENSE_LEVEL=1
|
||||||
|
IS_PERMANENT=false
|
||||||
|
```
|
||||||
|
|
||||||
|
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:
|
Das ermöglicht:
|
||||||
- Persistenz über Script-Neustarts hinweg
|
- Persistenz über Script-Neustarts hinweg
|
||||||
- Statusabfragen jederzeit möglich
|
- Statusabfragen jederzeit möglich
|
||||||
- Automatisches Aufräumen per Cron-Job
|
- Automatisches Aufräumen per Cron-Job
|
||||||
|
- Progressive Sperrzeiten über mehrere Ban-Zyklen hinweg
|
||||||
|
|
||||||
## Dateistruktur nach Installation
|
## Dateistruktur nach Installation
|
||||||
|
|
||||||
@@ -105,6 +126,7 @@ Das ermöglicht:
|
|||||||
|
|
||||||
/var/lib/adguard-shield/
|
/var/lib/adguard-shield/
|
||||||
├── *.ban # State-Dateien aktiver Sperren
|
├── *.ban # State-Dateien aktiver Sperren
|
||||||
|
├── *.offenses # Offense-Zähler (Progressive Sperren)
|
||||||
└── external-blocklist/ # Cache für externe Blocklisten
|
└── external-blocklist/ # Cache für externe Blocklisten
|
||||||
|
|
||||||
/var/log/
|
/var/log/
|
||||||
|
|||||||
@@ -73,6 +73,12 @@ sudo /opt/adguard-shield/adguard-shield.sh test
|
|||||||
# Dry-Run (nur loggen, nichts sperren)
|
# Dry-Run (nur loggen, nichts sperren)
|
||||||
sudo /opt/adguard-shield/adguard-shield.sh dry-run
|
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
|
# Externe Blocklist - Status anzeigen
|
||||||
sudo /opt/adguard-shield/adguard-shield.sh blocklist-status
|
sudo /opt/adguard-shield/adguard-shield.sh blocklist-status
|
||||||
|
|
||||||
|
|||||||
@@ -41,11 +41,34 @@ Dadurch muss der Benutzer bei Updates die Konfiguration nicht manuell austausche
|
|||||||
|
|
||||||
| Parameter | Standard | Beschreibung |
|
| Parameter | Standard | Beschreibung |
|
||||||
|-----------|----------|--------------|
|
|-----------|----------|--------------|
|
||||||
| `BAN_DURATION` | `3600` | Sperrdauer in Sekunden (3600 = 1 Stunde) |
|
| `BAN_DURATION` | `3600` | Basis-Sperrdauer in Sekunden (3600 = 1 Stunde) |
|
||||||
| `IPTABLES_CHAIN` | `ADGUARD_SHIELD` | Name der iptables Chain |
|
| `IPTABLES_CHAIN` | `ADGUARD_SHIELD` | Name der iptables Chain |
|
||||||
| `BLOCKED_PORTS` | `53 443 853 784 8853` | Ports die gesperrt werden |
|
| `BLOCKED_PORTS` | `53 443 853` | Ports die gesperrt werden (IPv4 + IPv6) |
|
||||||
| `WHITELIST` | `127.0.0.1,::1` | IPs die nie gesperrt werden (kommagetrennt) |
|
| `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:** Der Offense-Zähler wird automatisch zurückgesetzt, wenn eine IP für den konfigurierten Zeitraum (`PROGRESSIVE_BAN_RESET_AFTER`) kein erneutes Vergehen begeht. Permanente Sperren werden **nicht** automatisch aufgehoben – sie müssen manuell mit `unban` oder `flush` entfernt werden.
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
|
|
||||||
| Parameter | Standard | Beschreibung |
|
| Parameter | Standard | Beschreibung |
|
||||||
@@ -59,9 +82,13 @@ Dadurch muss der Benutzer bei Updates die Konfiguration nicht manuell austausche
|
|||||||
|
|
||||||
| Parameter | Standard | Beschreibung |
|
| Parameter | Standard | Beschreibung |
|
||||||
|-----------|----------|--------------|
|
|-----------|----------|--------------|
|
||||||
| `NOTIFY_ENABLED` | `false` | Webhook-Benachrichtigungen aktivieren |
|
| `NOTIFY_ENABLED` | `false` | Benachrichtigungen aktivieren |
|
||||||
| `NOTIFY_WEBHOOK_URL` | *(leer)* | Webhook-URL |
|
| `NOTIFY_TYPE` | `ntfy` | Typ: `ntfy`, `discord`, `slack`, `gotify`, `generic` |
|
||||||
| `NOTIFY_TYPE` | `generic` | Typ: `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) |
|
||||||
|
|
||||||
### Erweitert
|
### Erweitert
|
||||||
|
|
||||||
@@ -75,7 +102,7 @@ Dadurch muss der Benutzer bei Updates die Konfiguration nicht manuell austausche
|
|||||||
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.
|
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 |
|
| Parameter | Standard | Beschreibung |
|
||||||
|-----------|----------|}--------------|
|
|-----------|----------|--------------|
|
||||||
| `EXTERNAL_BLOCKLIST_ENABLED` | `false` | Aktiviert den externen Blocklist-Worker |
|
| `EXTERNAL_BLOCKLIST_ENABLED` | `false` | Aktiviert den externen Blocklist-Worker |
|
||||||
| `EXTERNAL_BLOCKLIST_URLS` | *(leer)* | URL(s) zu Textdateien mit IPs (kommagetrennt) |
|
| `EXTERNAL_BLOCKLIST_URLS` | *(leer)* | URL(s) zu Textdateien mit IPs (kommagetrennt) |
|
||||||
| `EXTERNAL_BLOCKLIST_INTERVAL` | `300` | Prüfintervall in Sekunden (300 = 5 Min.) |
|
| `EXTERNAL_BLOCKLIST_INTERVAL` | `300` | Prüfintervall in Sekunden (300 = 5 Min.) |
|
||||||
@@ -115,16 +142,14 @@ sudo systemctl restart adguard-shield
|
|||||||
```
|
```
|
||||||
## Gesperrte Ports im Detail
|
## Gesperrte Ports im Detail
|
||||||
|
|
||||||
Bei einem Rate-Limit-Verstoß werden **alle** DNS-Protokoll-Ports für den Client gesperrt:
|
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 |
|
| Port | Protokoll | Beschreibung |
|
||||||
|------|-----------|-------------|
|
|------|-----------|-------------|
|
||||||
| 53 | UDP/TCP | Standard DNS |
|
| 53 | UDP/TCP | Standard DNS |
|
||||||
| 443 | TCP | DNS-over-HTTPS (DoH) |
|
| 443 | TCP | DNS-over-HTTPS (DoH) |
|
||||||
| 853 | TCP | DNS-over-TLS (DoT) |
|
| 853 | TCP | DNS-over-TLS (`tls://dns1.techniverse.net:853`) |
|
||||||
| 853 | UDP | DNS-over-QUIC (DoQ) |
|
| 853 | UDP | DNS-over-QUIC (`quic://dns1.techniverse.net:853`) |
|
||||||
| 784 | UDP | DNS-over-QUIC (alternativ) |
|
|
||||||
| 8853 | UDP | DNS-over-QUIC (alternativ) |
|
|
||||||
|
|
||||||
## Whitelist richtig pflegen
|
## Whitelist richtig pflegen
|
||||||
|
|
||||||
|
|||||||
@@ -49,12 +49,30 @@ sudo /opt/adguard-shield/adguard-shield.sh start
|
|||||||
```bash
|
```bash
|
||||||
sudo /opt/adguard-shield/adguard-shield.sh history | grep 192.168.1.100
|
sudo /opt/adguard-shield/adguard-shield.sh history | grep 192.168.1.100
|
||||||
```
|
```
|
||||||
3. IP zur Whitelist hinzufügen in `adguard-shield.conf`
|
3. Offense-Zähler für die IP zurücksetzen (damit die progressive Sperre wieder bei Stufe 1 beginnt):
|
||||||
3. Service neustarten:
|
```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
|
```bash
|
||||||
sudo systemctl restart adguard-shield
|
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
|
### 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.
|
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.
|
||||||
|
|||||||
76
doc/update.md
Normal file
76
doc/update.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# 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 wird aktualisiert und `daemon-reload` ausgeführt
|
||||||
|
6. **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
|
||||||
|
```
|
||||||
@@ -51,6 +51,12 @@ for state_file in "${STATE_DIR}"/*.ban; do
|
|||||||
ban_until_epoch=$(grep '^BAN_UNTIL_EPOCH=' "$state_file" | cut -d= -f2)
|
ban_until_epoch=$(grep '^BAN_UNTIL_EPOCH=' "$state_file" | cut -d= -f2)
|
||||||
client_ip=$(grep '^CLIENT_IP=' "$state_file" | cut -d= -f2)
|
client_ip=$(grep '^CLIENT_IP=' "$state_file" | cut -d= -f2)
|
||||||
domain=$(grep '^DOMAIN=' "$state_file" | cut -d= -f2)
|
domain=$(grep '^DOMAIN=' "$state_file" | cut -d= -f2)
|
||||||
|
is_permanent=$(grep '^IS_PERMANENT=' "$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
|
if [[ -n "$ban_until_epoch" && "$NOW" -ge "$ban_until_epoch" ]]; then
|
||||||
echo "$LOG_PREFIX Entsperre abgelaufene Sperre: $client_ip" >> "$LOG_FILE"
|
echo "$LOG_PREFIX Entsperre abgelaufene Sperre: $client_ip" >> "$LOG_FILE"
|
||||||
|
|||||||
Reference in New Issue
Block a user