Files
adguard-shield/docs/architektur.md
2026-04-30 15:39:26 +02:00

12 KiB

Architektur & Funktionsweise

Überblick

┌─────────────────────┐
│   Client Anfragen   │
│  (DNS/DoH/DoT/DoQ)  │
└──────────┬──────────┘
           │
           ▼
┌─────────────────────┐      ┌──────────────────────┐
│   AdGuard Home      │────▶ │   Query Log (API)   │
│   DNS Server        │      └──────────┬───────────┘
└─────────────────────┘                │
                                       ▼
                            ┌──────────────────────┐
                            │  adguard-shield.sh   │
                            │  (Monitor Script)    │
                            └──────────┬───────────┘
                                       │
                        ┌──────────────┼──────────────┐
                        ▼              ▼              ▼
                ┌──────────┐   ┌──────────┐   ┌──────────┐
                │ iptables │   │   Log    │   │ Webhook  │
                │  DROP    │   │  Datei   │   │ Notify   │
                └──────────┘   └──────────┘   └──────────┘

Ablauf einer Sperre

Rate-Limit-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. Progressive Sperren: Offense-Level wird geprüft/erhöht, Sperrdauer berechnet
  7. iptables-Regel wird erstellt: DROP für 192.168.1.50 auf allen DNS-Ports
  8. State-Datei wird angelegt: /var/lib/adguard-shield/192.168.1.50.ban
  9. Offense-Datei wird aktualisiert: /var/lib/adguard-shield/192.168.1.50.offenses
  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

Subdomain-Flood-Sperre (Random Subdomain Attack)

  1. Client 10.0.0.99 fragt abc123.microsoft.com, xyz456.microsoft.com, ... ab
  2. Monitor extrahiert die Basisdomain (microsoft.com) aus jeder Anfrage
  3. Pro Client wird gezählt, wie viele eindeutige Subdomains einer Basisdomain im Zeitfenster abgefragt wurden
  4. Monitor erkennt: 63 eindeutige Subdomains > 50 (Schwellwert überschritten)
  5. Prüfung: Ist der Client auf der Whitelist? → Nein
  6. Sperre wird ausgeführt mit Domain *.microsoft.com und Grund subdomain-flood
  7. Progressive Sperren greifen auch hier — Wiederholungstäter werden stufenweise länger gesperrt

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:

INPUT Chain
  ├── ... (bestehende Regeln bleiben unberührt)
  ├── -p tcp --dport 53  → ADGUARD_SHIELD
  ├── -p udp --dport 53  → ADGUARD_SHIELD
  ├── -p tcp --dport 443 → ADGUARD_SHIELD
  ├── -p udp --dport 443 → ADGUARD_SHIELD
  ├── -p tcp --dport 853 → ADGUARD_SHIELD
  ├── -p udp --dport 853 → ADGUARD_SHIELD
  └── ...

ADGUARD_SHIELD 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_SHIELD

State-Management (SQLite)

Alle Laufzeitdaten werden in einer zentralen SQLite-Datenbank gespeichert:

/var/lib/adguard-shield/adguard-shield.db

Die Datenbank enthält folgende Tabellen:

Tabelle Beschreibung
active_bans Aktive Sperren (IP, Domain, Sperrdauer, Offense-Level, Grund, Quelle, GeoIP)
offense_tracking Offense-Zähler für progressive Sperren (Level, letztes/erstes Vergehen)
ban_history Vollständige Ban-History (alle Sperren und Entsperrungen)
whitelist_cache Cache der aufgelösten externen Whitelist-IPs
schema_version Datenbank-Schema-Version für zukünftige Migrationen

Vorteile gegenüber Flat-Files:

  • Schnellere Abfragen, besonders bei vielen aktiven Sperren
  • Atomare Transaktionen — kein Datenverlust bei Stromausfall
  • WAL-Modus für parallelen Lese-/Schreibzugriff
  • Indexierte Suche nach IP, Zeitstempel, Quelle und Aktion
  • Kompakte Speicherung statt tausender Einzeldateien

Die zentrale Datenbankbibliothek (db.sh) wird von allen Scripts per source db.sh eingebunden und stellt typisierte Funktionen für alle Tabellen bereit (z.B. db_ban_insert, db_offense_get_level, db_history_add).

Migration von Flat-Files

Beim Update auf die SQLite-Version werden bestehende Flat-File-Daten (.ban, .offenses, Ban-History-Log, Whitelist-Cache) automatisch in die Datenbank migriert. Die alten Dateien werden als Backup nach /var/lib/adguard-shield/.backup_pre_sqlite/ verschoben. Die Migration läuft einmalig beim Update und zeigt den Fortschritt im Terminal an.

Dateistruktur nach Installation

/opt/adguard-shield/
├── adguard-shield.sh              # Haupt-Monitor-Script
├── adguard-shield.conf            # Konfiguration (chmod 600)
├── adguard-shield.conf.old        # Backup der Konfig nach Update
├── adguard-shield-watchdog.sh     # Watchdog Health-Check-Script
├── iptables-helper.sh             # iptables Verwaltung
├── external-blocklist-worker.sh   # Externer Blocklist-Worker
├── external-whitelist-worker.sh   # Externer Whitelist-Worker (DNS-Auflösung)
├── geoip-worker.sh                # GeoIP-Länderfilter-Worker
├── offense-cleanup-worker.sh      # Aufräumen abgelaufener Offense-Zähler (nice 19, idle I/O)
├── db.sh                          # SQLite Datenbank-Bibliothek (wird von allen Scripts eingebunden)
├── unban-expired.sh               # Cron-basiertes Entsperren
└── geoip/                         # Auto-Download MaxMind GeoLite2 DB (optional)

/etc/systemd/system/
├── adguard-shield.service         # systemd Service (Autostart aktiv)
├── adguard-shield-watchdog.service # systemd Watchdog-Unit (oneshot)
└── adguard-shield-watchdog.timer  # systemd Timer (alle 5 Min.)

/var/lib/adguard-shield/
├── adguard-shield.db              # SQLite-Datenbank (Bans, Offenses, History, Whitelist-Cache)
├── .migration_v1_complete         # Marker: Flat-File-Migration abgeschlossen
├── .backup_pre_sqlite/            # Backup der alten Flat-Files nach Migration
├── external-blocklist/            # Cache für externe Blocklisten
├── external-whitelist/            # Cache für externe Whitelisten
└── geoip-cache/                   # Cache für GeoIP-Lookups (24h)

/var/log/
├── adguard-shield.log             # Laufzeit-Log
└── adguard-shield-bans.log        # Ban-History (Legacy, wird nach Migration nicht mehr geschrieben)

Installer-Architektur

Der Installer (install.sh) bietet ein interaktives Menü und folgende Funktionen:

Befehl Beschreibung
install Vollständige Neuinstallation (Abhängigkeiten, Dateien, Konfiguration, Service, Watchdog)
update Update mit automatischer Konfigurations-Migration, Datenbank-Migration, Watchdog-Aktivierung und Service-Neustart
uninstall Deinstallation mit optionalem Behalten der Konfiguration
status Installationsstatus, Version und Service-Status anzeigen
--help Hilfe und Befehlsübersicht

Konfigurations-Migration beim Update

┌─────────────────────────┐     ┌─────────────────────────┐
│   Bestehende Konfig     │     │   Neue Konfig (Repo)    │
│   (Benutzer-Settings)   │     │  (mit neuen Parametern) │
└───────────┬─────────────┘     └───────────┬─────────────┘
            │                               │
            ▼                               ▼
     ┌──────────────────────────────────────────┐
     │        Konfigurations-Migration          │
     │  1. Backup als .conf.old erstellen       │
     │  2. Alle Schlüssel vergleichen           │
     │  3. Neue Schlüssel zur Konfig ergänzen   │
     │  4. Bestehende Werte NICHT ändern        │
     └──────────────────────┬───────────────────┘
                            ▼
              ┌──────────────────────────┐
              │  Aktualisierte Konfig    │
              │ (alte Werte + neue Keys) │
              └──────────────────────────┘

Ban-History

Jede Sperre und Entsperrung wird dauerhaft in der SQLite-Datenbank protokolliert (Tabelle ban_history). Das ermöglicht eine lückenlose Nachvollziehbarkeit mit indexierter Suche nach IP, Zeitstempel und Aktion.

Gespeicherte Felder pro Eintrag:

Feld Beschreibung
timestamp_epoch Unix-Zeitstempel
timestamp_text Lesbarer Zeitstempel
action BAN oder UNBAN
client_ip Betroffene IP-Adresse
domain Angefragte Domain
count Anzahl der Anfragen
duration Sperrdauer
protocol Verwendetes DNS-Protokoll
reason Sperrgrund

Mögliche Gründe (GRUND-Spalte):

Grund Bedeutung
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
manual-flush Entsperrt durch flush-Befehl (alle Sperren aufgehoben)

History anzeigen:

sudo /opt/adguard-shield/adguard-shield.sh history       # letzte 50
sudo /opt/adguard-shield/adguard-shield.sh history 200   # letzte 200