From 3fc1aba22994b0c3cd08c089e17f8083291aee30 Mon Sep 17 00:00:00 2001 From: usmannasir Date: Sat, 27 Dec 2025 21:07:16 +0500 Subject: [PATCH] fix: update custom OLS binaries and add ModSecurity compatibility - Update SHA256 checksums for December 2025 OLS build (v1.8.4.1) - Add RHEL8 module support (cyberpanel_ols_x86_64_rhel8.so) - Add compatible ModSecurity binaries to prevent ABI crashes - Auto-detect and replace ModSecurity when custom OLS is installed - Add auto-rollback feature if new binary fails to start - Fix OWASP CRS UI toggle detection with multi-location checks Features included in new binaries: - PHPConfig support (.htaccess php_value/php_flag) - Origin header forwarding (CORS/WebSocket support) - Header unset fix (uses remove_resp_header API) - Static linking for cross-platform compatibility Platforms supported: - Ubuntu 22.04+/Debian 12+ (ubuntu-static) - AlmaLinux/Rocky/RHEL 9.x (rhel9-static) - AlmaLinux/Rocky/RHEL 8.x (rhel8-static) --- firewall/firewallManager.py | 49 +++++++++++- plogical/modSec.py | 113 ++++++++++++++++++++++++++ plogical/upgrade.py | 154 ++++++++++++++++++++++++++++++++++-- 3 files changed, 307 insertions(+), 9 deletions(-) diff --git a/firewall/firewallManager.py b/firewall/firewallManager.py index 09d95aab0..2159de541 100644 --- a/firewall/firewallManager.py +++ b/firewall/firewallManager.py @@ -1020,8 +1020,9 @@ class FirewallManager: if owaspInstalled == 1 and comodoInstalled == 1: break - # Also check rules.conf for manual OWASP installations + # Check multiple locations for OWASP CRS installation if owaspInstalled == 0: + # Check 1: rules.conf for OWASP includes rulesConfPath = os.path.join(virtualHostUtilities.Server_root, "conf/modsec/rules.conf") if os.path.exists(rulesConfPath): try: @@ -1036,6 +1037,37 @@ class FirewallManager: except: pass + # Check 2: owasp-master.conf exists and has rules loaded + if owaspInstalled == 0: + owaspMasterConf = os.path.join(virtualHostUtilities.Server_root, "conf/modsec/owasp-modsecurity-crs-3.0-master/owasp-master.conf") + if os.path.exists(owaspMasterConf): + try: + command = "sudo cat " + owaspMasterConf + owaspConfig = ProcessUtilities.outputExecutioner(command).splitlines() + # Check if at least one rule file is enabled (not commented) + for items in owaspConfig: + if items.strip() and not items.strip().startswith('#') and 'include' in items.lower(): + owaspInstalled = 1 + break + except: + pass + + # Check 3: OWASP CRS directory exists with rules + if owaspInstalled == 0: + owaspRulesDir = os.path.join(virtualHostUtilities.Server_root, "conf/modsec/owasp-modsecurity-crs-3.0-master/rules") + if os.path.exists(owaspRulesDir): + try: + command = "sudo ls " + owaspRulesDir + " | grep -c '.conf'" + output = ProcessUtilities.outputExecutioner(command).strip() + if output.isdigit() and int(output) > 0: + # Rules exist, check if referenced in httpd_config.conf + for items in httpdConfig: + if 'owasp-modsecurity-crs' in items.lower() or 'owasp-master.conf' in items.lower(): + owaspInstalled = 1 + break + except: + pass + final_dic = { 'modSecInstalled': 1, 'owaspInstalled': owaspInstalled, @@ -1065,6 +1097,7 @@ class FirewallManager: except subprocess.CalledProcessError: pass + # Check multiple locations for OWASP in LiteSpeed Enterprise try: command = 'cat /usr/local/lsws/conf/modsec.conf' output = ProcessUtilities.outputExecutioner(command) @@ -1073,6 +1106,20 @@ class FirewallManager: except: pass + # Also check owasp-master.conf for LSWS Enterprise + if owaspInstalled == 0: + owaspMasterConf = '/usr/local/lsws/conf/modsec/owasp-modsecurity-crs-3.0-master/owasp-master.conf' + if os.path.exists(owaspMasterConf): + try: + command = "cat " + owaspMasterConf + owaspConfig = ProcessUtilities.outputExecutioner(command).splitlines() + for items in owaspConfig: + if items.strip() and not items.strip().startswith('#') and 'include' in items.lower(): + owaspInstalled = 1 + break + except: + pass + final_dic = { 'modSecInstalled': 1, 'owaspInstalled': owaspInstalled, diff --git a/plogical/modSec.py b/plogical/modSec.py index 8b2e708d2..2fbc7ece1 100644 --- a/plogical/modSec.py +++ b/plogical/modSec.py @@ -18,6 +18,102 @@ class modSec: tempRulesFile = "/home/cyberpanel/tempModSecRules" mirrorPath = "cyberpanel.net" + # Compatible ModSecurity binaries (built against custom OLS headers) + # These prevent ABI incompatibility crashes (Signal 11/SIGSEGV) + MODSEC_COMPATIBLE = { + 'rhel8': { + 'url': 'https://cyberpanel.net/mod_security-compatible-rhel8.so', + 'sha256': 'bbbf003bdc7979b98f09b640dffe2cbbe5f855427f41319e4c121403c05837b2' + }, + 'rhel9': { + 'url': 'https://cyberpanel.net/mod_security-compatible-rhel.so', + 'sha256': '19deb2ffbaf1334cf4ce4d46d53f747a75b29e835bf5a01f91ebcc0c78e98629' + }, + 'ubuntu': { + 'url': 'https://cyberpanel.net/mod_security-compatible-ubuntu.so', + 'sha256': 'ed02c813136720bd4b9de5925f6e41bdc8392e494d7740d035479aaca6d1e0cd' + } + } + + @staticmethod + def detectPlatform(): + """Detect OS platform for compatible binary selection""" + try: + # Check for Ubuntu/Debian + if os.path.exists('/etc/lsb-release'): + with open('/etc/lsb-release', 'r') as f: + content = f.read() + if 'Ubuntu' in content or 'ubuntu' in content: + return 'ubuntu' + + # Check for Debian + if os.path.exists('/etc/debian_version'): + return 'ubuntu' # Use Ubuntu binary for Debian + + # Check for RHEL-based distributions + if os.path.exists('/etc/os-release'): + with open('/etc/os-release', 'r') as f: + content = f.read().lower() + + # Check for version 8.x + if 'version="8.' in content or 'version_id="8' in content: + return 'rhel8' + + # Check for version 9.x + if 'version="9.' in content or 'version_id="9' in content: + return 'rhel9' + + return 'rhel9' # Default to rhel9 + except: + return 'rhel9' + + @staticmethod + def downloadCompatibleModSec(platform): + """Download and install compatible ModSecurity binary""" + try: + config = modSec.MODSEC_COMPATIBLE.get(platform) + if not config: + logging.CyberCPLogFileWriter.writeToFile(f"No compatible ModSecurity for platform {platform}") + return False + + modsec_path = "/usr/local/lsws/modules/mod_security.so" + tmp_path = "/tmp/mod_security-compatible.so" + + # Download compatible binary + command = f"wget -q {config['url']} -O {tmp_path}" + result = subprocess.call(shlex.split(command)) + if result != 0: + logging.CyberCPLogFileWriter.writeToFile("Failed to download compatible ModSecurity") + return False + + # Verify checksum + import hashlib + sha256_hash = hashlib.sha256() + with open(tmp_path, "rb") as f: + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + actual_sha256 = sha256_hash.hexdigest() + + if actual_sha256 != config['sha256']: + logging.CyberCPLogFileWriter.writeToFile(f"ModSecurity checksum mismatch: expected {config['sha256']}, got {actual_sha256}") + os.remove(tmp_path) + return False + + # Backup original if exists + if os.path.exists(modsec_path): + shutil.copy2(modsec_path, f"{modsec_path}.stock") + + # Install compatible version + shutil.move(tmp_path, modsec_path) + os.chmod(modsec_path, 0o644) + + logging.CyberCPLogFileWriter.writeToFile("Installed compatible ModSecurity binary") + return True + + except BaseException as msg: + logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [downloadCompatibleModSec]") + return False + @staticmethod def installModSec(): try: @@ -45,6 +141,23 @@ class modSec: writeToFile.writelines("ModSecurity Installed.[200]\n") writeToFile.close() + # Check if custom OLS binary is installed - if so, replace with compatible ModSecurity + custom_ols_marker = "/usr/local/lsws/modules/cyberpanel_ols.so" + if os.path.exists(custom_ols_marker): + writeToFile = open(modSec.installLogPath, 'a') + writeToFile.writelines("Custom OLS detected, installing compatible ModSecurity...\n") + writeToFile.close() + + platform = modSec.detectPlatform() + if modSec.downloadCompatibleModSec(platform): + writeToFile = open(modSec.installLogPath, 'a') + writeToFile.writelines("Compatible ModSecurity installed successfully.\n") + writeToFile.close() + else: + writeToFile = open(modSec.installLogPath, 'a') + writeToFile.writelines("WARNING: Could not install compatible ModSecurity. May experience crashes.\n") + writeToFile.close() + return 1 except BaseException as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + "[installModSec]") diff --git a/plogical/upgrade.py b/plogical/upgrade.py index 6ba031d97..ea72adbb9 100644 --- a/plogical/upgrade.py +++ b/plogical/upgrade.py @@ -733,25 +733,32 @@ class Upgrade: platform = Upgrade.detectPlatform() Upgrade.stdOut(f"Detected platform: {platform}", 0) - # Platform-specific URLs and checksums (OpenLiteSpeed v1.8.4.1 - v2.0.5 Static Build) + # Platform-specific URLs and checksums (OpenLiteSpeed v1.8.4.1 with PHPConfig + Header unset fix + Static Linking) + # Build Date: December 27, 2025 BINARY_CONFIGS = { 'rhel8': { 'url': 'https://cyberpanel.net/openlitespeed-phpconfig-x86_64-rhel8-static', 'sha256': '6ce688a237615102cc1603ee1999b3cede0ff3482d31e1f65705e92396d34b3a', - 'module_url': None, # RHEL 8 doesn't have module (use RHEL 9 if needed) - 'module_sha256': None + 'module_url': 'https://cyberpanel.net/cyberpanel_ols_x86_64_rhel8.so', + 'module_sha256': 'c57f6f14a9ba787b9051dee98c1375a4f34ec4e25b492e97a8825aee04dda02a', + 'modsec_url': 'https://cyberpanel.net/mod_security-compatible-rhel8.so', + 'modsec_sha256': 'bbbf003bdc7979b98f09b640dffe2cbbe5f855427f41319e4c121403c05837b2' }, 'rhel9': { 'url': 'https://cyberpanel.net/openlitespeed-phpconfig-x86_64-rhel9-static', - 'sha256': '90468fb38767505185013024678d9144ae13100d2355097657f58719d98fbbc4', + 'sha256': '709093d99d5d3e789134c131893614968e17eefd9ade2200f811d9b076b2f02e', 'module_url': 'https://cyberpanel.net/cyberpanel_ols_x86_64_rhel.so', - 'module_sha256': '127227db81bcbebf80b225fc747b69cfcd4ad2f01cea486aa02d5c9ba6c18109' + 'module_sha256': 'ae79d4fcf56131c01c3d81dc704ad265ac881b61d0a90cec62e4ac22c0e69929', + 'modsec_url': 'https://cyberpanel.net/mod_security-compatible-rhel.so', + 'modsec_sha256': '19deb2ffbaf1334cf4ce4d46d53f747a75b29e835bf5a01f91ebcc0c78e98629' }, 'ubuntu': { 'url': 'https://cyberpanel.net/openlitespeed-phpconfig-x86_64-ubuntu-static', 'sha256': '89aaf66474e78cb3c1666784e0e7a417550bd317e6ab148201bdc318d36710cb', 'module_url': 'https://cyberpanel.net/cyberpanel_ols_x86_64_ubuntu.so', - 'module_sha256': 'e7734f1e6226c2a0a8e00c1f6534ea9f577df9081b046736a774b1c52c28e7e5' + 'module_sha256': '57129f12b98c5b1693d10eddad3ad57917773540ca68c5491dee23588ef313ac', + 'modsec_url': 'https://cyberpanel.net/mod_security-compatible-ubuntu.so', + 'modsec_sha256': 'ed02c813136720bd4b9de5925f6e41bdc8392e494d7740d035479aaca6d1e0cd' } } @@ -765,8 +772,11 @@ class Upgrade: OLS_BINARY_SHA256 = config['sha256'] MODULE_URL = config['module_url'] MODULE_SHA256 = config['module_sha256'] + MODSEC_URL = config.get('modsec_url') + MODSEC_SHA256 = config.get('modsec_sha256') OLS_BINARY_PATH = "/usr/local/lsws/bin/openlitespeed" MODULE_PATH = "/usr/local/lsws/modules/cyberpanel_ols.so" + MODSEC_PATH = "/usr/local/lsws/modules/mod_security.so" # Create backup from datetime import datetime @@ -778,12 +788,16 @@ class Upgrade: if os.path.exists(OLS_BINARY_PATH): shutil.copy2(OLS_BINARY_PATH, f"{backup_dir}/openlitespeed.backup") Upgrade.stdOut(f"Backup created at: {backup_dir}", 0) + # Also backup existing ModSecurity if it exists + if os.path.exists(MODSEC_PATH): + shutil.copy2(MODSEC_PATH, f"{backup_dir}/mod_security.so.backup") except Exception as e: Upgrade.stdOut(f"WARNING: Could not create backup: {e}", 0) # Download binaries to temp location tmp_binary = "/tmp/openlitespeed-custom" tmp_module = "/tmp/cyberpanel_ols.so" + tmp_modsec = "/tmp/mod_security.so" Upgrade.stdOut("Downloading custom binaries...", 0) @@ -804,6 +818,18 @@ class Upgrade: else: Upgrade.stdOut("Note: No CyberPanel module for this platform", 0) + # Download compatible ModSecurity if existing ModSecurity is installed + # This prevents ABI incompatibility crashes (Signal 11/SIGSEGV) + modsec_downloaded = False + if os.path.exists(MODSEC_PATH) and MODSEC_URL and MODSEC_SHA256: + Upgrade.stdOut("Existing ModSecurity detected - downloading compatible version...", 0) + if Upgrade.downloadCustomBinary(MODSEC_URL, tmp_modsec, MODSEC_SHA256): + modsec_downloaded = True + else: + Upgrade.stdOut("WARNING: Failed to download compatible ModSecurity", 0) + Upgrade.stdOut("ModSecurity may crash due to ABI incompatibility", 0) + Upgrade.stdOut("Consider manually updating ModSecurity after upgrade", 0) + # Install OpenLiteSpeed binary Upgrade.stdOut("Installing custom binaries...", 0) @@ -826,9 +852,49 @@ class Upgrade: Upgrade.stdOut(f"ERROR: Failed to install module: {e}", 0) return False - # Verify installation + # Install compatible ModSecurity (if downloaded) + if modsec_downloaded: + try: + shutil.move(tmp_modsec, MODSEC_PATH) + os.chmod(MODSEC_PATH, 0o644) + Upgrade.stdOut("Installed compatible ModSecurity module", 0) + except Exception as e: + Upgrade.stdOut(f"WARNING: Failed to install ModSecurity: {e}", 0) + # Non-fatal, continue + + # Verify installation - test binary before restart if os.path.exists(OLS_BINARY_PATH): if not module_downloaded or os.path.exists(MODULE_PATH): + # Test 1: Verify binary is executable and shows version + Upgrade.stdOut("Verifying new binary...", 0) + try: + result = subprocess.run( + [OLS_BINARY_PATH, '-v'], + capture_output=True, + text=True, + timeout=10 + ) + if result.returncode != 0: + raise Exception(f"Binary test failed with exit code {result.returncode}") + + # Extract version info + version_output = result.stdout if result.stdout else result.stderr + if 'LiteSpeed' in version_output or 'OpenLiteSpeed' in version_output: + Upgrade.stdOut(f"Binary version check passed", 0) + else: + Upgrade.stdOut("WARNING: Could not verify binary version", 0) + except subprocess.TimeoutExpired: + Upgrade.stdOut("WARNING: Binary version check timed out", 0) + except Exception as e: + Upgrade.stdOut(f"ERROR: Binary verification failed: {e}", 0) + # Auto-rollback + Upgrade.stdOut("Initiating auto-rollback...", 0) + if Upgrade.rollbackOLSBinary(backup_dir, OLS_BINARY_PATH, MODULE_PATH if module_downloaded else None): + Upgrade.stdOut("Rollback completed successfully", 0) + else: + Upgrade.stdOut("WARNING: Rollback may have failed", 0) + return False + Upgrade.stdOut("=" * 50, 0) Upgrade.stdOut("Custom Binaries Installed Successfully", 0) Upgrade.stdOut("Features enabled:", 0) @@ -842,6 +908,9 @@ class Upgrade: return True Upgrade.stdOut("ERROR: Installation verification failed", 0) + # Auto-rollback on verification failure + if Upgrade.rollbackOLSBinary(backup_dir, OLS_BINARY_PATH, MODULE_PATH if module_downloaded else None): + Upgrade.stdOut("Rollback completed successfully", 0) return False except Exception as msg: @@ -849,6 +918,50 @@ class Upgrade: Upgrade.stdOut("Continuing with standard OLS", 0) return True # Non-fatal error, continue + @staticmethod + def rollbackOLSBinary(backup_dir, binary_path, module_path=None): + """Rollback OpenLiteSpeed binary to previous version from backup""" + try: + Upgrade.stdOut("Rolling back to previous binary...", 0) + + backup_binary = os.path.join(backup_dir, "openlitespeed.backup") + + if os.path.exists(backup_binary): + # Stop OLS before rollback + Upgrade.stdOut("Stopping OpenLiteSpeed for rollback...", 0) + subprocess.run(['/usr/local/lsws/bin/lswsctrl', 'stop'], + capture_output=True, timeout=30) + + # Restore binary + shutil.copy2(backup_binary, binary_path) + os.chmod(binary_path, 0o755) + Upgrade.stdOut(f"Restored binary from {backup_binary}", 0) + + # Start OLS after rollback + Upgrade.stdOut("Starting OpenLiteSpeed after rollback...", 0) + result = subprocess.run(['/usr/local/lsws/bin/lswsctrl', 'start'], + capture_output=True, timeout=30) + + # Verify OLS started + import time + time.sleep(3) + + result = subprocess.run(['pgrep', '-f', 'openlitespeed'], + capture_output=True) + if result.returncode == 0: + Upgrade.stdOut("OpenLiteSpeed started successfully after rollback", 0) + return True + else: + Upgrade.stdOut("WARNING: OpenLiteSpeed may not have started after rollback", 0) + return True # Rollback was successful, startup issue is separate + else: + Upgrade.stdOut(f"ERROR: Backup not found at {backup_binary}", 0) + return False + + except Exception as e: + Upgrade.stdOut(f"ERROR during rollback: {e}", 0) + return False + @staticmethod def configureCustomModule(): """Configure CyberPanel module in OpenLiteSpeed config""" @@ -4482,10 +4595,35 @@ pm.max_spare_servers = 3 # Configure the custom module Upgrade.configureCustomModule() - # Restart OpenLiteSpeed to apply changes + # Restart OpenLiteSpeed to apply changes and verify it started Upgrade.stdOut("Restarting OpenLiteSpeed...", 0) command = '/usr/local/lsws/bin/lswsctrl restart' Upgrade.executioner(command, 'Restart OpenLiteSpeed', 0) + + # Verify OLS started successfully after restart + import time + time.sleep(5) # Give OLS time to start + + result = subprocess.run(['pgrep', '-f', 'openlitespeed'], + capture_output=True) + if result.returncode != 0: + Upgrade.stdOut("WARNING: OpenLiteSpeed may not have started after upgrade!", 0) + Upgrade.stdOut("Attempting auto-rollback...", 0) + + # Find the most recent backup directory + backup_base = '/usr/local/lsws' + backups = [d for d in os.listdir(backup_base) if d.startswith('backup-')] + if backups: + backups.sort(reverse=True) # Most recent first + latest_backup = os.path.join(backup_base, backups[0]) + if Upgrade.rollbackOLSBinary(latest_backup, '/usr/local/lsws/bin/openlitespeed'): + Upgrade.stdOut("Auto-rollback completed successfully", 0) + else: + Upgrade.stdOut("ERROR: Auto-rollback failed! Manual intervention may be required.", 0) + else: + Upgrade.stdOut("ERROR: No backup found for rollback!", 0) + else: + Upgrade.stdOut("OpenLiteSpeed restarted successfully", 0) else: Upgrade.stdOut("Custom binary installation failed, continuing with upgrade...", 0)