This commit is contained in:
2026-03-03 19:27:37 +01:00
parent cc5ac0154d
commit a5fb3bcf25
14 changed files with 2840 additions and 5 deletions

138
doc/architektur.md Normal file
View File

@@ -0,0 +1,138 @@
# Architektur & Funktionsweise
## Überblick
```
┌─────────────────────┐
│ Client Anfragen │
│ (DNS/DoH/DoT/DoQ) │
└──────────┬──────────┘
┌─────────────────────┐ ┌──────────────────────┐
│ AdGuard Home │────▶│ Query Log (API) │
│ DNS Server │ └──────────┬───────────┘
└─────────────────────┘ │
┌──────────────────────┐
│ adguard-ratelimit.sh │
│ (Monitor Script) │
└──────────┬───────────┘
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ iptables │ │ Log │ │ Webhook │
│ DROP │ │ Datei │ │ Notify │
└──────────┘ └──────────┘ └──────────┘
```
## Ablauf einer 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-ratelimit/192.168.1.50.ban`
8. Ban-History Eintrag wird in `/var/log/adguard-ratelimit-bans.log` geschrieben
9. Log-Eintrag + optionale Webhook-Benachrichtigung
10. Nach 3600 Sekunden (1 Stunde): automatische Entsperrung + History-Eintrag
## iptables Strategie
Das Tool erstellt eine eigene Chain `ADGUARD_RATELIMIT`:
```
INPUT Chain
├── ... (bestehende Regeln bleiben unberührt)
├── -p tcp --dport 53 → ADGUARD_RATELIMIT
├── -p udp --dport 53 → ADGUARD_RATELIMIT
├── -p tcp --dport 443 → ADGUARD_RATELIMIT
├── -p udp --dport 443 → ADGUARD_RATELIMIT
├── -p tcp --dport 853 → ADGUARD_RATELIMIT
├── -p udp --dport 853 → ADGUARD_RATELIMIT
└── ...
ADGUARD_RATELIMIT Chain
├── -s 192.168.1.50 → DROP (gesperrter Client)
├── -s 10.0.0.25 → DROP (gesperrter Client)
└── RETURN (alle anderen passieren)
```
**Vorteile der eigenen Chain:**
- Greift nicht in bestehende Firewall-Regeln ein
- Kann komplett geflusht werden ohne andere Regeln zu beeinflussen
- Einfaches Debugging per `iptables -L ADGUARD_RATELIMIT`
## State-Management
Jede aktive Sperre wird als Datei gespeichert:
```
/var/lib/adguard-ratelimit/192.168.1.50.ban
```
Inhalt:
```
CLIENT_IP=192.168.1.50
DOMAIN=microsoft.com
COUNT=45
BAN_TIME=2026-03-03 14:30:00
BAN_UNTIL_EPOCH=1741012200
BAN_UNTIL=2026-03-03 15:30:00
```
Das ermöglicht:
- Persistenz über Script-Neustarts hinweg
- Statusabfragen jederzeit möglich
- Automatisches Aufräumen per Cron-Job
## Dateistruktur nach Installation
```
/opt/adguard-ratelimit/
├── adguard-ratelimit.sh # Haupt-Monitor-Script
├── adguard-ratelimit.conf # Konfiguration (chmod 600)
├── iptables-helper.sh # iptables Verwaltung
└── unban-expired.sh # Cron-basiertes Entsperren
/etc/systemd/system/
└── adguard-ratelimit.service
/var/lib/adguard-ratelimit/
└── *.ban # State-Dateien aktiver Sperren
/var/log/
├── adguard-ratelimit.log # Laufzeit-Log
└── adguard-ratelimit-bans.log # Ban-History (alle Sperren/Entsperrungen)
```
## Ban-History
Jede Sperre und Entsperrung wird dauerhaft in der Ban-History protokolliert (`/var/log/adguard-ratelimit-bans.log`). Das ermöglicht eine lückenlose Nachvollziehbarkeit, auch nachdem State-Dateien bereits gelöscht wurden.
**Format:**
```
ZEITSTEMPEL | AKTION | CLIENT-IP | DOMAIN | ANFRAGEN | SPERRDAUER | GRUND
2026-03-03 14:30:12 | BAN | 192.168.1.50 | microsoft.com | 45 | 3600s | rate-limit
2026-03-03 15:30:12 | UNBAN | 192.168.1.50 | microsoft.com | - | - | expired
2026-03-03 16:10:33 | UNBAN | 10.0.0.25 | telemetry.example.com | - | - | manual
```
**Mögliche Gründe (GRUND-Spalte):**
| Grund | Bedeutung |
|-------|----------|
| `rate-limit` | Automatische Sperre wegen Limit-Überschreitung |
| `dry-run` | Im Dry-Run erkannt (nicht wirklich gesperrt) |
| `expired` | Automatisch entsperrt nach Ablauf der Sperrdauer |
| `expired-cron` | Entsperrt durch den Cron-Job (`unban-expired.sh`) |
| `manual` | Manuell entsperrt per `unban`-Befehl |
| `manual-flush` | Entsperrt durch `flush`-Befehl (alle Sperren aufgehoben) |
**History anzeigen:**
```bash
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh history # letzte 50
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh history 200 # letzte 200
```

134
doc/befehle.md Normal file
View File

@@ -0,0 +1,134 @@
# Befehle & Nutzung
## Monitor (Hauptscript)
```bash
# Starten
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh start
# Stoppen
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh stop
# Status + aktive Sperren anzeigen
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh status
# Ban-History anzeigen (letzte 50 Einträge)
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh history
# Ban-History anzeigen (letzte 100 Einträge)
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh history 100
# Alle Sperren aufheben
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh flush
# Einzelne IP entsperren
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh unban 192.168.1.100
# API-Verbindung testen
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh test
# Dry-Run (nur loggen, nichts sperren)
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh dry-run
# Externe Blocklist - Status anzeigen
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh blocklist-status
# Externe Blocklist - Einmalige Synchronisation
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh blocklist-sync
# Externe Blocklist - Alle Sperren der externen Liste aufheben
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh blocklist-flush
```
## iptables Helper
Für die manuelle Verwaltung der Firewall-Regeln:
```bash
# Chain erstellen
sudo /opt/adguard-ratelimit/iptables-helper.sh create
# Alle Regeln anzeigen
sudo /opt/adguard-ratelimit/iptables-helper.sh status
# IP manuell sperren
sudo /opt/adguard-ratelimit/iptables-helper.sh ban 192.168.1.100
# IP entsperren
sudo /opt/adguard-ratelimit/iptables-helper.sh unban 192.168.1.100
# Alle Regeln leeren
sudo /opt/adguard-ratelimit/iptables-helper.sh flush
# Chain komplett entfernen
sudo /opt/adguard-ratelimit/iptables-helper.sh remove
# Regeln speichern / wiederherstellen
sudo /opt/adguard-ratelimit/iptables-helper.sh save
sudo /opt/adguard-ratelimit/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-ratelimit/external-blocklist-worker.sh start
# Worker stoppen
sudo /opt/adguard-ratelimit/external-blocklist-worker.sh stop
# Einmalige Synchronisation (z.B. nach Konfigurationsänderung)
sudo /opt/adguard-ratelimit/external-blocklist-worker.sh sync
# Status anzeigen
sudo /opt/adguard-ratelimit/external-blocklist-worker.sh status
# Alle externen Sperren aufheben
sudo /opt/adguard-ratelimit/external-blocklist-worker.sh flush
```
## systemd Service
```bash
# Start / Stop / Restart
sudo systemctl start adguard-ratelimit
sudo systemctl stop adguard-ratelimit
sudo systemctl restart adguard-ratelimit
# Status
sudo systemctl status adguard-ratelimit
# Autostart aktivieren / deaktivieren
sudo systemctl enable adguard-ratelimit
sudo systemctl disable adguard-ratelimit
```
## Logs
```bash
# systemd Journal
sudo journalctl -u adguard-ratelimit -f
# Log-Datei direkt
sudo tail -f /var/log/adguard-ratelimit.log
# Nur Sperr-Einträge
sudo grep "SPERRE" /var/log/adguard-ratelimit.log
# Nur Entsperr-Einträge
sudo grep "ENTSPERRE" /var/log/adguard-ratelimit.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-ratelimit/unban-expired.sh
```

110
doc/benachrichtigungen.md Normal file
View File

@@ -0,0 +1,110 @@
# Webhook-Benachrichtigungen
Das Tool kann bei Sperren und Entsperrungen Benachrichtigungen an verschiedene Dienste senden.
## Aktivierung
In der Konfiguration (`adguard-ratelimit.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-ratelimit"
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": "🚫 DNS Rate-Limit: Client 192.168.1.50 gesperrt ...",
"action": "ban",
"client": "192.168.1.50",
"domain": "microsoft.com"
}
```
## Beispiel-Nachrichten
**Sperre:**
> 🚫 DNS Rate-Limit: Client **192.168.1.50** gesperrt (45x microsoft.com in 60s). Sperre für 3600s.
**Entsperrung:**
> ✅ DNS Rate-Limit: Client **192.168.1.50** wurde entsperrt.

130
doc/konfiguration.md Normal file
View File

@@ -0,0 +1,130 @@
# Konfiguration
Die Konfigurationsdatei liegt nach der Installation unter:
```
/opt/adguard-ratelimit/adguard-ratelimit.conf
```
## 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_RATELIMIT` | 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-ratelimit.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-ratelimit-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-ratelimit` | Verzeichnis für State-Dateien |
| `PID_FILE` | `/var/run/adguard-ratelimit.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-ratelimit/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-ratelimit
```
## 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"
```

View File

@@ -0,0 +1,94 @@
# Tipps & Troubleshooting
## Best Practices
- **Erst immer im Dry-Run testen**, bevor der scharfe Modus aktiviert wird
```bash
sudo /opt/adguard-ratelimit/adguard-ratelimit.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-ratelimit/adguard-ratelimit.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-ratelimit/adguard-ratelimit.sh start
```
### Client wird fälschlich gesperrt
1. Client sofort entsperren:
```bash
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh unban 192.168.1.100
```
2. In der Ban-History prüfen, warum gesperrt wurde:
```bash
sudo /opt/adguard-ratelimit/adguard-ratelimit.sh history | grep 192.168.1.100
```
3. IP zur Whitelist hinzufügen in `adguard-ratelimit.conf`
3. Service neustarten:
```bash
sudo systemctl restart adguard-ratelimit
```
### 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-ratelimit.pid
sudo systemctl start adguard-ratelimit
```
## Deinstallation
```bash
sudo bash install.sh uninstall
```
Oder manuell:
```bash
sudo systemctl stop adguard-ratelimit
sudo systemctl disable adguard-ratelimit
sudo /opt/adguard-ratelimit/iptables-helper.sh remove
sudo rm -rf /opt/adguard-ratelimit
sudo rm -f /etc/systemd/system/adguard-ratelimit.service
sudo systemctl daemon-reload
```