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 |
|
| `DRY_RUN` | `false` | Testmodus — nur loggen, nicht sperren |
|
||||||
### Externe Blocklist
|
### 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 |
|
| 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 Blocklist-Textdateien (kommagetrennt). Unterstützt IPv4, IPv6, CIDR und Hostnamen |
|
||||||
| `EXTERNAL_BLOCKLIST_INTERVAL` | `300` | Prüfintervall in Sekunden (300 = 5 Min.) |
|
| `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_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_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"`).
|
Die Kategorie `4` (DDoS Attack) wird standardmäßig verwendet. Weitere Kategorien können kommagetrennt angegeben werden (z.B. `"4,15"`).
|
||||||
#### Externe Blocklist einrichten
|
#### 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
|
```text
|
||||||
# Kommentare werden ignoriert
|
# Kommentare werden ignoriert
|
||||||
|
# Inline-Kommentare ebenfalls: 1.2.3.4 # dieser Kommentar wird entfernt
|
||||||
|
|
||||||
|
# IPv4
|
||||||
192.168.100.50
|
192.168.100.50
|
||||||
10.0.0.99
|
10.0.0.0/8
|
||||||
|
|
||||||
|
# IPv6
|
||||||
2001:db8::dead:beef
|
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:
|
2. Aktiviere die Blocklist in der Konfiguration:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -229,6 +246,27 @@ EXTERNAL_BLOCKLIST_URLS="https://example.com/list1.txt,https://other.com/list2.t
|
|||||||
```bash
|
```bash
|
||||||
sudo systemctl restart adguard-shield
|
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
|
## 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`):
|
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
|
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 ──────────────────────────────────────────
|
# ─── 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() {
|
parse_blocklist_ips() {
|
||||||
local cache_file="$1"
|
local cache_file="$1"
|
||||||
|
|
||||||
[[ -f "$cache_file" ]] || return
|
[[ -f "$cache_file" ]] || return
|
||||||
|
|
||||||
# Zeilen lesen, Leerzeilen und Kommentare ignorieren, IPs extrahieren
|
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
# Leerzeilen überspringen
|
# Leerzeilen und Kommentarzeilen überspringen
|
||||||
[[ -z "$line" ]] && continue
|
[[ -z "$line" ]] && continue
|
||||||
# Kommentare überspringen (# am Anfang)
|
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
|
||||||
# Whitespace trimmen
|
# Whitespace trimmen, dann Inline-Kommentare entfernen (# oder ;)
|
||||||
line=$(echo "$line" | xargs)
|
line=$(echo "$line" | xargs)
|
||||||
# Leere Zeilen nach Trim überspringen
|
line=$(echo "$line" | sed 's/[[:space:]]*[#;].*$//' | xargs)
|
||||||
[[ -z "$line" ]] && continue
|
[[ -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"
|
done < "$cache_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user