geoip_shop_manager.py aktualisiert
This commit is contained in:
@@ -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()):
|
||||||
@@ -1345,6 +1404,24 @@ def show_all_logs():
|
|||||||
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}")
|
||||||
if crowdsec_stats:
|
if crowdsec_stats:
|
||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user