geoip_shop_manager.py aktualisiert

This commit is contained in:
2025-12-09 07:49:46 +01:00
parent 111782e955
commit 91f86c76e5

View File

@@ -12,9 +12,23 @@ import subprocess
import json import json
import time import time
import re import re
import socket
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pathlib import Path from pathlib import Path
# ANSI Color Codes
COLOR_GREEN = "\033[92m"
COLOR_RED = "\033[91m"
COLOR_YELLOW = "\033[93m"
COLOR_RESET = "\033[0m"
COLOR_BOLD = "\033[1m"
# Link11 IP
LINK11_IP = "128.65.223.106"
# Cache for DNS lookups (to avoid repeated lookups)
DNS_CACHE = {}
# Configuration # Configuration
VHOSTS_DIR = "/var/www/vhosts" VHOSTS_DIR = "/var/www/vhosts"
BACKUP_SUFFIX = ".geoip_backup" BACKUP_SUFFIX = ".geoip_backup"
@@ -172,6 +186,41 @@ def detect_bot(user_agent):
return 'Unbekannt' return 'Unbekannt'
def check_link11(domain):
"""Check if domain resolves to Link11 IP"""
global DNS_CACHE
# Check cache first
if domain in DNS_CACHE:
return DNS_CACHE[domain]
try:
ip = socket.gethostbyname(domain)
is_link11 = (ip == LINK11_IP)
DNS_CACHE[domain] = {'is_link11': is_link11, 'ip': ip}
return DNS_CACHE[domain]
except socket.gaierror:
DNS_CACHE[domain] = {'is_link11': False, 'ip': 'N/A'}
return DNS_CACHE[domain]
def format_shop_with_link11(shop, prefix="", show_index=None):
"""Format shop name with Link11 color coding"""
link11_info = check_link11(shop)
if link11_info['is_link11']:
color = COLOR_GREEN
suffix = " [Link11]"
else:
color = COLOR_RED
suffix = " [Direkt]"
if show_index is not None:
return f" [{show_index}] {color}{shop}{suffix}{COLOR_RESET}"
else:
return f"{prefix}{color}{shop}{suffix}{COLOR_RESET}"
# PHP GeoIP blocking script (no exec, just logging) # PHP GeoIP blocking script (no exec, just logging)
GEOIP_SCRIPT = '''<?php GEOIP_SCRIPT = '''<?php
/** /**
@@ -982,10 +1031,14 @@ def activate_all_shops():
print(f"\n{'=' * 60}") print(f"\n{'=' * 60}")
print(f" DACH GeoIP-Blocking für ALLE Shops aktivieren") print(f" DACH GeoIP-Blocking für ALLE Shops aktivieren")
print(f"{'=' * 60}") print(f"{'=' * 60}")
print(f"\n📋 Folgende {len(available_shops)} Shop(s) werden aktiviert:\n") print(f"\n📋 Folgende {len(available_shops)} Shop(s) werden aktiviert:")
print(f" {COLOR_GREEN}Grün = hinter Link11{COLOR_RESET} | {COLOR_RED}Rot = Direkt{COLOR_RESET}\n")
for shop in available_shops: for shop in available_shops:
print(f"{shop}") link11_info = check_link11(shop)
color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED
link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]"
print(f"{color}{shop} {link11_tag}{COLOR_RESET}")
# Ask for mode # Ask for mode
print(f"\n🔧 Wähle den Blocking-Modus:") print(f"\n🔧 Wähle den Blocking-Modus:")
@@ -1130,10 +1183,14 @@ def deactivate_all_shops():
print(f"\n{'=' * 60}") print(f"\n{'=' * 60}")
print(f" DACH GeoIP-Blocking für ALLE Shops deaktivieren") print(f" DACH GeoIP-Blocking für ALLE Shops deaktivieren")
print(f"{'=' * 60}") print(f"{'=' * 60}")
print(f"\n📋 Folgende {len(active_shops)} Shop(s) werden deaktiviert:\n") print(f"\n📋 Folgende {len(active_shops)} Shop(s) werden deaktiviert:")
print(f" {COLOR_GREEN}Grün = hinter Link11{COLOR_RESET} | {COLOR_RED}Rot = Direkt{COLOR_RESET}\n")
for shop in active_shops: for shop in active_shops:
print(f"{shop}") link11_info = check_link11(shop)
color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED
link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]"
print(f"{color}{shop} {link11_tag}{COLOR_RESET}")
print(f"\n⚠️ Dies deaktiviert den Schutz für alle oben genannten Shops!") print(f"\n⚠️ Dies deaktiviert den Schutz für alle oben genannten Shops!")
print(f"⚠️ Alle zugehörigen CrowdSec-Decisions werden ebenfalls entfernt!") print(f"⚠️ Alle zugehörigen CrowdSec-Decisions werden ebenfalls entfernt!")
@@ -1291,8 +1348,8 @@ def show_all_logs():
print(f"{'' * 70}") print(f"{'' * 70}")
total_php_blocks = 0 total_php_blocks = 0
shop_php_stats = {} # shop -> {'blocks': N, 'activation': datetime, 'req_min': float} shop_php_stats = {} # shop -> {'blocks': N, 'activation': datetime, 'req_min': float, 'ips': {}}
all_ips = {} # ip -> {'count': N, 'ua': user_agent} all_ips = {} # ip -> {'count': N, 'ua': user_agent, 'shops': {shop: count}}
total_minutes = 0 total_minutes = 0
# Collect PHP stats # Collect PHP stats
@@ -1312,7 +1369,8 @@ def show_all_logs():
'blocks': blocks, 'blocks': blocks,
'activation': activation_time, 'activation': activation_time,
'runtime_minutes': runtime_minutes, 'runtime_minutes': runtime_minutes,
'req_min': req_min 'req_min': req_min,
'ips': ips # Store IPs per shop for top IP display
} }
if runtime_minutes > total_minutes: if runtime_minutes > total_minutes:
@@ -1320,8 +1378,9 @@ def show_all_logs():
for ip, data in ips.items(): for ip, data in ips.items():
if ip not in all_ips: if ip not in all_ips:
all_ips[ip] = {'count': 0, 'ua': data['ua']} all_ips[ip] = {'count': 0, 'ua': data['ua'], 'shops': {}}
all_ips[ip]['count'] += data['count'] all_ips[ip]['count'] += data['count']
all_ips[ip]['shops'][shop] = data['count']
# Keep the most informative UA # Keep the most informative UA
if data['ua'] != 'Unknown' and all_ips[ip]['ua'] == 'Unknown': if data['ua'] != 'Unknown' and all_ips[ip]['ua'] == 'Unknown':
all_ips[ip]['ua'] = data['ua'] all_ips[ip]['ua'] = data['ua']
@@ -1333,7 +1392,7 @@ def show_all_logs():
crowdsec_stats = get_crowdsec_stats_by_shop() crowdsec_stats = get_crowdsec_stats_by_shop()
total_crowdsec = sum(crowdsec_stats.values()) total_crowdsec = sum(crowdsec_stats.values())
# Display PHP blocks with req/min # Display PHP blocks with req/min and top IP per shop
print(f"\n📝 PHP-Blocks gesamt: {total_php_blocks} (⌀ {total_req_min:.1f} req/min, Laufzeit: {format_duration(total_minutes)})") print(f"\n📝 PHP-Blocks gesamt: {total_php_blocks} (⌀ {total_req_min:.1f} req/min, Laufzeit: {format_duration(total_minutes)})")
if shop_php_stats: if shop_php_stats:
for shop in sorted(shop_php_stats.keys()): for shop in sorted(shop_php_stats.keys()):
@@ -1344,6 +1403,24 @@ def show_all_logs():
bar = "" * min(int(req_min * 2), 20) if req_min > 0 else "" bar = "" * min(int(req_min * 2), 20) if req_min > 0 else ""
runtime_str = format_duration(runtime) if runtime > 0 else "?" runtime_str = format_duration(runtime) if runtime > 0 else "?"
print(f" ├─ {shop}: {count} ({req_min:.1f} req/min, seit {runtime_str}) {bar}") print(f" ├─ {shop}: {count} ({req_min:.1f} req/min, seit {runtime_str}) {bar}")
# Show top IP for this shop
shop_ips = stats['ips']
if shop_ips and count > 0:
top_ip = max(shop_ips.items(), key=lambda x: x[1]['count'])
top_ip_addr = top_ip[0]
top_ip_count = top_ip[1]['count']
top_ip_ua = top_ip[1]['ua']
top_ip_bot = detect_bot(top_ip_ua)
top_ip_req_min = top_ip_count / runtime if runtime > 0 else 0
# Show bot name or shortened UA if unknown
if top_ip_bot == 'Unbekannt':
display_name = (top_ip_ua[:40] + '...') if len(top_ip_ua) > 43 else top_ip_ua
else:
display_name = top_ip_bot
print(f" │ └─➤ Top: {top_ip_addr} ({display_name}) - {top_ip_count}x, {top_ip_req_min:.1f} req/min")
# Display CrowdSec bans # Display CrowdSec bans
print(f"\n🛡️ CrowdSec-Bans gesamt: {total_crowdsec}") print(f"\n🛡️ CrowdSec-Bans gesamt: {total_crowdsec}")
@@ -1357,7 +1434,7 @@ def show_all_logs():
else: else:
print(" └─ CrowdSec nicht verfügbar") print(" └─ CrowdSec nicht verfügbar")
# Top blocked IPs with bot detection # Top blocked IPs with bot detection, req/min, and top shop
if all_ips: if all_ips:
print(f"\n🔥 Top 100 blockierte IPs (alle Shops):") print(f"\n🔥 Top 100 blockierte IPs (alle Shops):")
sorted_ips = sorted(all_ips.items(), key=lambda x: x[1]['count'], reverse=True)[:100] sorted_ips = sorted(all_ips.items(), key=lambda x: x[1]['count'], reverse=True)[:100]
@@ -1365,8 +1442,35 @@ def show_all_logs():
count = data['count'] count = data['count']
ua = data['ua'] ua = data['ua']
bot_name = detect_bot(ua) bot_name = detect_bot(ua)
shops_data = data['shops']
# Calculate req/min for this IP
ip_req_min = count / total_minutes if total_minutes > 0 else 0
# Find top shop for this IP
if shops_data:
top_shop = max(shops_data.items(), key=lambda x: x[1])
top_shop_name = top_shop[0]
top_shop_count = top_shop[1]
# Shorten shop name if too long
if len(top_shop_name) > 25:
top_shop_display = top_shop_name[:22] + '...'
else:
top_shop_display = top_shop_name
else:
top_shop_display = "?"
top_shop_count = 0
# Show bot name or shortened UA if unknown
if bot_name == 'Unbekannt':
display_name = (ua[:35] + '...') if len(ua) > 38 else ua
if display_name == 'Unknown':
display_name = 'Unbekannt'
else:
display_name = bot_name
bar = "" * min(count // 5, 20) if count > 0 else "" bar = "" * min(count // 5, 20) if count > 0 else ""
print(f" {ip} ({bot_name}): {count} {bar}") print(f" {ip} ({display_name}): {count} ({ip_req_min:.1f} req/min) → {top_shop_display} [{top_shop_count}x] {bar}")
print(f"\n{'' * 70}") print(f"\n{'' * 70}")
@@ -1414,7 +1518,7 @@ def show_logs(shop):
print("=" * 70) print("=" * 70)
print(f"Gesamt: {len(lines)}") print(f"Gesamt: {len(lines)}")
# Show top IPs with bot detection # Show top IPs with bot detection and req/min
if ips: if ips:
print(f"\n🔥 Top 20 blockierte IPs:") print(f"\n🔥 Top 20 blockierte IPs:")
sorted_ips = sorted(ips.items(), key=lambda x: x[1]['count'], reverse=True)[:20] sorted_ips = sorted(ips.items(), key=lambda x: x[1]['count'], reverse=True)[:20]
@@ -1422,8 +1526,18 @@ def show_logs(shop):
count = data['count'] count = data['count']
ua = data['ua'] ua = data['ua']
bot_name = detect_bot(ua) bot_name = detect_bot(ua)
ip_req_min = count / runtime_minutes if runtime_minutes > 0 else 0
# Show bot name or shortened UA if unknown
if bot_name == 'Unbekannt':
display_name = (ua[:40] + '...') if len(ua) > 43 else ua
if display_name == 'Unknown':
display_name = 'Unbekannt'
else:
display_name = bot_name
bar = "" * min(count // 5, 20) if count > 0 else "" bar = "" * min(count // 5, 20) if count > 0 else ""
print(f" {ip} ({bot_name}): {count} {bar}") print(f" {ip} ({display_name}): {count} ({ip_req_min:.1f} req/min) {bar}")
else: else:
print(f"\n Keine PHP-Logs für {shop}") print(f"\n Keine PHP-Logs für {shop}")
@@ -1516,8 +1630,9 @@ def main():
continue continue
print("\n📋 Verfügbare Shops:") print("\n📋 Verfügbare Shops:")
print(f" {COLOR_GREEN}Grün = hinter Link11{COLOR_RESET} | {COLOR_RED}Rot = Direkt{COLOR_RESET}")
for i, shop in enumerate(available_shops, 1): for i, shop in enumerate(available_shops, 1):
print(f" [{i}] {shop}") print(format_shop_with_link11(shop, show_index=i))
shop_choice = input("\nWähle einen Shop: ").strip() shop_choice = input("\nWähle einen Shop: ").strip()
try: try:
@@ -1554,10 +1669,14 @@ def main():
continue continue
print("\n📋 Aktive Shops:") print("\n📋 Aktive Shops:")
print(f" {COLOR_GREEN}Grün = hinter Link11{COLOR_RESET} | {COLOR_RED}Rot = Direkt{COLOR_RESET}")
for i, shop in enumerate(active_shops, 1): for i, shop in enumerate(active_shops, 1):
mode = get_shop_mode(shop) mode = get_shop_mode(shop)
mode_icon = "🛡️" if mode == "php+crowdsec" else "📝" mode_icon = "🛡️" if mode == "php+crowdsec" else "📝"
print(f" [{i}] {shop} {mode_icon}") link11_info = check_link11(shop)
color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED
link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]"
print(f" [{i}] {color}{shop} {link11_tag}{COLOR_RESET} {mode_icon}")
shop_choice = input("\nWähle einen Shop: ").strip() shop_choice = input("\nWähle einen Shop: ").strip()
try: try:
@@ -1579,11 +1698,15 @@ def main():
continue continue
print("\n📋 Logs anzeigen für:") print("\n📋 Logs anzeigen für:")
print(f" {COLOR_GREEN}Grün = hinter Link11{COLOR_RESET} | {COLOR_RED}Rot = Direkt{COLOR_RESET}")
print(f" [0] 📊 ALLE Shops (Zusammenfassung)") print(f" [0] 📊 ALLE Shops (Zusammenfassung)")
for i, shop in enumerate(active_shops, 1): for i, shop in enumerate(active_shops, 1):
mode = get_shop_mode(shop) mode = get_shop_mode(shop)
mode_icon = "🛡️" if mode == "php+crowdsec" else "📝" mode_icon = "🛡️" if mode == "php+crowdsec" else "📝"
print(f" [{i}] {shop} {mode_icon}") link11_info = check_link11(shop)
color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED
link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]"
print(f" [{i}] {color}{shop} {link11_tag}{COLOR_RESET} {mode_icon}")
shop_choice = input("\nWähle eine Option: ").strip() shop_choice = input("\nWähle eine Option: ").strip()
try: try:
@@ -1603,6 +1726,7 @@ def main():
print(f"\n📊 Status:") print(f"\n📊 Status:")
print(f" Shops gesamt: {len(shops)}") print(f" Shops gesamt: {len(shops)}")
print(f" Aktive DACH-Blockings: {len(active_shops)}") print(f" Aktive DACH-Blockings: {len(active_shops)}")
print(f" {COLOR_GREEN}Grün = hinter Link11{COLOR_RESET} | {COLOR_RED}Rot = Direkt{COLOR_RESET}")
if active_shops: if active_shops:
for shop in active_shops: for shop in active_shops:
mode = get_shop_mode(shop) mode = get_shop_mode(shop)
@@ -1619,7 +1743,12 @@ def main():
req_min = 0 req_min = 0
runtime_str = "?" runtime_str = "?"
print(f"{shop} [{mode_text}] {mode_icon} - {blocks} blocks ({req_min:.1f} req/min, {runtime_str})") # Link11 check
link11_info = check_link11(shop)
color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED
link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]"
print(f"{color}{shop} {link11_tag}{COLOR_RESET} [{mode_text}] {mode_icon} - {blocks} blocks ({req_min:.1f} req/min, {runtime_str})")
elif choice == "5": elif choice == "5":
activate_all_shops() activate_all_shops()