Merge pull request 'v0.9.0' (#18) from v0.9.0 into main
Reviewed-on: #18
This commit was merged in pull request #18.
This commit is contained in:
@@ -32,6 +32,7 @@ Das schützt klassische DNS-Anfragen genauso wie DoH, DoT und DoQ, ohne deine be
|
||||
|
||||
- Automatische Sperren bei Rate-Limit-Verstößen
|
||||
- Erkennung von Random-Subdomain-Floods, z.B. `abc123.example.com`
|
||||
- DNS-Flood-Watchlist: sofortiger permanenter Ban + AbuseIPDB-Meldung für definierte Domains
|
||||
- Progressive Sperren für Wiederholungstäter, ähnlich wie bei fail2ban
|
||||
- Unterstützung für DNS, DoH, DoT, DoQ und DNSCrypt
|
||||
- IPv4 und IPv6
|
||||
@@ -127,6 +128,7 @@ Wichtige Startpunkte:
|
||||
- `RATE_LIMIT_MAX_REQUESTS`, `RATE_LIMIT_WINDOW` und `CHECK_INTERVAL` für die Erkennung
|
||||
- `BAN_DURATION` und `PROGRESSIVE_BAN_*` für temporäre und progressive Sperren
|
||||
- `WHITELIST` für vertrauenswürdige Clients wie Router, Management-IPs oder lokale Resolver
|
||||
- `DNS_FLOOD_WATCHLIST_*` für sofortigen Permanent-Ban bei bekannten Flood-Domains
|
||||
- `NOTIFY_*`, `REPORT_*`, `GEOIP_*`, `EXTERNAL_BLOCKLIST_*` und `EXTERNAL_WHITELIST_*` für optionale Funktionen
|
||||
|
||||
Bei Updates migriert der Installer die bestehende Konfiguration automatisch: vorhandene Werte bleiben erhalten, neue Parameter werden ergänzt und die alte Datei wird als `adguard-shield.conf.old` gesichert.
|
||||
|
||||
@@ -18,6 +18,10 @@ SUBDOMAIN_FLOOD_ENABLED=true
|
||||
SUBDOMAIN_FLOOD_MAX_UNIQUE=50 # Max. eindeutige Subdomains pro Basisdomain/Client
|
||||
SUBDOMAIN_FLOOD_WINDOW=60 # Zeitfenster in Sekunden
|
||||
|
||||
# --- DNS-Flood-Watchlist ---
|
||||
DNS_FLOOD_WATCHLIST_ENABLED=false
|
||||
DNS_FLOOD_WATCHLIST="" # Kommagetrennt, z.B. "example.com,evil.org"
|
||||
|
||||
# --- Sperr-Einstellungen ---
|
||||
BAN_DURATION=3600 # Basis-Sperrdauer in Sekunden
|
||||
IPTABLES_CHAIN="ADGUARD_SHIELD"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# Lizenz: MIT
|
||||
###############################################################################
|
||||
|
||||
VERSION="v0.8.2"
|
||||
VERSION="v0.9.0"
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -369,6 +369,32 @@ is_whitelisted() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# ─── DNS-Flood-Watchlist Prüfung ────────────────────────────────────────────
|
||||
is_dns_flood_watchlist_match() {
|
||||
local domain="$1"
|
||||
|
||||
if [[ "${DNS_FLOOD_WATCHLIST_ENABLED:-false}" != "true" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -z "${DNS_FLOOD_WATCHLIST:-}" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local entry
|
||||
IFS=',' read -ra watchlist_entries <<< "$DNS_FLOOD_WATCHLIST"
|
||||
for entry in "${watchlist_entries[@]}"; do
|
||||
entry=$(echo "$entry" | xargs)
|
||||
[[ -z "$entry" ]] && continue
|
||||
|
||||
if [[ "$domain" == "$entry" || "$domain" == *".$entry" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# ─── iptables Chain Setup ────────────────────────────────────────────────────
|
||||
setup_iptables_chain() {
|
||||
# IPv4 Chain erstellen falls nicht vorhanden
|
||||
@@ -425,6 +451,13 @@ ban_client() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# DNS-Flood-Watchlist: Sofort permanent sperren
|
||||
if [[ "$reason" == "dns-flood-watchlist" ]]; then
|
||||
is_permanent=true
|
||||
effective_duration=0
|
||||
log "WARN" "DNS-Flood-Watchlist: Erzwinge permanente Sperre für $client_ip ($domain)"
|
||||
fi
|
||||
|
||||
local ban_until
|
||||
local ban_until_display
|
||||
if [[ "$is_permanent" == "true" ]]; then
|
||||
@@ -439,16 +472,16 @@ ban_client() {
|
||||
duration_display=$(format_duration "$effective_duration")
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" ]]; then
|
||||
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" && "$reason" != "dns-flood-watchlist" ]]; then
|
||||
log "WARN" "[DRY-RUN] WÜRDE sperren: $client_ip (${count}x $domain in ${window}s via $protocol) für ${duration_display} [Stufe $offense_level] [${reason}]"
|
||||
else
|
||||
log "WARN" "[DRY-RUN] WÜRDE sperren: $client_ip (${count}x $domain in ${window}s via $protocol) [${reason}]"
|
||||
log "WARN" "[DRY-RUN] WÜRDE sperren: $client_ip (${count}x $domain in ${window}s via $protocol) für ${duration_display} [${reason}]"
|
||||
fi
|
||||
log_ban_history "DRY" "$client_ip" "$domain" "$count" "dry-run (${reason})" "${duration_display}" "$protocol"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" ]]; then
|
||||
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" && "$reason" != "dns-flood-watchlist" ]]; then
|
||||
log "WARN" "SPERRE Client: $client_ip (${count}x $domain in ${window}s via $protocol) für ${duration_display} [Stufe ${offense_level}/${PROGRESSIVE_BAN_MAX_LEVEL:-0}] [${reason}]"
|
||||
else
|
||||
log "WARN" "SPERRE Client: $client_ip (${count}x $domain in ${window}s via $protocol) für ${duration_display} [${reason}]"
|
||||
@@ -480,7 +513,7 @@ EOF
|
||||
|
||||
# Ban-History Eintrag
|
||||
local history_duration="${duration_display}"
|
||||
[[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" ]] && history_duration="${duration_display} (Stufe ${offense_level})"
|
||||
[[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" && "$reason" != "dns-flood-watchlist" ]] && history_duration="${duration_display} (Stufe ${offense_level})"
|
||||
log_ban_history "BAN" "$client_ip" "$domain" "$count" "$reason" "$history_duration" "$protocol"
|
||||
|
||||
# Benachrichtigung senden
|
||||
@@ -574,6 +607,7 @@ send_notification() {
|
||||
|
||||
local reason_label="Rate-Limit"
|
||||
[[ "$reason" == "subdomain-flood" ]] && reason_label="Subdomain-Flood"
|
||||
[[ "$reason" == "dns-flood-watchlist" ]] && reason_label="DNS-Flood-Watchlist"
|
||||
|
||||
local title
|
||||
local message
|
||||
@@ -591,12 +625,12 @@ send_notification() {
|
||||
abuseipdb_hint=$'\n⚠️ IP wurde an AbuseIPDB gemeldet'
|
||||
fi
|
||||
|
||||
# Dauer-Anzeige mit Stufe
|
||||
# Dauer-Anzeige mit Stufe (nicht bei Watchlist – dort ist es immer permanent)
|
||||
local dur_line
|
||||
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" && -n "$offense_level" ]]; then
|
||||
if [[ "${PROGRESSIVE_BAN_ENABLED:-false}" == "true" && -n "$offense_level" && "$reason" != "dns-flood-watchlist" ]]; then
|
||||
dur_line="**${duration_display}** [Stufe ${offense_level}/${PROGRESSIVE_BAN_MAX_LEVEL:-0}]"
|
||||
else
|
||||
dur_line=$(format_duration "${BAN_DURATION}")
|
||||
dur_line="**${duration_display}**"
|
||||
fi
|
||||
|
||||
message="🚫 AdGuard Shield Ban auf ${my_hostname}${abuseipdb_hint}
|
||||
@@ -839,7 +873,13 @@ analyze_queries() {
|
||||
continue
|
||||
fi
|
||||
|
||||
ban_client "$client" "$domain" "$count" "rate-limit" "$RATE_LIMIT_WINDOW" "$proto_display"
|
||||
local ban_reason="rate-limit"
|
||||
if is_dns_flood_watchlist_match "$domain"; then
|
||||
ban_reason="dns-flood-watchlist"
|
||||
log "WARN" "DNS-Flood-Watchlist Treffer: $client → $domain (${count}x in ${RATE_LIMIT_WINDOW}s) → permanenter Ban + AbuseIPDB"
|
||||
fi
|
||||
|
||||
ban_client "$client" "$domain" "$count" "$ban_reason" "$RATE_LIMIT_WINDOW" "$proto_display"
|
||||
fi
|
||||
done <<< "$violations"
|
||||
}
|
||||
@@ -961,7 +1001,13 @@ analyze_subdomain_flood() {
|
||||
continue
|
||||
fi
|
||||
|
||||
ban_client "$client" "*.${base_domain}" "$unique_count" "subdomain-flood" "$window" "$proto_display"
|
||||
local ban_reason="subdomain-flood"
|
||||
if is_dns_flood_watchlist_match "$base_domain"; then
|
||||
ban_reason="dns-flood-watchlist"
|
||||
log "WARN" "DNS-Flood-Watchlist Treffer (Subdomain-Flood): $client → *.${base_domain} → permanenter Ban + AbuseIPDB"
|
||||
fi
|
||||
|
||||
ban_client "$client" "*.${base_domain}" "$unique_count" "$ban_reason" "$window" "$proto_display"
|
||||
done <<< "$violations"
|
||||
}
|
||||
|
||||
@@ -989,6 +1035,14 @@ show_status() {
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# DNS-Flood-Watchlist Info
|
||||
if [[ "${DNS_FLOOD_WATCHLIST_ENABLED:-false}" == "true" ]]; then
|
||||
echo " 🎯 DNS-Flood-Watchlist: AKTIV"
|
||||
echo " Domains: ${DNS_FLOOD_WATCHLIST:-<keine>}"
|
||||
echo " Aktion: Sofort permanenter Ban + AbuseIPDB-Meldung"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# GeoIP-Filter Info
|
||||
if [[ "${GEOIP_ENABLED:-false}" == "true" ]]; then
|
||||
local geoip_mode_label
|
||||
@@ -1021,6 +1075,7 @@ show_status() {
|
||||
|
||||
local reason_tag=""
|
||||
[[ "$s_reason" == "subdomain-flood" ]] && reason_tag=" (Subdomain-Flood)"
|
||||
[[ "$s_reason" == "dns-flood-watchlist" ]] && reason_tag=" (DNS-Flood-Watchlist)"
|
||||
|
||||
local count_info=""
|
||||
if [[ -n "$s_count" && "$s_count" != "-" ]]; then
|
||||
@@ -1033,7 +1088,9 @@ show_status() {
|
||||
|
||||
local proto_tag=" via ${s_proto}"
|
||||
|
||||
if [[ "$s_perm" == "true" ]]; then
|
||||
if [[ "$s_perm" == "true" && "$s_reason" == "dns-flood-watchlist" ]]; then
|
||||
echo " 🚫 Gesperrt: $s_ip → $s_domain [PERMANENT${count_info}${proto_tag}]${reason_tag}"
|
||||
elif [[ "$s_perm" == "true" ]]; then
|
||||
echo " 🚫 Gesperrt: $s_ip → $s_domain [PERMANENT, Stufe ${s_level:-?}${count_info}${proto_tag}]${reason_tag}"
|
||||
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${count_info}${proto_tag}]${reason_tag}"
|
||||
@@ -1312,6 +1369,11 @@ main_loop() {
|
||||
else
|
||||
log "INFO" " Subdomain-Flood-Schutz: deaktiviert"
|
||||
fi
|
||||
if [[ "${DNS_FLOOD_WATCHLIST_ENABLED:-false}" == "true" ]]; then
|
||||
log "INFO" " DNS-Flood-Watchlist: AKTIV (Domains: ${DNS_FLOOD_WATCHLIST:-<keine>})"
|
||||
else
|
||||
log "INFO" " DNS-Flood-Watchlist: deaktiviert"
|
||||
fi
|
||||
if [[ "${ABUSEIPDB_ENABLED:-false}" == "true" ]]; then
|
||||
log "INFO" " AbuseIPDB Reporting: AKTIV (Kategorien: ${ABUSEIPDB_CATEGORIES:-4})"
|
||||
else
|
||||
|
||||
@@ -56,6 +56,17 @@
|
||||
|
||||
> **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.
|
||||
|
||||
### DNS-Flood-Watchlist-Sperre
|
||||
|
||||
1. Client `10.0.0.42` fragt `microsoft.com` 35x in 60 Sekunden an
|
||||
2. Monitor erkennt: 35 > 30 (Limit überschritten)
|
||||
3. Domain `microsoft.com` steht auf der DNS-Flood-Watchlist → **sofortige permanente Sperre**
|
||||
4. Progressive-Ban-Stufe wird ignoriert — kein stufenweises Hochstufen
|
||||
5. IP wird an AbuseIPDB gemeldet (falls aktiviert)
|
||||
6. Permanente Sperre bleibt bis zur manuellen Freigabe aktiv
|
||||
|
||||
> **Hinweis:** Die Watchlist greift sowohl bei normalen Rate-Limit-Verstößen als auch bei Subdomain-Flood-Erkennungen. Subdomains werden automatisch erkannt: `foo.microsoft.com` matcht den Watchlist-Eintrag `microsoft.com`.
|
||||
|
||||
## iptables Strategie
|
||||
|
||||
Das Tool erstellt eine eigene Chain `ADGUARD_SHIELD`:
|
||||
@@ -210,8 +221,10 @@ ZEITSTEMPEL | AKTION | CLIENT-IP | DOMAIN
|
||||
|-------|----------|
|
||||
| `rate-limit` | Automatische Sperre wegen Limit-Überschreitung |
|
||||
| `subdomain-flood` | Sperre wegen zu vieler eindeutiger Subdomains einer Basisdomain |
|
||||
| `dns-flood-watchlist` | Sofortige permanente Sperre + AbuseIPDB-Meldung (Domain auf der Watchlist) |
|
||||
| `dry-run` | Im Dry-Run erkannt (nicht wirklich gesperrt) |
|
||||
| `dry-run (subdomain-flood)` | Subdomain-Flood im Dry-Run erkannt |
|
||||
| `dry-run (dns-flood-watchlist)` | Watchlist-Treffer 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 |
|
||||
|
||||
@@ -116,12 +116,12 @@ Bei Sperren aus der **externen Blocklist** werden Benachrichtigungen separat üb
|
||||
### Service gestartet
|
||||
**Überschrift:** ✅ AdGuard Shield
|
||||
|
||||
> 🟢 AdGuard Shield v0.8.2 wurde auf dns1 gestartet.
|
||||
> 🟢 AdGuard Shield v0.9.0 wurde auf dns1 gestartet.
|
||||
|
||||
### Service gestoppt
|
||||
**Überschrift:** 🚨 🛡️ AdGuard Shield
|
||||
|
||||
> 🔴 AdGuard Shield v0.8.2 wurde auf dns1 gestoppt.
|
||||
> 🔴 AdGuard Shield v0.9.0 wurde auf dns1 gestoppt.
|
||||
|
||||
### Watchdog — Service wiederhergestellt
|
||||
**Überschrift:** 🔄 AdGuard Shield Watchdog
|
||||
@@ -167,6 +167,19 @@ Bei permanenter Sperre mit aktiviertem AbuseIPDB-Reporting erscheint zusätzlich
|
||||
> Whois: https://www.whois.com/whois/95.71.42.116
|
||||
> AbuseIPDB: https://www.abuseipdb.com/check/95.71.42.116
|
||||
|
||||
Bei DNS-Flood-Watchlist-Treffer (sofort permanent, ohne Stufe):
|
||||
|
||||
> 🚫 AdGuard Shield Ban auf dns1
|
||||
> ⚠️ IP wurde an AbuseIPDB gemeldet
|
||||
> ---
|
||||
> IP: 95.71.42.116
|
||||
> Hostname: example-host.provider.net
|
||||
> Grund: 45x microsoft.com in 60s via DNS, DNS-Flood-Watchlist
|
||||
> Dauer: PERMANENT
|
||||
>
|
||||
> 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
|
||||
|
||||
|
||||
@@ -70,6 +70,34 @@ xk9z3a.microsoft.com
|
||||
|
||||
> **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.
|
||||
|
||||
### DNS-Flood-Watchlist
|
||||
|
||||
Domains bei denen eine Rate-Limit-Überschreitung **sofort** zu einer **permanenten Sperre** und einer **AbuseIPDB-Meldung** führt — ohne progressive Eskalation. Ideal für bekannte Angriffsziele, die regelmäßig geflutet werden (z.B. `microsoft.com`, `google.com`).
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
|-----------|----------|--------------|
|
||||
| `DNS_FLOOD_WATCHLIST_ENABLED` | `false` | DNS-Flood-Watchlist aktivieren |
|
||||
| `DNS_FLOOD_WATCHLIST` | *(leer)* | Überwachte Domains, kommagetrennt (z.B. `"microsoft.com,google.com"`) |
|
||||
|
||||
#### Wie funktioniert die Watchlist?
|
||||
|
||||
1. Die reguläre Rate-Limit-Prüfung erkennt, dass ein Client mehr als `RATE_LIMIT_MAX_REQUESTS` Anfragen für eine Domain gestellt hat
|
||||
2. Zusätzlich wird geprüft, ob die angefragte Domain in der Watchlist steht (inkl. Subdomains: `foo.microsoft.com` matcht `microsoft.com`)
|
||||
3. Trifft beides zu → **sofortige permanente Sperre** + **AbuseIPDB-Meldung** (falls aktiviert)
|
||||
|
||||
Die Watchlist greift sowohl bei normalen Rate-Limit-Verstößen als auch bei Subdomain-Flood-Erkennungen.
|
||||
|
||||
#### Beispiel
|
||||
|
||||
```bash
|
||||
DNS_FLOOD_WATCHLIST_ENABLED=true
|
||||
DNS_FLOOD_WATCHLIST="microsoft.com,google.com,apple.com"
|
||||
```
|
||||
|
||||
→ Ein Client der `35x foo.microsoft.com` in 60s abfragt (bei `RATE_LIMIT_MAX_REQUESTS=30`) wird **sofort permanent** gesperrt und an AbuseIPDB gemeldet.
|
||||
|
||||
> **Hinweis:** Damit die AbuseIPDB-Meldung funktioniert, muss `ABUSEIPDB_ENABLED=true` und ein gültiger `ABUSEIPDB_API_KEY` konfiguriert sein. Ohne AbuseIPDB-Konfiguration wird nur permanent gesperrt.
|
||||
|
||||
### Sperr-Einstellungen
|
||||
|
||||
| Parameter | Standard | Beschreibung |
|
||||
@@ -251,7 +279,7 @@ sudo systemctl restart adguard-shield
|
||||
|
||||
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 Rate-Limit / DNS-Flood-Watchlist:** `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"`).
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Lizenz: MIT
|
||||
###############################################################################
|
||||
|
||||
VERSION="v0.8.2"
|
||||
VERSION="v0.9.0"
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
Reference in New Issue
Block a user