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 time
import re
import socket
from datetime import datetime, timedelta
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
VHOSTS_DIR = "/var/www/vhosts"
BACKUP_SUFFIX = ".geoip_backup"
@@ -172,6 +186,41 @@ def detect_bot(user_agent):
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)
GEOIP_SCRIPT = '''<?php
/**
@@ -982,10 +1031,14 @@ def activate_all_shops():
print(f"\n{'=' * 60}")
print(f" DACH GeoIP-Blocking für ALLE Shops aktivieren")
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:
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
print(f"\n🔧 Wähle den Blocking-Modus:")
@@ -1130,10 +1183,14 @@ def deactivate_all_shops():
print(f"\n{'=' * 60}")
print(f" DACH GeoIP-Blocking für ALLE Shops deaktivieren")
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:
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"⚠️ Alle zugehörigen CrowdSec-Decisions werden ebenfalls entfernt!")
@@ -1291,8 +1348,8 @@ def show_all_logs():
print(f"{'' * 70}")
total_php_blocks = 0
shop_php_stats = {} # shop -> {'blocks': N, 'activation': datetime, 'req_min': float}
all_ips = {} # ip -> {'count': N, 'ua': user_agent}
shop_php_stats = {} # shop -> {'blocks': N, 'activation': datetime, 'req_min': float, 'ips': {}}
all_ips = {} # ip -> {'count': N, 'ua': user_agent, 'shops': {shop: count}}
total_minutes = 0
# Collect PHP stats
@@ -1312,7 +1369,8 @@ def show_all_logs():
'blocks': blocks,
'activation': activation_time,
'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:
@@ -1320,8 +1378,9 @@ def show_all_logs():
for ip, data in ips.items():
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]['shops'][shop] = data['count']
# Keep the most informative UA
if data['ua'] != 'Unknown' and all_ips[ip]['ua'] == 'Unknown':
all_ips[ip]['ua'] = data['ua']
@@ -1333,7 +1392,7 @@ def show_all_logs():
crowdsec_stats = get_crowdsec_stats_by_shop()
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)})")
if shop_php_stats:
for shop in sorted(shop_php_stats.keys()):
@@ -1345,6 +1404,24 @@ def show_all_logs():
runtime_str = format_duration(runtime) if runtime > 0 else "?"
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
print(f"\n🛡️ CrowdSec-Bans gesamt: {total_crowdsec}")
if crowdsec_stats:
@@ -1357,7 +1434,7 @@ def show_all_logs():
else:
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:
print(f"\n🔥 Top 100 blockierte IPs (alle Shops):")
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']
ua = data['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 ""
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}")
@@ -1414,7 +1518,7 @@ def show_logs(shop):
print("=" * 70)
print(f"Gesamt: {len(lines)}")
# Show top IPs with bot detection
# Show top IPs with bot detection and req/min
if ips:
print(f"\n🔥 Top 20 blockierte IPs:")
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']
ua = data['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 ""
print(f" {ip} ({bot_name}): {count} {bar}")
print(f" {ip} ({display_name}): {count} ({ip_req_min:.1f} req/min) {bar}")
else:
print(f"\n Keine PHP-Logs für {shop}")
@@ -1516,8 +1630,9 @@ def main():
continue
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):
print(f" [{i}] {shop}")
print(format_shop_with_link11(shop, show_index=i))
shop_choice = input("\nWähle einen Shop: ").strip()
try:
@@ -1554,10 +1669,14 @@ def main():
continue
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):
mode = get_shop_mode(shop)
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()
try:
@@ -1579,11 +1698,15 @@ def main():
continue
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)")
for i, shop in enumerate(active_shops, 1):
mode = get_shop_mode(shop)
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()
try:
@@ -1603,6 +1726,7 @@ def main():
print(f"\n📊 Status:")
print(f" Shops gesamt: {len(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:
for shop in active_shops:
mode = get_shop_mode(shop)
@@ -1619,7 +1743,12 @@ def main():
req_min = 0
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":
activate_all_shops()