geoip_shop_manager.py aktualisiert

This commit is contained in:
2025-12-09 10:37:27 +01:00
parent 4e0960ccff
commit 6a253ad01d

View File

@@ -1,8 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
GeoIP Shop Blocker Manager - DACH Version GeoIP Shop Blocker Manager - DACH & Eurozone Version
2-Component System: PHP blocking + Python watcher (systemd service) 2-Component System: PHP blocking + Python watcher (systemd service)
Blocks all IPs outside Germany, Austria, and Switzerland (DACH region) Supports two geo regions:
- DACH: Germany, Austria, Switzerland (3 countries)
- Eurozone+GB: All Eurozone countries + GB + CH (22 countries)
""" """
import os import os
@@ -20,6 +22,7 @@ from pathlib import Path
COLOR_GREEN = "\033[92m" COLOR_GREEN = "\033[92m"
COLOR_RED = "\033[91m" COLOR_RED = "\033[91m"
COLOR_YELLOW = "\033[93m" COLOR_YELLOW = "\033[93m"
COLOR_BLUE = "\033[94m"
COLOR_RESET = "\033[0m" COLOR_RESET = "\033[0m"
COLOR_BOLD = "\033[1m" COLOR_BOLD = "\033[1m"
@@ -33,13 +36,56 @@ DNS_CACHE = {}
VHOSTS_DIR = "/var/www/vhosts" VHOSTS_DIR = "/var/www/vhosts"
BACKUP_SUFFIX = ".geoip_backup" BACKUP_SUFFIX = ".geoip_backup"
BLOCKING_FILE = "geoip_blocking.php" BLOCKING_FILE = "geoip_blocking.php"
CACHE_FILE = "dach_ip_ranges.cache" CACHE_FILE = "geoip_ip_ranges.cache"
LOG_FILE = "geoip_blocked.log" LOG_FILE = "geoip_blocked.log"
CROWDSEC_QUEUE_FILE = "geoip_crowdsec_queue.log" CROWDSEC_QUEUE_FILE = "geoip_crowdsec_queue.log"
WATCHER_SCRIPT = "/usr/local/bin/geoip_crowdsec_watcher.py" WATCHER_SCRIPT = "/usr/local/bin/geoip_crowdsec_watcher.py"
SYSTEMD_SERVICE = "/etc/systemd/system/geoip-crowdsec-watcher.service" SYSTEMD_SERVICE = "/etc/systemd/system/geoip-crowdsec-watcher.service"
ACTIVE_SHOPS_FILE = "/var/lib/crowdsec/geoip_active_shops.json" ACTIVE_SHOPS_FILE = "/var/lib/crowdsec/geoip_active_shops.json"
# =============================================================================
# GEO REGIONS
# =============================================================================
GEO_REGIONS = {
"dach": {
"name": "DACH",
"countries": ["de", "at", "ch"],
"description": "Deutschland, Österreich, Schweiz",
"icon": "🇩🇪🇦🇹🇨🇭",
"short": "DACH"
},
"eurozone": {
"name": "Eurozone + GB",
"countries": [
"de", # Deutschland
"at", # Österreich
"ch", # Schweiz
"be", # Belgien
"cy", # Zypern
"ee", # Estland
"es", # Spanien
"fi", # Finnland
"fr", # Frankreich
"gb", # Großbritannien
"gr", # Griechenland
"hr", # Kroatien
"ie", # Irland
"it", # Italien
"lt", # Litauen
"lu", # Luxemburg
"lv", # Lettland
"mt", # Malta
"nl", # Niederlande
"pt", # Portugal
"si", # Slowenien
"sk", # Slowakei
],
"description": "22 Länder: DE, AT, CH, BE, CY, EE, ES, FI, FR, GB, GR, HR, IE, IT, LT, LU, LV, MT, NL, PT, SI, SK",
"icon": "🇪🇺",
"short": "EU+"
}
}
# ============================================================================= # =============================================================================
# BOT DETECTION - Comprehensive list of known bots/crawlers # BOT DETECTION - Comprehensive list of known bots/crawlers
# ============================================================================= # =============================================================================
@@ -221,11 +267,23 @@ def format_shop_with_link11(shop, prefix="", show_index=None):
return f"{prefix}{color}{shop}{suffix}{COLOR_RESET}" return f"{prefix}{color}{shop}{suffix}{COLOR_RESET}"
# PHP GeoIP blocking script (no exec, just logging) def get_geo_region_info(geo_region):
GEOIP_SCRIPT = '''<?php """Get info for a geo region"""
return GEO_REGIONS.get(geo_region, GEO_REGIONS["dach"])
def generate_php_countries_array(geo_region):
"""Generate PHP array string for countries"""
region_info = get_geo_region_info(geo_region)
countries = region_info["countries"]
return ", ".join([f"'{c}'" for c in countries])
# PHP GeoIP blocking script template (with CrowdSec)
GEOIP_SCRIPT_TEMPLATE = '''<?php
/** /**
* GeoIP Blocking Script - Blocks all non-DACH IPs * GeoIP Blocking Script - Blocks all IPs outside allowed region
* DACH = Germany (DE), Austria (AT), Switzerland (CH) * Region: {region_name} ({region_description})
* Logs blocked IPs for CrowdSec watcher to process * Logs blocked IPs for CrowdSec watcher to process
* Valid until: {expiry_date} * Valid until: {expiry_date}
*/ */
@@ -253,10 +311,12 @@ $cache_duration = 86400; // 24 hours
$log_file = __DIR__ . '/{log_file}'; $log_file = __DIR__ . '/{log_file}';
$crowdsec_queue = __DIR__ . '/{crowdsec_queue}'; $crowdsec_queue = __DIR__ . '/{crowdsec_queue}';
// Function to download DACH IP ranges (Germany, Austria, Switzerland) // Allowed countries
function download_dach_ranges() {{ $allowed_countries = [{countries_array}];
// Function to download IP ranges for allowed countries
function download_allowed_ranges($countries) {{
$ranges = []; $ranges = [];
$countries = ['de', 'at', 'ch']; // Germany, Austria, Switzerland
foreach ($countries as $country) {{ foreach ($countries as $country) {{
$url = "https://www.ipdeny.com/ipblocks/data/aggregated/$country-aggregated.zone"; $url = "https://www.ipdeny.com/ipblocks/data/aggregated/$country-aggregated.zone";
@@ -286,27 +346,27 @@ function ip_in_range($ip, $cidr) {{
}} }}
// Load or download IP ranges // Load or download IP ranges
$dach_ranges = []; $allowed_ranges = [];
if (file_exists($cache_file) && (time() - filemtime($cache_file)) < $cache_duration) {{ if (file_exists($cache_file) && (time() - filemtime($cache_file)) < $cache_duration) {{
$dach_ranges = unserialize(file_get_contents($cache_file)); $allowed_ranges = unserialize(file_get_contents($cache_file));
}} else {{ }} else {{
$dach_ranges = download_dach_ranges(); $allowed_ranges = download_allowed_ranges($allowed_countries);
if (!empty($dach_ranges)) {{ if (!empty($allowed_ranges)) {{
@file_put_contents($cache_file, serialize($dach_ranges)); @file_put_contents($cache_file, serialize($allowed_ranges));
}} }}
}} }}
// Check if visitor IP is from DACH region // Check if visitor IP is from allowed region
$is_dach = false; $is_allowed = false;
foreach ($dach_ranges as $range) {{ foreach ($allowed_ranges as $range) {{
if (ip_in_range($visitor_ip, $range)) {{ if (ip_in_range($visitor_ip, $range)) {{
$is_dach = true; $is_allowed = true;
break; break;
}} }}
}} }}
// Block non-DACH IPs // Block non-allowed IPs
if (!$is_dach) {{ if (!$is_allowed) {{
$timestamp = date('Y-m-d H:i:s'); $timestamp = date('Y-m-d H:i:s');
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown'; $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
$request_uri = $_SERVER['REQUEST_URI'] ?? '/'; $request_uri = $_SERVER['REQUEST_URI'] ?? '/';
@@ -324,11 +384,11 @@ if (!$is_dach) {{
}} }}
''' '''
# PHP GeoIP blocking script - PHP ONLY (no CrowdSec queue) # PHP GeoIP blocking script template - PHP ONLY (no CrowdSec queue)
GEOIP_SCRIPT_PHP_ONLY = '''<?php GEOIP_SCRIPT_TEMPLATE_PHP_ONLY = '''<?php
/** /**
* GeoIP Blocking Script - Blocks all non-DACH IPs (PHP-only mode) * GeoIP Blocking Script - Blocks all IPs outside allowed region (PHP-only mode)
* DACH = Germany (DE), Austria (AT), Switzerland (CH) * Region: {region_name} ({region_description})
* Logs blocked IPs (no CrowdSec synchronisation) * Logs blocked IPs (no CrowdSec synchronisation)
* Valid until: {expiry_date} * Valid until: {expiry_date}
*/ */
@@ -355,10 +415,12 @@ $cache_file = __DIR__ . '/{cache_file}';
$cache_duration = 86400; // 24 hours $cache_duration = 86400; // 24 hours
$log_file = __DIR__ . '/{log_file}'; $log_file = __DIR__ . '/{log_file}';
// Function to download DACH IP ranges (Germany, Austria, Switzerland) // Allowed countries
function download_dach_ranges() {{ $allowed_countries = [{countries_array}];
// Function to download IP ranges for allowed countries
function download_allowed_ranges($countries) {{
$ranges = []; $ranges = [];
$countries = ['de', 'at', 'ch']; // Germany, Austria, Switzerland
foreach ($countries as $country) {{ foreach ($countries as $country) {{
$url = "https://www.ipdeny.com/ipblocks/data/aggregated/$country-aggregated.zone"; $url = "https://www.ipdeny.com/ipblocks/data/aggregated/$country-aggregated.zone";
@@ -388,27 +450,27 @@ function ip_in_range($ip, $cidr) {{
}} }}
// Load or download IP ranges // Load or download IP ranges
$dach_ranges = []; $allowed_ranges = [];
if (file_exists($cache_file) && (time() - filemtime($cache_file)) < $cache_duration) {{ if (file_exists($cache_file) && (time() - filemtime($cache_file)) < $cache_duration) {{
$dach_ranges = unserialize(file_get_contents($cache_file)); $allowed_ranges = unserialize(file_get_contents($cache_file));
}} else {{ }} else {{
$dach_ranges = download_dach_ranges(); $allowed_ranges = download_allowed_ranges($allowed_countries);
if (!empty($dach_ranges)) {{ if (!empty($allowed_ranges)) {{
@file_put_contents($cache_file, serialize($dach_ranges)); @file_put_contents($cache_file, serialize($allowed_ranges));
}} }}
}} }}
// Check if visitor IP is from DACH region // Check if visitor IP is from allowed region
$is_dach = false; $is_allowed = false;
foreach ($dach_ranges as $range) {{ foreach ($allowed_ranges as $range) {{
if (ip_in_range($visitor_ip, $range)) {{ if (ip_in_range($visitor_ip, $range)) {{
$is_dach = true; $is_allowed = true;
break; break;
}} }}
}} }}
// Block non-DACH IPs // Block non-allowed IPs
if (!$is_dach) {{ if (!$is_allowed) {{
$timestamp = date('Y-m-d H:i:s'); $timestamp = date('Y-m-d H:i:s');
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown'; $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
$request_uri = $_SERVER['REQUEST_URI'] ?? '/'; $request_uri = $_SERVER['REQUEST_URI'] ?? '/';
@@ -468,7 +530,7 @@ def add_to_crowdsec(ip, shop):
'--ip', ip, '--ip', ip,
'--duration', '72h', '--duration', '72h',
'--type', 'ban', '--type', 'ban',
'--reason', f'GeoIP: Non-DACH IP blocked by {shop}' '--reason', f'GeoIP: Non-allowed IP blocked by {shop}'
] ]
try: try:
@@ -528,7 +590,7 @@ def process_queue_file(shop_path, shop):
return processed return processed
def main(): def main():
log("🚀 GeoIP CrowdSec Watcher started (DACH mode)") log("🚀 GeoIP CrowdSec Watcher started")
while True: while True:
try: try:
@@ -564,7 +626,7 @@ if __name__ == "__main__":
# Systemd service file # Systemd service file
SYSTEMD_SERVICE_CONTENT = '''[Unit] SYSTEMD_SERVICE_CONTENT = '''[Unit]
Description=GeoIP CrowdSec Watcher Service (DACH) Description=GeoIP CrowdSec Watcher Service
After=network.target crowdsec.service After=network.target crowdsec.service
Wants=crowdsec.service Wants=crowdsec.service
@@ -652,8 +714,8 @@ def uninstall_watcher_service():
print(" ✅ Service deinstalliert") print(" ✅ Service deinstalliert")
def add_shop_to_active(shop, mode="php+crowdsec"): def add_shop_to_active(shop, mode="php+crowdsec", geo_region="dach"):
"""Add shop to active shops tracking with mode""" """Add shop to active shops tracking with mode and geo region"""
os.makedirs(os.path.dirname(ACTIVE_SHOPS_FILE), exist_ok=True) os.makedirs(os.path.dirname(ACTIVE_SHOPS_FILE), exist_ok=True)
shops = {} shops = {}
@@ -664,7 +726,8 @@ def add_shop_to_active(shop, mode="php+crowdsec"):
shops[shop] = { shops[shop] = {
"activated": datetime.now().isoformat(), "activated": datetime.now().isoformat(),
"expiry": (datetime.now() + timedelta(hours=72)).isoformat(), "expiry": (datetime.now() + timedelta(hours=72)).isoformat(),
"mode": mode # "php+crowdsec" or "php-only" "mode": mode, # "php+crowdsec" or "php-only"
"geo_region": geo_region # "dach" or "eurozone"
} }
with open(ACTIVE_SHOPS_FILE, 'w') as f: with open(ACTIVE_SHOPS_FILE, 'w') as f:
@@ -684,6 +747,19 @@ def get_shop_mode(shop):
return "php+crowdsec" return "php+crowdsec"
def get_shop_geo_region(shop):
"""Get the geo region for a shop"""
if not os.path.isfile(ACTIVE_SHOPS_FILE):
return "dach"
try:
with open(ACTIVE_SHOPS_FILE, 'r') as f:
shops = json.load(f)
return shops.get(shop, {}).get("geo_region", "dach")
except:
return "dach"
def get_shop_activation_time(shop): def get_shop_activation_time(shop):
"""Get the activation timestamp for a shop""" """Get the activation timestamp for a shop"""
if not os.path.isfile(ACTIVE_SHOPS_FILE): if not os.path.isfile(ACTIVE_SHOPS_FILE):
@@ -819,13 +895,43 @@ def get_active_shops():
return active return active
def activate_blocking(shop, silent=False, mode="php+crowdsec"): def select_geo_region():
"""Interactive geo region selection"""
print(f"\n🌍 Wähle die Geo-Region:")
print(f" [1] {GEO_REGIONS['dach']['icon']} DACH - {GEO_REGIONS['dach']['description']}")
print(f" [2] {GEO_REGIONS['eurozone']['icon']} Eurozone+GB - {len(GEO_REGIONS['eurozone']['countries'])} Länder")
choice = input(f"\nRegion wählen [1/2]: ").strip()
if choice == "2":
return "eurozone"
else:
return "dach"
def select_mode():
"""Interactive mode selection"""
print(f"\n🔧 Wähle den Blocking-Modus:")
print(f" [1] PHP + CrowdSec (IPs werden an CrowdSec gemeldet)")
print(f" [2] Nur PHP (keine CrowdSec-Synchronisation)")
choice = input(f"\nModus wählen [1/2]: ").strip()
if choice == "2":
return "php-only"
else:
return "php+crowdsec"
def activate_blocking(shop, silent=False, mode="php+crowdsec", geo_region="dach"):
"""Activate GeoIP blocking for a single shop""" """Activate GeoIP blocking for a single shop"""
httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs') httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs')
index_php = os.path.join(httpdocs, 'index.php') index_php = os.path.join(httpdocs, 'index.php')
backup_php = os.path.join(httpdocs, f'index.php{BACKUP_SUFFIX}') backup_php = os.path.join(httpdocs, f'index.php{BACKUP_SUFFIX}')
blocking_file = os.path.join(httpdocs, BLOCKING_FILE) blocking_file = os.path.join(httpdocs, BLOCKING_FILE)
region_info = get_geo_region_info(geo_region)
if os.path.isfile(backup_php): if os.path.isfile(backup_php):
if not silent: if not silent:
print(f"⚠️ GeoIP-Blocking bereits aktiv für {shop}") print(f"⚠️ GeoIP-Blocking bereits aktiv für {shop}")
@@ -837,8 +943,8 @@ def activate_blocking(shop, silent=False, mode="php+crowdsec"):
return False return False
if not silent: if not silent:
print(f"\n🔧 Aktiviere DACH GeoIP-Blocking für: {shop}") print(f"\n🔧 Aktiviere {region_info['icon']} {region_info['name']} GeoIP-Blocking für: {shop}")
print(" (Erlaubt: Deutschland, Österreich, Schweiz)") print(f" Erlaubt: {region_info['description']}")
print(f" Modus: {'PHP + CrowdSec' if mode == 'php+crowdsec' else 'Nur PHP'}") print(f" Modus: {'PHP + CrowdSec' if mode == 'php+crowdsec' else 'Nur PHP'}")
print("=" * 60) print("=" * 60)
@@ -892,25 +998,31 @@ def activate_blocking(shop, silent=False, mode="php+crowdsec"):
print(" ✏️ index.php modifiziert") print(" ✏️ index.php modifiziert")
expiry = datetime.now() + timedelta(hours=72) expiry = datetime.now() + timedelta(hours=72)
countries_array = generate_php_countries_array(geo_region)
# Generate PHP script based on mode # Generate PHP script based on mode
if mode == "php+crowdsec": if mode == "php+crowdsec":
geoip_content = GEOIP_SCRIPT.format( geoip_content = GEOIP_SCRIPT_TEMPLATE.format(
region_name=region_info['name'],
region_description=region_info['description'],
expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'), expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'),
expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'), expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'),
cache_file=CACHE_FILE, cache_file=CACHE_FILE,
log_file=LOG_FILE, log_file=LOG_FILE,
crowdsec_queue=CROWDSEC_QUEUE_FILE, crowdsec_queue=CROWDSEC_QUEUE_FILE,
shop_name=shop shop_name=shop,
countries_array=countries_array
) )
else: else:
# PHP-only mode: no crowdsec queue writing geoip_content = GEOIP_SCRIPT_TEMPLATE_PHP_ONLY.format(
geoip_content = GEOIP_SCRIPT_PHP_ONLY.format( region_name=region_info['name'],
region_description=region_info['description'],
expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'), expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'),
expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'), expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'),
cache_file=CACHE_FILE, cache_file=CACHE_FILE,
log_file=LOG_FILE, log_file=LOG_FILE,
shop_name=shop shop_name=shop,
countries_array=countries_array
) )
with open(blocking_file, 'w', encoding='utf-8') as f: with open(blocking_file, 'w', encoding='utf-8') as f:
@@ -921,14 +1033,14 @@ def activate_blocking(shop, silent=False, mode="php+crowdsec"):
# Step 3: Register shop # Step 3: Register shop
if not silent: if not silent:
print("\n[3/3] Registriere Shop...") print("\n[3/3] Registriere Shop...")
add_shop_to_active(shop, mode) add_shop_to_active(shop, mode, geo_region)
if not silent: if not silent:
print(" ✅ Shop registriert") print(" ✅ Shop registriert")
if not silent: if not silent:
print("\n" + "=" * 60) print("\n" + "=" * 60)
print(f"DACH GeoIP-Blocking aktiviert für: {shop}") print(f"{region_info['icon']} {region_info['name']} GeoIP-Blocking aktiviert für: {shop}")
print(f" Erlaubte Länder: 🇩🇪 DE | 🇦🇹 AT | 🇨🇭 CH") print(f" Erlaubte Länder: {region_info['description']}")
print(f" Modus: {'PHP + CrowdSec 🛡️' if mode == 'php+crowdsec' else 'Nur PHP 📝'}") print(f" Modus: {'PHP + CrowdSec 🛡️' if mode == 'php+crowdsec' else 'Nur PHP 📝'}")
print(f" Gültig bis: {expiry.strftime('%Y-%m-%d %H:%M:%S CET')}") print(f" Gültig bis: {expiry.strftime('%Y-%m-%d %H:%M:%S CET')}")
print(f" PHP-Log: {os.path.join(httpdocs, LOG_FILE)}") print(f" PHP-Log: {os.path.join(httpdocs, LOG_FILE)}")
@@ -950,11 +1062,13 @@ def deactivate_blocking(shop, silent=False):
log_file = os.path.join(httpdocs, LOG_FILE) log_file = os.path.join(httpdocs, LOG_FILE)
queue_file = os.path.join(httpdocs, CROWDSEC_QUEUE_FILE) queue_file = os.path.join(httpdocs, CROWDSEC_QUEUE_FILE)
# Get mode before removing from tracking # Get mode and geo region before removing from tracking
shop_mode = get_shop_mode(shop) shop_mode = get_shop_mode(shop)
shop_geo = get_shop_geo_region(shop)
region_info = get_geo_region_info(shop_geo)
if not silent: if not silent:
print(f"\n🔧 Deaktiviere DACH GeoIP-Blocking für: {shop}") print(f"\n🔧 Deaktiviere {region_info['icon']} {region_info['name']} GeoIP-Blocking für: {shop}")
print(f" Modus war: {'PHP + CrowdSec' if shop_mode == 'php+crowdsec' else 'Nur PHP'}") print(f" Modus war: {'PHP + CrowdSec' if shop_mode == 'php+crowdsec' else 'Nur PHP'}")
print("=" * 60) print("=" * 60)
@@ -1011,7 +1125,7 @@ def deactivate_blocking(shop, silent=False):
if not silent: if not silent:
print("\n" + "=" * 60) print("\n" + "=" * 60)
print(f" DACH GeoIP-Blocking deaktiviert für: {shop}") print(f"✅ GeoIP-Blocking deaktiviert für: {shop}")
print("=" * 60) print("=" * 60)
return True return True
@@ -1029,7 +1143,7 @@ def activate_all_shops():
return return
print(f"\n{'=' * 60}") print(f"\n{'=' * 60}")
print(f" DACH GeoIP-Blocking für ALLE Shops aktivieren") print(f" GeoIP-Blocking für ALLE Shops aktivieren")
print(f"{'=' * 60}") print(f"{'=' * 60}")
print(f"\n📋 Folgende {len(available_shops)} Shop(s) werden aktiviert:") 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") print(f" {COLOR_GREEN}Grün = hinter Link11{COLOR_RESET} | {COLOR_RED}Rot = Direkt{COLOR_RESET}\n")
@@ -1040,20 +1154,16 @@ def activate_all_shops():
link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]" link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]"
print(f"{color}{shop} {link11_tag}{COLOR_RESET}") print(f"{color}{shop} {link11_tag}{COLOR_RESET}")
# Ask for mode # Ask for geo region
print(f"\n🔧 Wähle den Blocking-Modus:") geo_region = select_geo_region()
print(f" [1] PHP + CrowdSec (IPs werden an CrowdSec gemeldet)") region_info = get_geo_region_info(geo_region)
print(f" [2] Nur PHP (keine CrowdSec-Synchronisation)")
mode_choice = input(f"\nModus wählen [1/2]: ").strip()
if mode_choice == "2": # Ask for mode
mode = "php-only" mode = select_mode()
mode_display = "Nur PHP 📝" mode_display = "PHP + CrowdSec 🛡️" if mode == "php+crowdsec" else "Nur PHP 📝"
else:
mode = "php+crowdsec"
mode_display = "PHP + CrowdSec 🛡️"
print(f"\n⚠️ Dies aktiviert den Schutz für alle oben genannten Shops!") print(f"\n⚠️ Dies aktiviert den Schutz für alle oben genannten Shops!")
print(f" Region: {region_info['icon']} {region_info['name']}")
print(f" Modus: {mode_display}") print(f" Modus: {mode_display}")
confirm = input(f"\nFortfahren? (ja/nein): ").strip().lower() confirm = input(f"\nFortfahren? (ja/nein): ").strip().lower()
@@ -1062,7 +1172,7 @@ def activate_all_shops():
return return
print(f"\n{'=' * 60}") print(f"\n{'=' * 60}")
print(f" Starte Aktivierung ({mode_display})...") print(f" Starte Aktivierung ({region_info['icon']} {region_info['name']}, {mode_display})...")
print(f"{'=' * 60}") print(f"{'=' * 60}")
success_count = 0 success_count = 0
@@ -1122,29 +1232,37 @@ def activate_all_shops():
# Create blocking script based on mode # Create blocking script based on mode
expiry = datetime.now() + timedelta(hours=72) expiry = datetime.now() + timedelta(hours=72)
countries_array = generate_php_countries_array(geo_region)
if mode == "php+crowdsec": if mode == "php+crowdsec":
geoip_content = GEOIP_SCRIPT.format( geoip_content = GEOIP_SCRIPT_TEMPLATE.format(
region_name=region_info['name'],
region_description=region_info['description'],
expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'), expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'),
expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'), expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'),
cache_file=CACHE_FILE, cache_file=CACHE_FILE,
log_file=LOG_FILE, log_file=LOG_FILE,
crowdsec_queue=CROWDSEC_QUEUE_FILE, crowdsec_queue=CROWDSEC_QUEUE_FILE,
shop_name=shop shop_name=shop,
countries_array=countries_array
) )
else: else:
geoip_content = GEOIP_SCRIPT_PHP_ONLY.format( geoip_content = GEOIP_SCRIPT_TEMPLATE_PHP_ONLY.format(
region_name=region_info['name'],
region_description=region_info['description'],
expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'), expiry_date=expiry.strftime('%Y-%m-%d %H:%M:%S CET'),
expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'), expiry_timestamp=expiry.strftime('%Y-%m-%d %H:%M:%S'),
cache_file=CACHE_FILE, cache_file=CACHE_FILE,
log_file=LOG_FILE, log_file=LOG_FILE,
shop_name=shop shop_name=shop,
countries_array=countries_array
) )
with open(blocking_file, 'w', encoding='utf-8') as f: with open(blocking_file, 'w', encoding='utf-8') as f:
f.write(geoip_content) f.write(geoip_content)
# Register shop with mode # Register shop with mode and geo region
add_shop_to_active(shop, mode) add_shop_to_active(shop, mode, geo_region)
print(f" ✅ Aktiviert (bis {expiry.strftime('%Y-%m-%d %H:%M')})") print(f" ✅ Aktiviert (bis {expiry.strftime('%Y-%m-%d %H:%M')})")
success_count += 1 success_count += 1
@@ -1166,7 +1284,7 @@ def activate_all_shops():
for shop in failed_shops: for shop in failed_shops:
print(f"{shop}") print(f"{shop}")
print(f"\n 🇩🇪 🇦🇹 🇨🇭 Nur DACH-Traffic erlaubt") print(f"\n {region_info['icon']} Region: {region_info['name']}")
print(f" 🔧 Modus: {mode_display}") print(f" 🔧 Modus: {mode_display}")
print(f" ⏰ Gültig für 72 Stunden") print(f" ⏰ Gültig für 72 Stunden")
print(f"{'=' * 60}") print(f"{'=' * 60}")
@@ -1181,7 +1299,7 @@ def deactivate_all_shops():
return return
print(f"\n{'=' * 60}") print(f"\n{'=' * 60}")
print(f" DACH GeoIP-Blocking für ALLE Shops deaktivieren") print(f" GeoIP-Blocking für ALLE Shops deaktivieren")
print(f"{'=' * 60}") print(f"{'=' * 60}")
print(f"\n📋 Folgende {len(active_shops)} Shop(s) werden deaktiviert:") 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") print(f" {COLOR_GREEN}Grün = hinter Link11{COLOR_RESET} | {COLOR_RED}Rot = Direkt{COLOR_RESET}\n")
@@ -1190,7 +1308,9 @@ def deactivate_all_shops():
link11_info = check_link11(shop) link11_info = check_link11(shop)
color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED
link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]" link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]"
print(f"{color}{shop} {link11_tag}{COLOR_RESET}") geo_region = get_shop_geo_region(shop)
region_info = get_geo_region_info(geo_region)
print(f"{color}{shop} {link11_tag}{COLOR_RESET} {region_info['icon']}")
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!")
@@ -1404,6 +1524,11 @@ 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 "?"
# Get geo region icon
geo_region = get_shop_geo_region(shop)
region_info = get_geo_region_info(geo_region)
geo_icon = region_info['icon']
# Color shop name based on Link11 status # Color shop name based on Link11 status
link11_info = check_link11(shop) link11_info = check_link11(shop)
if link11_info['is_link11']: if link11_info['is_link11']:
@@ -1411,7 +1536,7 @@ def show_all_logs():
else: else:
shop_colored = f"{COLOR_RED}{shop}{COLOR_RESET}" shop_colored = f"{COLOR_RED}{shop}{COLOR_RESET}"
print(f" ├─ {shop_colored}: {count} ({req_min:.1f} req/min, seit {runtime_str}) {bar}") print(f" ├─ {shop_colored} {geo_icon}: {count} ({req_min:.1f} req/min, seit {runtime_str}) {bar}")
# Show top IP for this shop # Show top IP for this shop
shop_ips = stats['ips'] shop_ips = stats['ips']
@@ -1438,6 +1563,11 @@ def show_all_logs():
count = crowdsec_stats[shop] count = crowdsec_stats[shop]
bar = "" * min(count // 10, 20) if count > 0 else "" bar = "" * min(count // 10, 20) if count > 0 else ""
# Get geo region icon
geo_region = get_shop_geo_region(shop)
region_info = get_geo_region_info(geo_region)
geo_icon = region_info['icon']
# Color shop name based on Link11 status # Color shop name based on Link11 status
link11_info = check_link11(shop) link11_info = check_link11(shop)
if link11_info['is_link11']: if link11_info['is_link11']:
@@ -1445,7 +1575,7 @@ def show_all_logs():
else: else:
shop_colored = f"{COLOR_RED}{shop}{COLOR_RESET}" shop_colored = f"{COLOR_RED}{shop}{COLOR_RESET}"
print(f" ├─ {shop_colored}: {count} {bar}") print(f" ├─ {shop_colored} {geo_icon}: {count} {bar}")
elif check_crowdsec(): elif check_crowdsec():
print(" └─ Keine aktiven Bans") print(" └─ Keine aktiven Bans")
else: else:
@@ -1507,6 +1637,8 @@ def show_logs(shop):
httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs') httpdocs = os.path.join(VHOSTS_DIR, shop, 'httpdocs')
log_file = os.path.join(httpdocs, LOG_FILE) log_file = os.path.join(httpdocs, LOG_FILE)
shop_mode = get_shop_mode(shop) shop_mode = get_shop_mode(shop)
shop_geo = get_shop_geo_region(shop)
region_info = get_geo_region_info(shop_geo)
# Get stats # Get stats
blocks, ips, activation_time = get_shop_log_stats(shop) blocks, ips, activation_time = get_shop_log_stats(shop)
@@ -1526,7 +1658,8 @@ def show_logs(shop):
mode_display = "PHP + CrowdSec 🛡️" if shop_mode == "php+crowdsec" else "Nur PHP 📝" mode_display = "PHP + CrowdSec 🛡️" if shop_mode == "php+crowdsec" else "Nur PHP 📝"
print(f"\n{'' * 70}") print(f"\n{'' * 70}")
print(f"📊 Logs für {shop} [{mode_display}]") print(f"📊 Logs für {shop}")
print(f" {region_info['icon']} {region_info['name']} | {mode_display}")
print(f"{'' * 70}") print(f"{'' * 70}")
print(f"\n⏱️ Aktiviert: {activation_str}") print(f"\n⏱️ Aktiviert: {activation_str}")
print(f"⏱️ Laufzeit: {runtime_str}") print(f"⏱️ Laufzeit: {runtime_str}")
@@ -1613,8 +1746,8 @@ def show_logs(shop):
def main(): def main():
"""Main menu""" """Main menu"""
print("\n" + "=" * 60) print("\n" + "=" * 60)
print(" GeoIP Shop Blocker Manager - DACH Version") print(" GeoIP Shop Blocker Manager")
print(" Erlaubt: 🇩🇪 Deutschland | 🇦🇹 Österreich | 🇨🇭 Schweiz") print(" Regionen: 🇩🇪🇦🇹🇨🇭 DACH | 🇪🇺 Eurozone+GB (22 Länder)")
print(" PHP + CrowdSec Watcher (systemd service)") print(" PHP + CrowdSec Watcher (systemd service)")
print("=" * 60) print("=" * 60)
@@ -1664,22 +1797,17 @@ def main():
if 0 <= shop_idx < len(available_shops): if 0 <= shop_idx < len(available_shops):
selected_shop = available_shops[shop_idx] selected_shop = available_shops[shop_idx]
# Ask for geo region
geo_region = select_geo_region()
region_info = get_geo_region_info(geo_region)
# Ask for mode # Ask for mode
print(f"\n🔧 Wähle den Blocking-Modus:") mode = select_mode()
print(f" [1] PHP + CrowdSec (IPs werden an CrowdSec gemeldet)") mode_display = "PHP + CrowdSec" if mode == "php+crowdsec" else "Nur PHP"
print(f" [2] Nur PHP (keine CrowdSec-Synchronisation)")
mode_choice = input(f"\nModus wählen [1/2]: ").strip()
if mode_choice == "2": confirm = input(f"\n⚠️ {region_info['icon']} {region_info['name']}-Blocking ({mode_display}) aktivieren für '{selected_shop}'? (ja/nein): ").strip().lower()
mode = "php-only"
mode_display = "Nur PHP"
else:
mode = "php+crowdsec"
mode_display = "PHP + CrowdSec"
confirm = input(f"\n⚠️ DACH-Blocking ({mode_display}) aktivieren für '{selected_shop}'? (ja/nein): ").strip().lower()
if confirm in ['ja', 'j', 'yes', 'y']: if confirm in ['ja', 'j', 'yes', 'y']:
activate_blocking(selected_shop, mode=mode) activate_blocking(selected_shop, mode=mode, geo_region=geo_region)
else: else:
print("❌ Ungültig") print("❌ Ungültig")
except ValueError: except ValueError:
@@ -1696,11 +1824,13 @@ def main():
print(f" {COLOR_GREEN}Grün = hinter Link11{COLOR_RESET} | {COLOR_RED}Rot = Direkt{COLOR_RESET}") 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)
geo_region = get_shop_geo_region(shop)
region_info = get_geo_region_info(geo_region)
mode_icon = "🛡️" if mode == "php+crowdsec" else "📝" mode_icon = "🛡️" if mode == "php+crowdsec" else "📝"
link11_info = check_link11(shop) link11_info = check_link11(shop)
color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED
link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]" link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]"
print(f" [{i}] {color}{shop} {link11_tag}{COLOR_RESET} {mode_icon}") print(f" [{i}] {color}{shop} {link11_tag}{COLOR_RESET} {region_info['icon']} {mode_icon}")
shop_choice = input("\nWähle einen Shop: ").strip() shop_choice = input("\nWähle einen Shop: ").strip()
try: try:
@@ -1726,11 +1856,13 @@ def main():
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)
geo_region = get_shop_geo_region(shop)
region_info = get_geo_region_info(geo_region)
mode_icon = "🛡️" if mode == "php+crowdsec" else "📝" mode_icon = "🛡️" if mode == "php+crowdsec" else "📝"
link11_info = check_link11(shop) link11_info = check_link11(shop)
color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED
link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]" link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]"
print(f" [{i}] {color}{shop} {link11_tag}{COLOR_RESET} {mode_icon}") print(f" [{i}] {color}{shop} {link11_tag}{COLOR_RESET} {region_info['icon']} {mode_icon}")
shop_choice = input("\nWähle eine Option: ").strip() shop_choice = input("\nWähle eine Option: ").strip()
try: try:
@@ -1749,11 +1881,13 @@ def main():
active_shops = get_active_shops() active_shops = get_active_shops()
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 GeoIP-Blockings: {len(active_shops)}")
print(f" {COLOR_GREEN}Grün = hinter Link11{COLOR_RESET} | {COLOR_RED}Rot = Direkt{COLOR_RESET}") 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)
geo_region = get_shop_geo_region(shop)
region_info = get_geo_region_info(geo_region)
mode_icon = "🛡️" if mode == "php+crowdsec" else "📝" mode_icon = "🛡️" if mode == "php+crowdsec" else "📝"
mode_text = "PHP+CS" if mode == "php+crowdsec" else "PHP" mode_text = "PHP+CS" if mode == "php+crowdsec" else "PHP"
@@ -1772,7 +1906,7 @@ def main():
color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED color = COLOR_GREEN if link11_info['is_link11'] else COLOR_RED
link11_tag = "[Link11]" if link11_info['is_link11'] else "[Direkt]" 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})") print(f"{color}{shop} {link11_tag}{COLOR_RESET} {region_info['icon']} [{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()