geoip_shop_manager.py aktualisiert
This commit is contained in:
@@ -450,21 +450,24 @@ def get_active_shops():
|
||||
return active
|
||||
|
||||
|
||||
def activate_blocking(shop):
|
||||
"""Activate GeoIP blocking"""
|
||||
def activate_blocking(shop, silent=False):
|
||||
"""Activate GeoIP blocking for a single shop"""
|
||||
httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs')
|
||||
index_php = os.path.join(httpdocs, 'index.php')
|
||||
backup_php = os.path.join(httpdocs, f'index.php{BACKUP_SUFFIX}')
|
||||
blocking_file = os.path.join(httpdocs, BLOCKING_FILE)
|
||||
|
||||
if os.path.isfile(backup_php):
|
||||
if not silent:
|
||||
print(f"⚠️ GeoIP-Blocking bereits aktiv für {shop}")
|
||||
return False
|
||||
|
||||
if not os.path.isfile(index_php):
|
||||
if not silent:
|
||||
print(f"❌ index.php nicht gefunden")
|
||||
return False
|
||||
|
||||
if not silent:
|
||||
print(f"\n🔧 Aktiviere DACH GeoIP-Blocking für: {shop}")
|
||||
print(" (Erlaubt: Deutschland, Österreich, Schweiz)")
|
||||
print("=" * 60)
|
||||
@@ -472,18 +475,22 @@ def activate_blocking(shop):
|
||||
# Step 1: Install watcher service if not exists
|
||||
active_shops = get_active_shops()
|
||||
if not active_shops: # First shop
|
||||
if not silent:
|
||||
print("\n[1/3] Installiere CrowdSec-Watcher-Service...")
|
||||
if check_crowdsec():
|
||||
install_watcher_service()
|
||||
else:
|
||||
if not silent:
|
||||
print(" ⚠️ CrowdSec nicht verfügbar - nur PHP-Blocking")
|
||||
else:
|
||||
if not silent:
|
||||
print("\n[1/3] CrowdSec-Watcher-Service bereits aktiv")
|
||||
|
||||
# Step 2: PHP blocking
|
||||
if not silent:
|
||||
print("\n[2/3] Aktiviere PHP-Blocking...")
|
||||
|
||||
print(" 📋 Backup erstellen...")
|
||||
|
||||
shutil.copy2(index_php, backup_php)
|
||||
|
||||
with open(index_php, 'r', encoding='utf-8') as f:
|
||||
@@ -505,6 +512,7 @@ def activate_blocking(shop):
|
||||
lines.insert(insert_line, require_statement)
|
||||
with open(index_php, 'w', encoding='utf-8') as f:
|
||||
f.write('\n'.join(lines))
|
||||
if not silent:
|
||||
print(" ✏️ index.php modifiziert")
|
||||
|
||||
expiry = datetime.now() + timedelta(hours=72)
|
||||
@@ -519,13 +527,17 @@ def activate_blocking(shop):
|
||||
|
||||
with open(blocking_file, 'w', encoding='utf-8') as f:
|
||||
f.write(geoip_content)
|
||||
if not silent:
|
||||
print(" 📝 geoip_blocking.php erstellt")
|
||||
|
||||
# Step 3: Register shop
|
||||
if not silent:
|
||||
print("\n[3/3] Registriere Shop...")
|
||||
add_shop_to_active(shop)
|
||||
if not silent:
|
||||
print(" ✅ Shop registriert")
|
||||
|
||||
if not silent:
|
||||
print("\n" + "=" * 60)
|
||||
print(f"✅ DACH GeoIP-Blocking aktiviert für: {shop}")
|
||||
print(f" Erlaubte Länder: 🇩🇪 DE | 🇦🇹 AT | 🇨🇭 CH")
|
||||
@@ -538,8 +550,8 @@ def activate_blocking(shop):
|
||||
return True
|
||||
|
||||
|
||||
def deactivate_blocking(shop):
|
||||
"""Deactivate GeoIP blocking"""
|
||||
def deactivate_blocking(shop, silent=False):
|
||||
"""Deactivate GeoIP blocking for a single shop"""
|
||||
httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs')
|
||||
index_php = os.path.join(httpdocs, 'index.php')
|
||||
backup_php = os.path.join(httpdocs, f'index.php{BACKUP_SUFFIX}')
|
||||
@@ -548,14 +560,17 @@ def deactivate_blocking(shop):
|
||||
log_file = os.path.join(httpdocs, LOG_FILE)
|
||||
queue_file = os.path.join(httpdocs, CROWDSEC_QUEUE_FILE)
|
||||
|
||||
if not silent:
|
||||
print(f"\n🔧 Deaktiviere DACH GeoIP-Blocking für: {shop}")
|
||||
print("=" * 60)
|
||||
|
||||
# Step 1: Remove PHP blocking
|
||||
if not silent:
|
||||
print("\n[1/4] PHP-Blocking entfernen...")
|
||||
|
||||
if os.path.isfile(backup_php):
|
||||
shutil.move(backup_php, index_php)
|
||||
if not silent:
|
||||
print(" 📋 index.php wiederhergestellt")
|
||||
else:
|
||||
if os.path.isfile(index_php):
|
||||
@@ -568,27 +583,35 @@ def deactivate_blocking(shop):
|
||||
for f in [blocking_file, cache_file, log_file, queue_file]:
|
||||
if os.path.isfile(f):
|
||||
os.remove(f)
|
||||
if not silent:
|
||||
print(" 🗑️ PHP-Dateien gelöscht")
|
||||
|
||||
# Step 2: Remove from tracking
|
||||
if not silent:
|
||||
print("\n[2/4] Deregistriere Shop...")
|
||||
remove_shop_from_active(shop)
|
||||
if not silent:
|
||||
print(" ✅ Shop deregistriert")
|
||||
|
||||
# Step 3: Clean CrowdSec decisions
|
||||
if not silent:
|
||||
print("\n[3/4] CrowdSec-Decisions entfernen...")
|
||||
if check_crowdsec():
|
||||
cleanup_crowdsec_decisions(shop)
|
||||
|
||||
# Step 4: Uninstall service if last shop
|
||||
if not silent:
|
||||
print("\n[4/4] Prüfe Watcher-Service...")
|
||||
remaining_shops = [s for s in get_active_shops() if s != shop]
|
||||
if not remaining_shops:
|
||||
if not silent:
|
||||
print(" ℹ️ Keine aktiven Shops mehr - deinstalliere Service")
|
||||
uninstall_watcher_service()
|
||||
else:
|
||||
if not silent:
|
||||
print(f" ℹ️ Service bleibt aktiv ({len(remaining_shops)} Shop(s) noch aktiv)")
|
||||
|
||||
if not silent:
|
||||
print("\n" + "=" * 60)
|
||||
print(f"✅ DACH GeoIP-Blocking deaktiviert für: {shop}")
|
||||
print("=" * 60)
|
||||
@@ -596,8 +619,335 @@ def deactivate_blocking(shop):
|
||||
return True
|
||||
|
||||
|
||||
def activate_all_shops():
|
||||
"""Activate GeoIP blocking for all available shops"""
|
||||
shops = get_available_shops()
|
||||
active_shops = get_active_shops()
|
||||
available_shops = [s for s in shops if s not in active_shops]
|
||||
|
||||
if not available_shops:
|
||||
print("\n⚠️ Keine Shops zum Aktivieren verfügbar")
|
||||
print(" Alle Shops haben bereits aktives GeoIP-Blocking")
|
||||
return
|
||||
|
||||
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")
|
||||
|
||||
for shop in available_shops:
|
||||
print(f" • {shop}")
|
||||
|
||||
print(f"\n⚠️ Dies aktiviert den Schutz für alle oben genannten Shops!")
|
||||
confirm = input(f"\nFortfahren? (ja/nein): ").strip().lower()
|
||||
|
||||
if confirm not in ['ja', 'j', 'yes', 'y']:
|
||||
print("\n❌ Abgebrochen")
|
||||
return
|
||||
|
||||
print(f"\n{'=' * 60}")
|
||||
print(" Starte Aktivierung...")
|
||||
print(f"{'=' * 60}")
|
||||
|
||||
success_count = 0
|
||||
failed_count = 0
|
||||
failed_shops = []
|
||||
|
||||
# Install watcher service first if needed
|
||||
if not active_shops and check_crowdsec():
|
||||
print("\n📦 Installiere CrowdSec-Watcher-Service...")
|
||||
install_watcher_service()
|
||||
|
||||
for i, shop in enumerate(available_shops, 1):
|
||||
print(f"\n[{i}/{len(available_shops)}] Aktiviere: {shop}")
|
||||
|
||||
try:
|
||||
# Use silent mode but show progress
|
||||
httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs')
|
||||
index_php = os.path.join(httpdocs, 'index.php')
|
||||
backup_php = os.path.join(httpdocs, f'index.php{BACKUP_SUFFIX}')
|
||||
blocking_file = os.path.join(httpdocs, BLOCKING_FILE)
|
||||
|
||||
if os.path.isfile(backup_php):
|
||||
print(f" ⚠️ Bereits aktiv - überspringe")
|
||||
continue
|
||||
|
||||
if not os.path.isfile(index_php):
|
||||
print(f" ❌ index.php nicht gefunden")
|
||||
failed_count += 1
|
||||
failed_shops.append(shop)
|
||||
continue
|
||||
|
||||
# Create backup
|
||||
shutil.copy2(index_php, backup_php)
|
||||
|
||||
# Modify index.php
|
||||
with open(index_php, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
lines = content.split('\n')
|
||||
insert_line = 0
|
||||
|
||||
for idx, line in enumerate(lines):
|
||||
if 'declare(strict_types' in line:
|
||||
insert_line = idx + 1
|
||||
break
|
||||
elif '<?php' in line and insert_line == 0:
|
||||
insert_line = idx + 1
|
||||
|
||||
require_statement = f"require_once __DIR__ . '/{BLOCKING_FILE}';"
|
||||
|
||||
if require_statement not in content:
|
||||
lines.insert(insert_line, require_statement)
|
||||
with open(index_php, 'w', encoding='utf-8') as f:
|
||||
f.write('\n'.join(lines))
|
||||
|
||||
# Create blocking script
|
||||
expiry = datetime.now() + timedelta(hours=72)
|
||||
geoip_content = GEOIP_SCRIPT.format(
|
||||
expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'),
|
||||
expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
cache_file=CACHE_FILE,
|
||||
log_file=LOG_FILE,
|
||||
crowdsec_queue=CROWDSEC_QUEUE_FILE,
|
||||
shop_name=shop
|
||||
)
|
||||
|
||||
with open(blocking_file, 'w', encoding='utf-8') as f:
|
||||
f.write(geoip_content)
|
||||
|
||||
# Register shop
|
||||
add_shop_to_active(shop)
|
||||
|
||||
print(f" ✅ Aktiviert (bis {expiry.strftime('%Y-%m-%d %H:%M')})")
|
||||
success_count += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Fehler: {e}")
|
||||
failed_count += 1
|
||||
failed_shops.append(shop)
|
||||
|
||||
# Summary
|
||||
print(f"\n{'=' * 60}")
|
||||
print(" ZUSAMMENFASSUNG")
|
||||
print(f"{'=' * 60}")
|
||||
print(f"\n ✅ Erfolgreich aktiviert: {success_count}")
|
||||
print(f" ❌ Fehlgeschlagen: {failed_count}")
|
||||
|
||||
if failed_shops:
|
||||
print(f"\n Fehlgeschlagene Shops:")
|
||||
for shop in failed_shops:
|
||||
print(f" • {shop}")
|
||||
|
||||
print(f"\n 🇩🇪 🇦🇹 🇨🇭 Nur DACH-Traffic erlaubt")
|
||||
print(f" ⏰ Gültig für 72 Stunden")
|
||||
print(f"{'=' * 60}")
|
||||
|
||||
|
||||
def deactivate_all_shops():
|
||||
"""Deactivate GeoIP blocking for all active shops"""
|
||||
active_shops = get_active_shops()
|
||||
|
||||
if not active_shops:
|
||||
print("\n⚠️ Keine Shops mit aktivem GeoIP-Blocking gefunden")
|
||||
return
|
||||
|
||||
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")
|
||||
|
||||
for shop in active_shops:
|
||||
print(f" • {shop}")
|
||||
|
||||
print(f"\n⚠️ Dies deaktiviert den Schutz für alle oben genannten Shops!")
|
||||
print(f"⚠️ Alle zugehörigen CrowdSec-Decisions werden ebenfalls entfernt!")
|
||||
confirm = input(f"\nFortfahren? (ja/nein): ").strip().lower()
|
||||
|
||||
if confirm not in ['ja', 'j', 'yes', 'y']:
|
||||
print("\n❌ Abgebrochen")
|
||||
return
|
||||
|
||||
print(f"\n{'=' * 60}")
|
||||
print(" Starte Deaktivierung...")
|
||||
print(f"{'=' * 60}")
|
||||
|
||||
success_count = 0
|
||||
failed_count = 0
|
||||
failed_shops = []
|
||||
|
||||
for i, shop in enumerate(active_shops, 1):
|
||||
print(f"\n[{i}/{len(active_shops)}] Deaktiviere: {shop}")
|
||||
|
||||
try:
|
||||
httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs')
|
||||
index_php = os.path.join(httpdocs, 'index.php')
|
||||
backup_php = os.path.join(httpdocs, f'index.php{BACKUP_SUFFIX}')
|
||||
blocking_file = os.path.join(httpdocs, BLOCKING_FILE)
|
||||
cache_file = os.path.join(httpdocs, CACHE_FILE)
|
||||
log_file_path = os.path.join(httpdocs, LOG_FILE)
|
||||
queue_file = os.path.join(httpdocs, CROWDSEC_QUEUE_FILE)
|
||||
|
||||
# Restore backup
|
||||
if os.path.isfile(backup_php):
|
||||
shutil.move(backup_php, index_php)
|
||||
print(f" 📋 index.php wiederhergestellt")
|
||||
else:
|
||||
if os.path.isfile(index_php):
|
||||
with open(index_php, 'r') as f:
|
||||
content = f.read()
|
||||
lines = [line for line in content.split('\n') if BLOCKING_FILE not in line]
|
||||
with open(index_php, 'w') as f:
|
||||
f.write('\n'.join(lines))
|
||||
|
||||
# Remove files
|
||||
for f in [blocking_file, cache_file, log_file_path, queue_file]:
|
||||
if os.path.isfile(f):
|
||||
os.remove(f)
|
||||
|
||||
# Remove from tracking
|
||||
remove_shop_from_active(shop)
|
||||
|
||||
# Clean CrowdSec decisions
|
||||
if check_crowdsec():
|
||||
cleanup_crowdsec_decisions(shop)
|
||||
|
||||
print(f" ✅ Deaktiviert")
|
||||
success_count += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Fehler: {e}")
|
||||
failed_count += 1
|
||||
failed_shops.append(shop)
|
||||
|
||||
# Uninstall watcher service
|
||||
print("\n📦 Deinstalliere CrowdSec-Watcher-Service...")
|
||||
uninstall_watcher_service()
|
||||
|
||||
# Summary
|
||||
print(f"\n{'=' * 60}")
|
||||
print(" ZUSAMMENFASSUNG")
|
||||
print(f"{'=' * 60}")
|
||||
print(f"\n ✅ Erfolgreich deaktiviert: {success_count}")
|
||||
print(f" ❌ Fehlgeschlagen: {failed_count}")
|
||||
|
||||
if failed_shops:
|
||||
print(f"\n Fehlgeschlagene Shops:")
|
||||
for shop in failed_shops:
|
||||
print(f" • {shop}")
|
||||
|
||||
print(f"\n 🌍 Alle IPs sind nun wieder erlaubt")
|
||||
print(f"{'=' * 60}")
|
||||
|
||||
|
||||
def get_shop_log_stats(shop):
|
||||
"""Get log statistics for a single shop"""
|
||||
httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs')
|
||||
log_file = os.path.join(httpdocs, LOG_FILE)
|
||||
|
||||
php_blocks = 0
|
||||
ips = {}
|
||||
|
||||
if os.path.isfile(log_file):
|
||||
with open(log_file, 'r') as f:
|
||||
for line in f:
|
||||
php_blocks += 1
|
||||
# Extract IP from log line
|
||||
if 'IP: ' in line:
|
||||
try:
|
||||
ip = line.split('IP: ')[1].split(' |')[0].strip()
|
||||
ips[ip] = ips.get(ip, 0) + 1
|
||||
except:
|
||||
pass
|
||||
|
||||
return php_blocks, ips
|
||||
|
||||
|
||||
def get_crowdsec_stats_by_shop():
|
||||
"""Get CrowdSec decision counts grouped by shop"""
|
||||
if not check_crowdsec():
|
||||
return {}
|
||||
|
||||
stats = {}
|
||||
code, stdout, _ = run_command("cscli decisions list -o raw")
|
||||
|
||||
if code == 0 and stdout:
|
||||
lines = stdout.strip().split('\n')
|
||||
for line in lines[1:]: # Skip header
|
||||
# Find shop name in reason field
|
||||
for shop in get_active_shops():
|
||||
if shop in line:
|
||||
stats[shop] = stats.get(shop, 0) + 1
|
||||
break
|
||||
|
||||
return stats
|
||||
|
||||
|
||||
def show_all_logs():
|
||||
"""Show combined logs for all active shops"""
|
||||
active_shops = get_active_shops()
|
||||
|
||||
if not active_shops:
|
||||
print("\n⚠️ Keine aktiven Shops")
|
||||
return
|
||||
|
||||
print(f"\n{'═' * 60}")
|
||||
print(" 📊 GESAMTÜBERSICHT ALLER SHOPS")
|
||||
print(f"{'═' * 60}")
|
||||
|
||||
total_php_blocks = 0
|
||||
shop_php_stats = {}
|
||||
all_ips = {}
|
||||
|
||||
# Collect PHP stats
|
||||
for shop in active_shops:
|
||||
blocks, ips = get_shop_log_stats(shop)
|
||||
total_php_blocks += blocks
|
||||
shop_php_stats[shop] = blocks
|
||||
|
||||
for ip, count in ips.items():
|
||||
all_ips[ip] = all_ips.get(ip, 0) + count
|
||||
|
||||
# Get CrowdSec stats
|
||||
crowdsec_stats = get_crowdsec_stats_by_shop()
|
||||
total_crowdsec = sum(crowdsec_stats.values())
|
||||
|
||||
# Display PHP blocks
|
||||
print(f"\n📝 PHP-Blocks gesamt: {total_php_blocks}")
|
||||
if shop_php_stats:
|
||||
for shop in sorted(shop_php_stats.keys()):
|
||||
count = shop_php_stats[shop]
|
||||
bar = "█" * min(count // 10, 20) if count > 0 else ""
|
||||
print(f" ├─ {shop}: {count} {bar}")
|
||||
|
||||
# Display CrowdSec bans
|
||||
print(f"\n🛡️ CrowdSec-Bans gesamt: {total_crowdsec}")
|
||||
if crowdsec_stats:
|
||||
for shop in sorted(crowdsec_stats.keys()):
|
||||
count = crowdsec_stats[shop]
|
||||
bar = "█" * min(count // 10, 20) if count > 0 else ""
|
||||
print(f" ├─ {shop}: {count} {bar}")
|
||||
elif check_crowdsec():
|
||||
print(" └─ Keine aktiven Bans")
|
||||
else:
|
||||
print(" └─ CrowdSec nicht verfügbar")
|
||||
|
||||
# Top blocked IPs
|
||||
if all_ips:
|
||||
print(f"\n🔥 Top 10 blockierte IPs (alle Shops):")
|
||||
sorted_ips = sorted(all_ips.items(), key=lambda x: x[1], reverse=True)[:10]
|
||||
for ip, count in sorted_ips:
|
||||
bar = "█" * min(count // 5, 20) if count > 0 else "█"
|
||||
print(f" {ip}: {count} {bar}")
|
||||
|
||||
print(f"\n{'═' * 60}")
|
||||
|
||||
# Wait for user
|
||||
input("\nDrücke Enter um fortzufahren...")
|
||||
|
||||
|
||||
def show_logs(shop):
|
||||
"""Show logs"""
|
||||
"""Show logs for a single shop"""
|
||||
httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs')
|
||||
log_file = os.path.join(httpdocs, LOG_FILE)
|
||||
|
||||
@@ -677,10 +1027,15 @@ def main():
|
||||
print(" ⚠️ Watcher-Service: Nicht aktiv")
|
||||
|
||||
while True:
|
||||
print("\n[1] GeoIP-Blocking AKTIVIEREN")
|
||||
print("[2] GeoIP-Blocking DEAKTIVIEREN")
|
||||
print("\n" + "-" * 40)
|
||||
print("[1] GeoIP-Blocking AKTIVIEREN (einzeln)")
|
||||
print("[2] GeoIP-Blocking DEAKTIVIEREN (einzeln)")
|
||||
print("[3] Logs anzeigen")
|
||||
print("[4] Status anzeigen")
|
||||
print("-" * 40)
|
||||
print("[5] 🚀 ALLE Shops aktivieren")
|
||||
print("[6] 🛑 ALLE Shops deaktivieren")
|
||||
print("-" * 40)
|
||||
print("[0] Beenden")
|
||||
|
||||
choice = input("\nWähle eine Option: ").strip()
|
||||
@@ -741,17 +1096,22 @@ def main():
|
||||
print("\n⚠️ Keine aktiven Shops")
|
||||
continue
|
||||
|
||||
print("\n📋 Shops mit Logs:")
|
||||
print("\n📋 Logs anzeigen für:")
|
||||
print(f" [0] 📊 ALLE Shops (Zusammenfassung)")
|
||||
for i, shop in enumerate(active_shops, 1):
|
||||
print(f" [{i}] {shop}")
|
||||
|
||||
shop_choice = input("\nWähle einen Shop: ").strip()
|
||||
shop_choice = input("\nWähle eine Option: ").strip()
|
||||
try:
|
||||
shop_idx = int(shop_choice) - 1
|
||||
if 0 <= shop_idx < len(active_shops):
|
||||
show_logs(active_shops[shop_idx])
|
||||
shop_idx = int(shop_choice)
|
||||
if shop_idx == 0:
|
||||
show_all_logs()
|
||||
elif 1 <= shop_idx <= len(active_shops):
|
||||
show_logs(active_shops[shop_idx - 1])
|
||||
else:
|
||||
print("❌ Ungültig")
|
||||
except ValueError:
|
||||
pass
|
||||
print("❌ Ungültig")
|
||||
|
||||
elif choice == "4":
|
||||
shops = get_available_shops()
|
||||
@@ -763,6 +1123,12 @@ def main():
|
||||
for shop in active_shops:
|
||||
print(f" ✓ {shop}")
|
||||
|
||||
elif choice == "5":
|
||||
activate_all_shops()
|
||||
|
||||
elif choice == "6":
|
||||
deactivate_all_shops()
|
||||
|
||||
elif choice == "0":
|
||||
print("\n👋 Auf Wiedersehen!")
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user