Blocklisten mit Hostnames werden nun in IPs aufglöst und können importiert werden.
This commit is contained in:
@@ -151,12 +151,12 @@ Regelmäßige Statistik-Reports per E-Mail. Voraussetzung ist ein funktionierend
|
||||
| `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.
|
||||
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 Textdateien mit IPs (kommagetrennt) |
|
||||
| `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 |
|
||||
@@ -201,15 +201,32 @@ Der Report an AbuseIPDB enthält (auf Englisch):
|
||||
Die Kategorie `4` (DDoS Attack) wird standardmäßig verwendet. Weitere Kategorien können kommagetrennt angegeben werden (z.B. `"4,15"`).
|
||||
#### Externe Blocklist einrichten
|
||||
|
||||
1. Erstelle eine Textdatei auf einem Webserver mit einer IP pro Zeile:
|
||||
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.99
|
||||
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.
|
||||
|
||||
2. Aktiviere die Blocklist in der Konfiguration:
|
||||
|
||||
```bash
|
||||
@@ -229,6 +246,27 @@ EXTERNAL_BLOCKLIST_URLS="https://example.com/list1.txt,https://other.com/list2.t
|
||||
```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`):
|
||||
|
||||
@@ -318,24 +318,147 @@ 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
|
||||
# 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)
|
||||
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"
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user