#!/bin/bash ############################################################################### # AdGuard Shield - GeoIP Worker # Prüft Client-IPs auf Herkunftsland und sperrt/erlaubt basierend auf Konfig. # Wird als Hintergrundprozess vom Hauptscript gestartet. # # Autor: Patrick Asmus # E-Mail: support@techniverse.net # Lizenz: MIT ############################################################################### set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CONFIG_FILE="${SCRIPT_DIR}/adguard-shield.conf" # ─── Konfiguration laden ─────────────────────────────────────────────────────── if [[ ! -f "$CONFIG_FILE" ]]; then echo "FEHLER: Konfigurationsdatei nicht gefunden: $CONFIG_FILE" >&2 exit 1 fi # shellcheck source=adguard-shield.conf source "$CONFIG_FILE" # shellcheck source=db.sh source "${SCRIPT_DIR}/db.sh" # ─── Worker PID-File ────────────────────────────────────────────────────────── WORKER_PID_FILE="/var/run/adguard-geoip-worker.pid" # ─── GeoIP Cache ────────────────────────────────────────────────────────────── GEOIP_CACHE_DIR="${STATE_DIR}/geoip-cache" # ─── MaxMind Auto-Download Verzeichnis ──────────────────────────────────────── GEOIP_DB_DIR="${SCRIPT_DIR}/geoip" GEOIP_AUTO_DB="${GEOIP_DB_DIR}/GeoLite2-Country.mmdb" GEOIP_DB_UPDATE_INTERVAL=86400 # 24 Stunden (fest) # ─── Logging (eigene Funktion, nutzt gleiche Log-Datei) ─────────────────────── declare -A LOG_LEVELS=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3) log() { local level="$1" shift local message="$*" local configured_level="${LOG_LEVEL:-INFO}" if [[ ${LOG_LEVELS[$level]:-1} -ge ${LOG_LEVELS[$configured_level]:-1} ]]; then local timestamp timestamp="$(date '+%Y-%m-%d %H:%M:%S')" local log_entry="[$timestamp] [$level] [GEOIP-WORKER] $message" echo "$log_entry" | tee -a "$LOG_FILE" >&2 fi } # ─── Ban-History ───────────────────────────────────────────────────────────── log_ban_history() { local action="$1" local client_ip="$2" local country="${3:-}" local reason="${4:-geoip}" db_history_add "$action" "$client_ip" "Land: ${country:-?}" "-" "$reason" "permanent" "-" } # ─── Verzeichnisse erstellen ────────────────────────────────────────────────── init_directories() { mkdir -p "$GEOIP_CACHE_DIR" mkdir -p "$GEOIP_DB_DIR" mkdir -p "$STATE_DIR" mkdir -p "$(dirname "$LOG_FILE")" db_init } # ─── Private IP-Adressen erkennen ──────────────────────────────────────────── is_private_ip() { local ip="$1" # IPv6 Loopback und Link-Local if [[ "$ip" == "::1" || "$ip" == fe80:* || "$ip" == fc00:* || "$ip" == fd00:* ]]; then return 0 fi # IPv4 private Bereiche if [[ "$ip" =~ ^10\. || "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. || "$ip" =~ ^192\.168\. || "$ip" =~ ^127\. || "$ip" == "0.0.0.0" ]]; then return 0 fi # IPv4 CGNAT if [[ "$ip" =~ ^100\.(6[4-9]|[7-9][0-9]|1[0-1][0-9]|12[0-7])\. ]]; then return 0 fi return 1 } # ─── Whitelist Prüfung ─────────────────────────────────────────────────────── is_whitelisted() { local ip="$1" IFS=',' read -ra wl_entries <<< "$WHITELIST" for entry in "${wl_entries[@]}"; do entry=$(echo "$entry" | xargs) if [[ "$ip" == "$entry" ]]; then return 0 fi done if db_whitelist_contains "$ip"; then return 0 fi return 1 } # ─── MaxMind GeoLite2 Auto-Download & Update ───────────────────────────────── # Lädt die GeoLite2-Country.mmdb herunter, wenn GEOIP_LICENSE_KEY gesetzt ist # und kein eigener GEOIP_MMDB_PATH angegeben wurde. # Aktualisiert automatisch alle 24 Stunden. update_maxmind_db() { local license_key="${GEOIP_LICENSE_KEY:-}" # Kein License-Key → nichts zu tun if [[ -z "$license_key" ]]; then return 0 fi # User hat eigenen Pfad gesetzt → kein Auto-Download if [[ -n "${GEOIP_MMDB_PATH:-}" ]]; then return 0 fi # Prüfen ob Update nötig (alle 24h) if [[ -f "$GEOIP_AUTO_DB" ]]; then local db_age db_age=$(( $(date '+%s') - $(stat -c '%Y' "$GEOIP_AUTO_DB" 2>/dev/null || stat -f '%m' "$GEOIP_AUTO_DB" 2>/dev/null || echo "0") )) if [[ "$db_age" -lt "$GEOIP_DB_UPDATE_INTERVAL" ]]; then log "DEBUG" "MaxMind DB ist aktuell (Alter: $((db_age / 3600))h, nächstes Update in $(( (GEOIP_DB_UPDATE_INTERVAL - db_age) / 3600 ))h)" return 0 fi log "INFO" "MaxMind DB ist älter als 24h – starte Update..." else log "INFO" "MaxMind DB nicht vorhanden – starte Erstdownload..." fi # Download-URL zusammenbauen (MaxMind Permalink) local download_url="https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${license_key}&suffix=tar.gz" local tmp_file="${GEOIP_DB_DIR}/GeoLite2-Country.tar.gz" local tmp_extract="${GEOIP_DB_DIR}/extract_tmp" # Herunterladen local http_code http_code=$(curl -s -o "$tmp_file" -w "%{http_code}" \ --connect-timeout 10 \ --max-time 60 \ "$download_url" 2>/dev/null) || true if [[ "$http_code" != "200" ]]; then rm -f "$tmp_file" case "$http_code" in 401) log "ERROR" "MaxMind Download fehlgeschlagen: Ungültiger License-Key (HTTP 401)" ;; 403) log "ERROR" "MaxMind Download fehlgeschlagen: Zugriff verweigert (HTTP 403) – License-Key prüfen" ;; *) log "ERROR" "MaxMind Download fehlgeschlagen (HTTP ${http_code:-timeout})" ;; esac return 1 fi # Entpacken rm -rf "$tmp_extract" mkdir -p "$tmp_extract" if ! tar -xzf "$tmp_file" -C "$tmp_extract" 2>/dev/null; then log "ERROR" "MaxMind DB: tar-Archiv konnte nicht entpackt werden" rm -f "$tmp_file" rm -rf "$tmp_extract" return 1 fi # .mmdb Datei finden und verschieben local mmdb_file mmdb_file=$(find "$tmp_extract" -name 'GeoLite2-Country.mmdb' -type f 2>/dev/null | head -1) if [[ -z "$mmdb_file" || ! -f "$mmdb_file" ]]; then log "ERROR" "MaxMind DB: GeoLite2-Country.mmdb nicht im Archiv gefunden" rm -f "$tmp_file" rm -rf "$tmp_extract" return 1 fi mv "$mmdb_file" "$GEOIP_AUTO_DB" rm -f "$tmp_file" rm -rf "$tmp_extract" log "INFO" "MaxMind GeoLite2-Country DB erfolgreich aktualisiert: $GEOIP_AUTO_DB" return 0 } # ─── Effektiven MMDB-Pfad ermitteln ────────────────────────────────────────── # Priorität: GEOIP_MMDB_PATH (User) > Auto-Download > leer (Fallback auf geoiplookup) resolve_mmdb_path() { # User hat eigenen Pfad gesetzt if [[ -n "${GEOIP_MMDB_PATH:-}" && -f "${GEOIP_MMDB_PATH:-}" ]]; then echo "$GEOIP_MMDB_PATH" return 0 fi # Auto-Download DB vorhanden if [[ -f "$GEOIP_AUTO_DB" ]]; then echo "$GEOIP_AUTO_DB" return 0 fi # Kein MMDB verfügbar echo "" return 1 } # ─── GeoIP Lookup ──────────────────────────────────────────────────────────── # Gibt den ISO 3166-1 Alpha-2 Ländercode zurück (z.B. "DE", "US", "CN") # Nutzt Cache um wiederholte Lookups zu vermeiden geoip_lookup() { local ip="$1" local cache_file="${GEOIP_CACHE_DIR}/${ip//[:\/]/_}.country" # Cache prüfen (max 24 Stunden alt) if [[ -f "$cache_file" ]]; then local cache_age cache_age=$(( $(date '+%s') - $(stat -c '%Y' "$cache_file" 2>/dev/null || stat -f '%m' "$cache_file" 2>/dev/null || echo "0") )) if [[ "$cache_age" -lt 86400 ]]; then cat "$cache_file" return 0 fi fi local country_code="" # Effektiven MMDB-Pfad ermitteln (User-Pfad oder Auto-Download) local effective_mmdb effective_mmdb=$(resolve_mmdb_path 2>/dev/null) || true # Methode 1: MaxMind mmdbinspect (bevorzugt, genauer) if [[ -n "$effective_mmdb" && -f "$effective_mmdb" ]] && command -v mmdbinspect &>/dev/null; then country_code=$(mmdbinspect -db "$effective_mmdb" -ip "$ip" 2>/dev/null \ | jq -r '.[0].Records[0].Record.country.iso_code // empty' 2>/dev/null || true) fi # Methode 2: geoiplookup (GeoIP Legacy) if [[ -z "$country_code" ]] && command -v geoiplookup &>/dev/null; then if [[ "$ip" == *:* ]]; then # IPv6 if command -v geoiplookup6 &>/dev/null; then country_code=$(geoiplookup6 "$ip" 2>/dev/null \ | grep -oP '(?<=: )[A-Z]{2}(?=,)' | head -1 || true) fi else # IPv4 country_code=$(geoiplookup "$ip" 2>/dev/null \ | grep -oP '(?<=: )[A-Z]{2}(?=,)' | head -1 || true) fi fi # Methode 3: mmdblookup (libmaxminddb) if [[ -z "$country_code" && -n "$effective_mmdb" && -f "$effective_mmdb" ]] && command -v mmdblookup &>/dev/null; then country_code=$(mmdblookup --file "$effective_mmdb" --ip "$ip" country iso_code 2>/dev/null \ | grep -oP '"[A-Z]{2}"' | tr -d '"' | head -1 || true) fi if [[ -n "$country_code" ]]; then echo "$country_code" > "$cache_file" echo "$country_code" return 0 fi # Unbekannt – nicht cachen (könnte temporärer Fehler sein) echo "" return 1 } # ─── GeoIP Prüfung: Soll eine IP gesperrt werden? ──────────────────────────── # Return 0 = sperren, Return 1 = erlauben should_block_by_geoip() { local country_code="$1" local mode="${GEOIP_MODE:-blocklist}" local countries="${GEOIP_COUNTRIES:-}" [[ -z "$country_code" || -z "$countries" ]] && return 1 # Länder-Liste in Array umwandeln IFS=',' read -ra country_list <<< "$countries" local found=false for c in "${country_list[@]}"; do c=$(echo "$c" | xargs | tr '[:lower:]' '[:upper:]') # trim + uppercase if [[ "$country_code" == "$c" ]]; then found=true break fi done if [[ "$mode" == "blocklist" ]]; then # Blocklist-Modus: Sperren wenn Land in der Liste [[ "$found" == "true" ]] && return 0 || return 1 elif [[ "$mode" == "allowlist" ]]; then # Allowlist-Modus: Sperren wenn Land NICHT in der Liste [[ "$found" == "true" ]] && return 1 || return 0 fi return 1 } # ─── IP via iptables sperren ───────────────────────────────────────────────── ban_ip_geoip() { local client_ip="$1" local country_code="$2" local mode="${GEOIP_MODE:-blocklist}" # Prüfen ob bereits gesperrt if db_ban_exists "$client_ip"; then log "DEBUG" "GeoIP: $client_ip ist bereits gesperrt" return 0 fi # GeoIP-Sperren sind immer permanent local ban_until=0 local ban_until_display="PERMANENT" local reason_text if [[ "$mode" == "blocklist" ]]; then reason_text="geoip-blocklist (Land: $country_code)" else reason_text="geoip-allowlist (Land: $country_code)" fi log "WARN" "GeoIP SPERRE: $client_ip (Land: $country_code, Modus: $mode) PERMANENT" # iptables Regel setzen if [[ "$client_ip" == *:* ]]; then ip6tables -I "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true else iptables -I "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true fi # State in Datenbank speichern db_ban_insert "$client_ip" "GeoIP:${country_code}" "0" "$(date '+%Y-%m-%d %H:%M:%S')" "0" "0" "0" "1" "geoip" "-" "geoip" "$country_code" "$mode" # Ban-History log_ban_history "BAN" "$client_ip" "$country_code" "$reason_text" # Benachrichtigung senden if [[ "${GEOIP_NOTIFY:-true}" == "true" && "${NOTIFY_ENABLED:-false}" == "true" ]]; then send_geoip_notification "ban" "$client_ip" "$country_code" "PERMANENT" "$mode" fi } # ─── GeoIP Benachrichtigung ────────────────────────────────────────────────── send_geoip_notification() { local action="$1" local client_ip="$2" local country_code="$3" local duration="${4:-PERMANENT}" local mode="${5:-blocklist}" local my_hostname my_hostname=$(hostname) local title="🌍 🛡️ AdGuard Shield" local mode_label [[ "$mode" == "blocklist" ]] && mode_label="Blocklist" || mode_label="Allowlist" local message="🌍 AdGuard Shield GeoIP-Sperre auf ${my_hostname} --- IP: ${client_ip} Land: ${country_code} Modus: ${mode_label} Dauer: ${duration} Whois: https://www.whois.com/whois/${client_ip} AbuseIPDB: https://www.abuseipdb.com/check/${client_ip}" case "${NOTIFY_TYPE:-}" in discord) local json_payload json_payload=$(jq -nc --arg msg "$message" '{content: $msg}') curl -s -H "Content-Type: application/json" \ -d "$json_payload" \ "$NOTIFY_WEBHOOK_URL" &>/dev/null & ;; slack) local json_payload json_payload=$(jq -nc --arg msg "$message" '{text: $msg}') curl -s -H "Content-Type: application/json" \ -d "$json_payload" \ "$NOTIFY_WEBHOOK_URL" &>/dev/null & ;; gotify) local clean_message clean_message=$(echo "$message" | sed 's/\*\*//g') curl -s -X POST "$NOTIFY_WEBHOOK_URL" \ -F "title=${title}" \ -F "message=${clean_message}" \ -F "priority=5" &>/dev/null & ;; ntfy) local ntfy_url="${NTFY_SERVER_URL:-https://ntfy.sh}" local -a curl_args=( -s -X POST "${ntfy_url}/${NTFY_TOPIC}" -H "Title: 🛡️ AdGuard Shield GeoIP" -H "Priority: ${NTFY_PRIORITY:-4}" -H "Tags: globe_with_meridians,ban" -d "$message" ) [[ -n "${NTFY_TOKEN:-}" ]] && curl_args+=(-H "Authorization: Bearer ${NTFY_TOKEN}") curl "${curl_args[@]}" &>/dev/null & ;; generic) local json_payload json_payload=$(jq -nc --arg msg "$message" --arg cl "$client_ip" --arg cc "$country_code" \ '{message: $msg, action: "geoip-ban", client: $cl, country: $cc}') curl -s -H "Content-Type: application/json" \ -d "$json_payload" \ "$NOTIFY_WEBHOOK_URL" &>/dev/null & ;; esac } # ─── iptables Chain Setup ──────────────────────────────────────────────────── setup_iptables_chain() { if ! iptables -n -L "$IPTABLES_CHAIN" &>/dev/null; then iptables -N "$IPTABLES_CHAIN" for port in $BLOCKED_PORTS; do iptables -I INPUT -p tcp --dport "$port" -j "$IPTABLES_CHAIN" iptables -I INPUT -p udp --dport "$port" -j "$IPTABLES_CHAIN" done fi if ! ip6tables -n -L "$IPTABLES_CHAIN" &>/dev/null; then ip6tables -N "$IPTABLES_CHAIN" for port in $BLOCKED_PORTS; do ip6tables -I INPUT -p tcp --dport "$port" -j "$IPTABLES_CHAIN" ip6tables -I INPUT -p udp --dport "$port" -j "$IPTABLES_CHAIN" done fi } # ─── GeoIP-Tools Verfügbarkeit prüfen ──────────────────────────────────────── check_geoip_tools() { # Effektiven MMDB-Pfad ermitteln local effective_mmdb effective_mmdb=$(resolve_mmdb_path 2>/dev/null) || true # mmdbinspect + MMDB if [[ -n "$effective_mmdb" && -f "$effective_mmdb" ]]; then if command -v mmdbinspect &>/dev/null; then echo "mmdbinspect" return 0 elif command -v mmdblookup &>/dev/null; then echo "mmdblookup" return 0 fi fi # geoiplookup (Legacy GeoIP) if command -v geoiplookup &>/dev/null; then echo "geoiplookup" return 0 fi echo "none" return 1 } # ─── Client-IPs aus AdGuard API extrahieren ────────────────────────────────── get_active_clients() { local response response=$(curl -s -u "${ADGUARD_USER}:${ADGUARD_PASS}" \ --connect-timeout 5 \ --max-time 10 \ -k "${ADGUARD_URL}/control/querylog?limit=${API_QUERY_LIMIT:-500}&response_status=all" 2>/dev/null) if [[ -z "$response" || "$response" == "null" ]]; then log "ERROR" "Keine Antwort von AdGuard Home API" return 1 fi # Eindeutige Client-IPs extrahieren echo "$response" | jq -r '.data // [] | [.[].client // .[].client_info.ip] | unique | .[]' 2>/dev/null | sort -u } # ─── Auto-Unban: GeoIP-Sperren aufheben bei Konfigurationsänderung ──────────── # Prüft alle bestehenden GeoIP-Sperren und hebt sie auf, wenn: # - Das Land nicht mehr in GEOIP_COUNTRIES steht # - Der Modus gewechselt wurde (blocklist ↔ allowlist) # - GeoIP deaktiviert wurde auto_unban_geoip() { local unban_count=0 local geoip_bans geoip_bans=$(db_ban_get_by_reason "geoip") [[ -z "$geoip_bans" ]] && return while IFS='|' read -r client_ip domain count ban_time ban_until_epoch ban_duration offense_level is_permanent reason protocol source geoip_country geoip_mode; do [[ -z "$client_ip" ]] && continue local should_unban=false if [[ "${GEOIP_ENABLED:-false}" != "true" ]]; then should_unban=true elif [[ -n "$geoip_mode" && "$geoip_mode" != "${GEOIP_MODE:-blocklist}" ]]; then should_unban=true elif [[ -n "$geoip_country" ]] && ! should_block_by_geoip "$geoip_country"; then should_unban=true fi if [[ "$should_unban" == "true" ]]; then log "INFO" "GeoIP Auto-Unban: $client_ip (Land: ${geoip_country:-?}, war: ${geoip_mode:-?})" if [[ "$client_ip" == *:* ]]; then ip6tables -D "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true else iptables -D "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true fi db_ban_delete "$client_ip" log_ban_history "UNBAN" "$client_ip" "$geoip_country" "geoip-auto-unban" unban_count=$((unban_count + 1)) fi done <<< "$geoip_bans" if [[ $unban_count -gt 0 ]]; then log "INFO" "GeoIP Auto-Unban: $unban_count Sperren aufgehoben (Länderliste/Modus geändert)" fi } # ─── Einmaliger GeoIP-Sync ────────────────────────────────────────────────── sync_geoip() { # Auto-Unban zuerst: bestehende Sperren prüfen, die nicht mehr zur Config passen auto_unban_geoip if [[ "${GEOIP_ENABLED:-false}" != "true" ]]; then log "INFO" "GeoIP ist deaktiviert" return 0 fi # MaxMind DB automatisch herunterladen/aktualisieren (falls License-Key gesetzt) update_maxmind_db || true local countries="${GEOIP_COUNTRIES:-}" if [[ -z "$countries" ]]; then log "WARN" "GeoIP: Keine Länder konfiguriert (GEOIP_COUNTRIES ist leer)" return 0 fi local tool tool=$(check_geoip_tools) || { log "ERROR" "GeoIP: Kein GeoIP-Tool verfügbar. Installiere geoip-bin oder mmdbinspect." return 1 } log "INFO" "GeoIP-Sync gestartet (Tool: $tool, Modus: ${GEOIP_MODE:-blocklist}, Länder: $countries)" # Client-IPs aus der API holen local clients clients=$(get_active_clients) || { log "ERROR" "GeoIP: Konnte aktive Clients nicht ermitteln" return 1 } local checked=0 local blocked=0 local skipped=0 while IFS= read -r client_ip; do [[ -z "$client_ip" || "$client_ip" == "null" ]] && continue # Private IPs überspringen if [[ "${GEOIP_SKIP_PRIVATE:-true}" == "true" ]] && is_private_ip "$client_ip"; then log "DEBUG" "GeoIP: Private IP übersprungen: $client_ip" skipped=$((skipped + 1)) continue fi # Whitelist prüfen if is_whitelisted "$client_ip"; then log "DEBUG" "GeoIP: Whitelisted IP übersprungen: $client_ip" skipped=$((skipped + 1)) continue fi # Bereits gesperrt? if db_ban_exists "$client_ip"; then skipped=$((skipped + 1)) continue fi checked=$((checked + 1)) # GeoIP Lookup local country_code country_code=$(geoip_lookup "$client_ip") || true if [[ -z "$country_code" ]]; then log "DEBUG" "GeoIP: Kein Ergebnis für $client_ip" continue fi log "DEBUG" "GeoIP: $client_ip → $country_code" # Prüfen ob gesperrt werden soll if should_block_by_geoip "$country_code"; then ban_ip_geoip "$client_ip" "$country_code" blocked=$((blocked + 1)) fi done <<< "$clients" log "INFO" "GeoIP-Sync abgeschlossen: $checked geprüft, $blocked gesperrt, $skipped übersprungen" } # ─── Worker-Hauptschleife ──────────────────────────────────────────────────── start_worker() { if [[ "${GEOIP_ENABLED:-false}" != "true" ]]; then log "DEBUG" "GeoIP-Worker ist deaktiviert" return 0 fi # PID schreiben echo $$ > "$WORKER_PID_FILE" trap 'rm -f "$WORKER_PID_FILE"; exit 0' SIGTERM SIGINT SIGHUP local interval="${GEOIP_CHECK_INTERVAL:-0}" [[ "$interval" -le 0 ]] && interval="${CHECK_INTERVAL:-10}" log "INFO" "GeoIP-Worker gestartet (PID: $$, Intervall: ${interval}s)" # Beim Start: MaxMind DB herunterladen/aktualisieren (falls License-Key gesetzt) update_maxmind_db || true while true; do sync_geoip sleep "$interval" done } # ─── Status anzeigen ───────────────────────────────────────────────────────── show_status() { echo "═══════════════════════════════════════════════════════════════" echo " AdGuard Shield - GeoIP Status" echo "═══════════════════════════════════════════════════════════════" echo "" if [[ "${GEOIP_ENABLED:-false}" != "true" ]]; then echo " ℹ️ GeoIP ist deaktiviert" echo "" return fi echo " Modus: ${GEOIP_MODE:-blocklist}" echo " Länder: ${GEOIP_COUNTRIES:-}" echo " Sperrdauer: PERMANENT (Auto-Unban bei Änderung der Länderliste)" echo " Private IPs überspringen: ${GEOIP_SKIP_PRIVATE:-true}" echo "" # MaxMind DB Info local eff_mmdb eff_mmdb=$(resolve_mmdb_path) if [[ -n "${GEOIP_MMDB_PATH:-}" ]]; then echo " MMDB-Pfad: ${GEOIP_MMDB_PATH} (manuell konfiguriert)" elif [[ -n "${GEOIP_LICENSE_KEY:-}" ]]; then echo " MMDB-Pfad: ${GEOIP_AUTO_DB} (Auto-Download)" if [[ -f "${GEOIP_AUTO_DB}" ]]; then local db_age db_age_h db_age=$(( $(date +%s) - $(stat -c %Y "${GEOIP_AUTO_DB}" 2>/dev/null || echo 0) )) db_age_h=$(( db_age / 3600 )) echo " DB-Alter: ${db_age_h}h (Update alle 24h)" else echo " DB-Status: ⚠️ Noch nicht heruntergeladen" fi elif [[ -n "$eff_mmdb" ]]; then echo " MMDB-Pfad: ${eff_mmdb}" else echo " MMDB-Pfad: (Fallback auf geoiplookup)" fi echo " License-Key: $(if [[ -n "${GEOIP_LICENSE_KEY:-}" ]]; then echo "✅ konfiguriert"; else echo "❌ nicht gesetzt (kein Auto-Download)"; fi)" echo "" # GeoIP Tools prüfen echo " GeoIP Tools:" local tool tool=$(check_geoip_tools 2>/dev/null) || tool="none" case "$tool" in mmdbinspect) echo " ✅ mmdbinspect mit MaxMind DB" ;; mmdblookup) echo " ✅ mmdblookup mit MaxMind DB" ;; geoiplookup) echo " ✅ geoiplookup (Legacy GeoIP)" ;; none) echo " ❌ Kein GeoIP-Tool gefunden!" ;; esac echo "" # Worker-Status if [[ -f "$WORKER_PID_FILE" ]]; then local wpid wpid=$(cat "$WORKER_PID_FILE") if kill -0 "$wpid" 2>/dev/null; then echo " Worker: Läuft (PID: $wpid)" else echo " Worker: Abgestürzt (PID: $wpid existiert nicht mehr)" fi else echo " Worker: Nicht gestartet" fi echo "" # GeoIP-Sperren anzeigen local geoip_bans_data geoip_bans_data=$(db_ban_get_by_reason "geoip") local geoip_bans=0 if [[ -n "$geoip_bans_data" ]]; then while IFS='|' read -r s_ip s_domain _ _ s_ban_until_epoch _ _ s_perm_int _ _ _ s_country _; do [[ -z "$s_ip" ]] && continue geoip_bans=$((geoip_bans + 1)) local s_until_display="PERMANENT" if [[ "$s_ban_until_epoch" != "0" && "$s_perm_int" != "1" ]]; then s_until_display=$(date -d "@$s_ban_until_epoch" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "?") fi echo " 🌍 $s_ip → Land: ${s_country:-?} (bis: $s_until_display)" done <<< "$geoip_bans_data" fi if [[ $geoip_bans -eq 0 ]]; then echo " Keine aktiven GeoIP-Sperren" else echo "" echo " Gesamt: $geoip_bans aktive GeoIP-Sperren" fi # Cache-Statistik if [[ -d "$GEOIP_CACHE_DIR" ]]; then local cache_count cache_count=$(find "$GEOIP_CACHE_DIR" -name '*.country' -type f 2>/dev/null | wc -l) echo "" echo " Cache: $cache_count IP-Lookups zwischengespeichert" fi echo "" echo "═══════════════════════════════════════════════════════════════" } # ─── Einzelne IP nachschlagen ──────────────────────────────────────────────── lookup_ip() { local ip="$1" local eff_mmdb eff_mmdb=$(resolve_mmdb_path) local tool tool=$(check_geoip_tools 2>/dev/null) || tool="none" if [[ "$tool" == "none" ]]; then echo "❌ Kein GeoIP-Tool verfügbar." echo " Installiere geoip-bin: sudo apt install geoip-bin geoip-database" echo " Oder mmdbinspect mit MaxMind GeoLite2 DB" return 1 fi local country_code country_code=$(geoip_lookup "$ip") || true if [[ -z "$country_code" ]]; then echo "IP: $ip → Land: unbekannt (kein GeoIP-Ergebnis)" return 1 fi echo "IP: $ip → Land: $country_code (Tool: $tool)" [[ -n "$eff_mmdb" ]] && echo " MMDB: $eff_mmdb" # Prüfen ob diese IP gesperrt werden würde if [[ "${GEOIP_ENABLED:-false}" == "true" && -n "${GEOIP_COUNTRIES:-}" ]]; then if should_block_by_geoip "$country_code"; then echo "→ Würde GESPERRT werden (Modus: ${GEOIP_MODE:-blocklist}, Länder: ${GEOIP_COUNTRIES})" else echo "→ Würde ERLAUBT werden (Modus: ${GEOIP_MODE:-blocklist}, Länder: ${GEOIP_COUNTRIES})" fi fi } # ─── Cache leeren ──────────────────────────────────────────────────────────── flush_cache() { if [[ -d "$GEOIP_CACHE_DIR" ]]; then local count count=$(find "$GEOIP_CACHE_DIR" -name '*.country' -type f 2>/dev/null | wc -l) rm -f "${GEOIP_CACHE_DIR}"/*.country 2>/dev/null || true echo "✅ GeoIP-Cache geleert ($count Einträge entfernt)" log "INFO" "GeoIP-Cache geleert ($count Einträge)" else echo "ℹ️ GeoIP-Cache-Verzeichnis existiert nicht" fi } # ─── GeoIP-Sperren aufheben ───────────────────────────────────────────────── flush_geoip_bans() { local count=0 local geoip_ips geoip_ips=$(db_query "SELECT client_ip FROM active_bans WHERE reason='geoip';") if [[ -n "$geoip_ips" ]]; then while IFS= read -r client_ip; do [[ -z "$client_ip" ]] && continue # iptables Regel entfernen if [[ "$client_ip" == *:* ]]; then ip6tables -D "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true else iptables -D "$IPTABLES_CHAIN" -s "$client_ip" -j DROP 2>/dev/null || true fi db_ban_delete "$client_ip" log_ban_history "UNBAN" "$client_ip" "" "geoip-flush" count=$((count + 1)) done <<< "$geoip_ips" fi echo "✅ $count GeoIP-Sperren aufgehoben" log "INFO" "$count GeoIP-Sperren aufgehoben (flush)" } # ─── Hauptprogramm ────────────────────────────────────────────────────────── case "${1:-help}" in start) init_directories setup_iptables_chain start_worker ;; sync) init_directories setup_iptables_chain sync_geoip ;; status) init_directories show_status ;; lookup) if [[ -z "${2:-}" ]]; then echo "Nutzung: $0 lookup " >&2 exit 1 fi init_directories lookup_ip "$2" ;; flush) init_directories flush_geoip_bans ;; flush-cache) init_directories flush_cache ;; stop) if [[ -f "$WORKER_PID_FILE" ]]; then local wpid wpid=$(cat "$WORKER_PID_FILE") if kill -0 "$wpid" 2>/dev/null; then kill "$wpid" 2>/dev/null || true rm -f "$WORKER_PID_FILE" echo "GeoIP-Worker gestoppt" else rm -f "$WORKER_PID_FILE" echo "GeoIP-Worker war nicht aktiv" fi else echo "GeoIP-Worker läuft nicht" fi ;; *) cat << USAGE AdGuard Shield - GeoIP Worker Nutzung: $0 {start|stop|sync|status|lookup|flush|flush-cache} Befehle: start Startet den GeoIP-Worker (Hintergrundprozess) stop Stoppt den GeoIP-Worker sync Einmalige GeoIP-Prüfung aller aktiven Clients status Zeigt GeoIP-Status und aktive Sperren lookup GeoIP-Lookup für eine einzelne IP flush Alle GeoIP-Sperren aufheben flush-cache GeoIP-Lookup-Cache leeren Konfiguration in: $CONFIG_FILE GEOIP_ENABLED=true/false GEOIP_MODE=blocklist/allowlist GEOIP_COUNTRIES="CN,RU,..." USAGE exit 0 ;; esac