Add ModSecurity compatibility handling for custom OLS binaries

Implement automatic detection and update of ModSecurity when upgrading to custom OpenLiteSpeed binaries.

Problem:
- Stock ModSecurity modules are NOT ABI-compatible with custom OLS binaries
- Using stock ModSecurity with custom OLS causes server crashes (segfaults)
- Custom OLS has different memory layout and function signatures
- ModSecurity must be rebuilt against custom OLS headers

Solution:
- Detect if custom OLS binary is installed (check for PHPConfig markers)
- Detect if ModSecurity is currently installed
- Automatically download and install ABI-compatible ModSecurity
- Verify checksums before installation
- Backup existing ModSecurity before replacing

Implementation:
- isCustomOLSBinaryInstalled(): Detects custom OLS by scanning binary for markers
- installCompatibleModSecurity(): Downloads OS-specific compatible ModSecurity
- handleModSecurityCompatibility(): Orchestrates the compatibility check and update
- Integrated into upgrade process after custom binary installation

Binary URLs:
- RHEL/AlmaLinux: https://cyberpanel.net/mod_security-compatible-rhel.so
- Ubuntu/Debian: https://cyberpanel.net/mod_security-compatible-ubuntu.so

Checksums:
- RHEL SHA256: db580afc431fda40d46bdae2249ac74690d9175ff6d8b1843f2837d86f8d602f
- Ubuntu SHA256: 115971fcd44b74bc7c7b097b9cec33ddcfb0fb07bb9b562ec9f4f0691c388a6b

Safety features:
- Checksum verification before installation
- Automatic backup of existing ModSecurity
- Graceful OLS restart with timeout handling
- Non-fatal errors allow upgrade to continue

This prevents server crashes for existing CyberPanel users who have ModSecurity
installed when they upgrade to custom OpenLiteSpeed binaries.
This commit is contained in:
usmannasir 2025-11-08 15:23:59 +05:00
parent 120dcd3d20
commit 44bc736ee7
1 changed files with 138 additions and 0 deletions

View File

@ -882,6 +882,141 @@ class Upgrade:
Upgrade.stdOut("Continuing with standard OLS", 0)
return True # Non-fatal error, continue
@staticmethod
def isCustomOLSBinaryInstalled():
"""Detect if custom OpenLiteSpeed binary is installed"""
try:
OLS_BINARY_PATH = "/usr/local/lsws/bin/openlitespeed"
if not os.path.exists(OLS_BINARY_PATH):
return False
# Check for PHPConfig function signature in binary
command = f'strings {OLS_BINARY_PATH}'
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=10)
if result.returncode == 0:
# Look for custom binary markers
return 'set_php_config_value' in result.stdout or 'PHPConfig LSIAPI' in result.stdout
return False
except Exception as msg:
Upgrade.stdOut(f"WARNING: Could not detect OLS binary type: {msg}", 0)
return False
@staticmethod
def installCompatibleModSecurity():
"""Install ModSecurity compatible with custom OpenLiteSpeed binary"""
try:
Upgrade.stdOut("Installing ModSecurity compatible with custom OpenLiteSpeed binary...", 0)
MODSEC_PATH = "/usr/local/lsws/modules/mod_security.so"
# Detect OS and select appropriate ModSecurity binary
binary_suffix = Upgrade.detectBinarySuffix()
if binary_suffix == 'rhel':
MODSEC_URL = "https://cyberpanel.net/mod_security-compatible-rhel.so"
EXPECTED_SHA256 = "db580afc431fda40d46bdae2249ac74690d9175ff6d8b1843f2837d86f8d602f"
EXPECTED_MD5 = "1efa1e442fe8eedf4705584ac194fc95"
else: # ubuntu
MODSEC_URL = "https://cyberpanel.net/mod_security-compatible-ubuntu.so"
EXPECTED_SHA256 = "115971fcd44b74bc7c7b097b9cec33ddcfb0fb07bb9b562ec9f4f0691c388a6b"
EXPECTED_MD5 = "c3987c41182355c1290530b6553658db"
# Download to temp location
tmp_modsec = "/tmp/mod_security_custom.so"
Upgrade.stdOut(f"Downloading compatible ModSecurity for {binary_suffix}...", 0)
command = f'wget -q --show-progress {MODSEC_URL} -O {tmp_modsec}'
result = subprocess.call(shlex.split(command))
if result != 0 or not os.path.exists(tmp_modsec):
Upgrade.stdOut("ERROR: Failed to download ModSecurity", 0)
return False
# Verify checksum
Upgrade.stdOut("Verifying checksum...", 0)
result = subprocess.run(f'sha256sum {tmp_modsec}', shell=True, capture_output=True, text=True)
actual_sha256 = result.stdout.split()[0]
if actual_sha256 != EXPECTED_SHA256:
Upgrade.stdOut(f"ERROR: Checksum verification failed", 0)
Upgrade.stdOut(f" Expected: {EXPECTED_SHA256}", 0)
Upgrade.stdOut(f" Got: {actual_sha256}", 0)
os.remove(tmp_modsec)
return False
Upgrade.stdOut("Checksum verified successfully", 0)
# Backup existing ModSecurity if present
if os.path.exists(MODSEC_PATH):
backup_path = f"{MODSEC_PATH}.backup.{int(time.time())}"
shutil.copy2(MODSEC_PATH, backup_path)
Upgrade.stdOut(f"Backed up existing ModSecurity to: {backup_path}", 0)
# Stop OpenLiteSpeed
Upgrade.stdOut("Stopping OpenLiteSpeed...", 0)
subprocess.run(['/usr/local/lsws/bin/lswsctrl', 'stop'], timeout=30)
time.sleep(2)
# Install compatible ModSecurity
os.makedirs(os.path.dirname(MODSEC_PATH), exist_ok=True)
shutil.copy2(tmp_modsec, MODSEC_PATH)
os.chmod(MODSEC_PATH, 0o755)
os.remove(tmp_modsec)
Upgrade.stdOut("Compatible ModSecurity installed successfully", 0)
# Start OpenLiteSpeed
Upgrade.stdOut("Starting OpenLiteSpeed...", 0)
subprocess.run(['/usr/local/lsws/bin/lswsctrl', 'start'], timeout=30)
Upgrade.stdOut("✓ ModSecurity updated to compatible version", 0)
return True
except subprocess.TimeoutExpired:
Upgrade.stdOut("ERROR: Timeout during OpenLiteSpeed restart", 0)
return False
except Exception as msg:
Upgrade.stdOut(f"ERROR: ModSecurity installation failed: {msg}", 0)
return False
@staticmethod
def handleModSecurityCompatibility():
"""Check and update ModSecurity if custom OLS binary is installed"""
try:
MODSEC_PATH = "/usr/local/lsws/modules/mod_security.so"
# Check if ModSecurity is installed
if not os.path.exists(MODSEC_PATH):
Upgrade.stdOut("ModSecurity not installed, skipping compatibility check", 0)
return True
# Check if custom OLS binary is installed
if not Upgrade.isCustomOLSBinaryInstalled():
Upgrade.stdOut("Stock OLS binary detected, ModSecurity compatibility check not needed", 0)
return True
Upgrade.stdOut("=" * 50, 0)
Upgrade.stdOut("Detected ModSecurity with custom OpenLiteSpeed binary", 0)
Upgrade.stdOut("Updating to ABI-compatible ModSecurity version...", 0)
Upgrade.stdOut("=" * 50, 0)
# Install compatible version
if Upgrade.installCompatibleModSecurity():
Upgrade.stdOut("ModSecurity compatibility update completed", 0)
return True
else:
Upgrade.stdOut("WARNING: ModSecurity compatibility update failed", 0)
Upgrade.stdOut("Server may experience crashes. Please contact support.", 0)
return False
except Exception as msg:
Upgrade.stdOut(f"ERROR in ModSecurity compatibility check: {msg}", 0)
return False
@staticmethod
def configureCustomModule():
"""Configure CyberPanel module in OpenLiteSpeed config"""
@ -4420,6 +4555,9 @@ pm.max_spare_servers = 3
# Configure the custom module
Upgrade.configureCustomModule()
# Check and update ModSecurity compatibility if needed
Upgrade.handleModSecurityCompatibility()
# Restart OpenLiteSpeed to apply changes
Upgrade.stdOut("Restarting OpenLiteSpeed...", 0)
command = '/usr/local/lsws/bin/lswsctrl restart'