diff --git a/jtl-wafi-agent.py b/jtl-wafi-agent.py index 0ff2988..5f2616e 100644 --- a/jtl-wafi-agent.py +++ b/jtl-wafi-agent.py @@ -861,6 +861,140 @@ def set_owner(path: str, uid: int, gid: int, recursive: bool = False): pass +# ============================================================================= +# PHP-FPM RESTART FUNCTIONS +# ============================================================================= +def extract_domain_from_path(shop_path: str) -> Optional[str]: + """ + Extrahiert Domain aus Pfad wie /var/www/vhosts/example.com/httpdocs. + + Args: + shop_path: Shop-Pfad oder Domain + + Returns: + Domain-String oder None + """ + # Wenn es ein Pfad ist + if '/' in shop_path: + match = re.search(r'/var/www/vhosts/([^/]+)', shop_path) + if match: + return match.group(1) + # Ansonsten ist es vermutlich schon eine Domain + return shop_path + + +def find_php_fpm_service(domain: str) -> Optional[str]: + """ + Findet den PHP-FPM Service für eine Domain. + + Sucht nach Services im Format: plesk-php{version}-fpm_{domain}_{id}.service + + Args: + domain: Shop-Domain (z.B. "example.de") + + Returns: + Service-Name oder None wenn nicht gefunden + """ + try: + result = subprocess.run( + ['systemctl', 'list-units', '--type=service', '--all', '--no-legend'], + capture_output=True, + text=True, + timeout=10 + ) + + if result.returncode != 0: + logger.warning(f"systemctl list-units fehlgeschlagen: {result.stderr}") + return None + + for line in result.stdout.split('\n'): + # Suche nach plesk-php*-fpm_{domain}_ + if 'plesk-php' in line and f'fpm_{domain}_' in line: + # Extrahiere Service-Namen (erstes Feld) + parts = line.split() + if parts: + service_name = parts[0] + logger.debug(f"PHP-FPM Service gefunden für {domain}: {service_name}") + return service_name + + logger.debug(f"Kein PHP-FPM Service für {domain} gefunden") + return None + + except subprocess.TimeoutExpired: + logger.warning("systemctl list-units Timeout") + return None + except Exception as e: + logger.warning(f"Fehler beim Suchen des PHP-FPM Service: {e}") + return None + + +def restart_php_fpm(domain: str) -> dict: + """ + Startet den PHP-FPM Service für eine Domain neu. + + Args: + domain: Shop-Domain (z.B. "example.de") + + Returns: + Dict mit 'success', 'service' und 'message' + """ + # Domain aus Pfad extrahieren falls nötig + clean_domain = extract_domain_from_path(domain) + if not clean_domain: + return { + 'success': False, + 'service': None, + 'message': f'Konnte Domain nicht extrahieren aus: {domain}' + } + + service = find_php_fpm_service(clean_domain) + if not service: + return { + 'success': False, + 'service': None, + 'message': f'Kein PHP-FPM Service gefunden für {clean_domain}' + } + + try: + result = subprocess.run( + ['systemctl', 'restart', service], + capture_output=True, + text=True, + timeout=30 + ) + + if result.returncode == 0: + logger.info(f"PHP-FPM Service {service} neugestartet für {clean_domain}") + return { + 'success': True, + 'service': service, + 'message': f'OPcache geleert ({service})' + } + else: + error_msg = result.stderr.strip() if result.stderr else 'Unknown error' + logger.error(f"Fehler beim Neustart von {service}: {error_msg}") + return { + 'success': False, + 'service': service, + 'message': f'Restart fehlgeschlagen: {error_msg}' + } + + except subprocess.TimeoutExpired: + logger.error(f"Timeout beim Neustart von {service}") + return { + 'success': False, + 'service': service, + 'message': 'Restart Timeout (>30s)' + } + except Exception as e: + logger.error(f"Exception beim PHP-FPM Restart: {e}") + return { + 'success': False, + 'service': service, + 'message': str(e) + } + + # ============================================================================= # BOT DETECTION FUNCTIONS # ============================================================================= @@ -2650,6 +2784,7 @@ class JTLWAFiAgent: country_ban_duration = data.get('country_ban_duration') unlimited_countries = data.get('unlimited_countries', []) monitor_only = data.get('monitor_only', False) + restart_fpm = data.get('restart_fpm', False) # Log-Meldung erstellen mode_parts = [] @@ -2678,11 +2813,25 @@ class JTLWAFiAgent: ) if success: + # PHP-FPM Restart wenn gewünscht + fpm_result = None + if restart_fpm: + fpm_result = restart_php_fpm(shop) + if fpm_result['success']: + logger.info(f"PHP-FPM Restart erfolgreich: {fpm_result['message']}") + else: + logger.warning(f"PHP-FPM Restart fehlgeschlagen: {fpm_result['message']}") + + message = f'Shop {shop} aktiviert ({mode_str})' + if fpm_result: + message += f' | FPM: {fpm_result["message"]}' + await self._send_event('command.result', { 'command_id': command_id, 'status': 'success', - 'message': f'Shop {shop} aktiviert ({mode_str})', - 'shop': shop + 'message': message, + 'shop': shop, + 'fpm_restart': fpm_result }) # Full Update senden await self._send_full_update() @@ -2705,6 +2854,7 @@ class JTLWAFiAgent: """Verarbeitet deactivate-Command.""" command_id = data.get('command_id', 'unknown') shop = data.get('shop') + restart_fpm = data.get('restart_fpm', False) logger.info(f"Deaktiviere {shop}") @@ -2712,11 +2862,25 @@ class JTLWAFiAgent: success = deactivate_blocking(shop, silent=True) if success: + # PHP-FPM Restart wenn gewünscht + fpm_result = None + if restart_fpm: + fpm_result = restart_php_fpm(shop) + if fpm_result['success']: + logger.info(f"PHP-FPM Restart erfolgreich: {fpm_result['message']}") + else: + logger.warning(f"PHP-FPM Restart fehlgeschlagen: {fpm_result['message']}") + + message = f'Shop {shop} deaktiviert' + if fpm_result: + message += f' | FPM: {fpm_result["message"]}' + await self._send_event('command.result', { 'command_id': command_id, 'status': 'success', - 'message': f'Shop {shop} deaktiviert', - 'shop': shop + 'message': message, + 'shop': shop, + 'fpm_restart': fpm_result }) # Full Update senden await self._send_full_update()