From f7fc75b258ac25dd91ff3107002870f22cad0e57 Mon Sep 17 00:00:00 2001 From: usmannasir Date: Fri, 7 Nov 2025 13:50:47 +0500 Subject: [PATCH] Add binary verification and rollback mechanism for OLS custom binaries Implement safety checks to verify custom OpenLiteSpeed binaries work before committing to them: Verification checks: - Check library dependencies with ldd to detect missing libraries - Test binary execution with -v flag to ensure it can run - Detect issues like wrong binary type (ubuntu vs rhel) for the OS Rollback mechanism: - Automatically restore original binary from backup if verification fails - Remove incompatible custom module - Continue installation with standard OLS if custom binary fails This prevents installation failures and system downtime when: - Wrong binary type is downloaded due to OS detection issues - Library dependencies are missing - Binary cannot execute on the target system Changes: - Added verifyCustomBinary() method to check dependencies and execution - Added rollbackCustomBinary() method to restore from backup - Updated installCustomOLSBinaries() to verify and rollback on failure - Applied to both install/installCyberPanel.py and plogical/upgrade.py Benefits: - Zero downtime: System falls back to working binary automatically - Better error reporting: Shows which libraries are missing - Safer upgrades: Users won't be left with broken installations --- install/installCyberPanel.py | 111 +++++++++++++++++++++++++++++++---- plogical/upgrade.py | 111 +++++++++++++++++++++++++++++++---- 2 files changed, 196 insertions(+), 26 deletions(-) diff --git a/install/installCyberPanel.py b/install/installCyberPanel.py index 2548f9100..97d2fe8cb 100644 --- a/install/installCyberPanel.py +++ b/install/installCyberPanel.py @@ -284,6 +284,76 @@ class InstallCyberPanel: InstallCyberPanel.stdOut(f"ERROR: {msg}", 1) return False + def verifyCustomBinary(self, binary_path): + """Verify custom binary has correct dependencies and can run""" + try: + InstallCyberPanel.stdOut("Verifying custom binary compatibility...", 1) + + # Check library dependencies + command = f'ldd {binary_path}' + result = subprocess.run(command, shell=True, capture_output=True, text=True) + + if result.returncode != 0: + InstallCyberPanel.stdOut("ERROR: Failed to check binary dependencies", 1) + return False + + # Check for missing libraries + if 'not found' in result.stdout: + InstallCyberPanel.stdOut("ERROR: Binary has missing library dependencies:", 1) + for line in result.stdout.split('\n'): + if 'not found' in line: + InstallCyberPanel.stdOut(f" {line.strip()}", 1) + return False + + # Try to run the binary with -v to check if it can execute + command = f'{binary_path} -v' + result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=5) + + if result.returncode != 0: + InstallCyberPanel.stdOut("ERROR: Binary failed to execute", 1) + if result.stderr: + InstallCyberPanel.stdOut(f" Error: {result.stderr.strip()}", 1) + return False + + InstallCyberPanel.stdOut("Binary verification successful", 1) + return True + + except subprocess.TimeoutExpired: + InstallCyberPanel.stdOut("ERROR: Binary verification timed out", 1) + return False + except Exception as msg: + logging.InstallLog.writeToFile(str(msg) + " [verifyCustomBinary]") + InstallCyberPanel.stdOut(f"ERROR: Verification failed: {msg}", 1) + return False + + def rollbackCustomBinary(self, backup_dir, binary_path, module_path): + """Rollback to original binary if custom binary fails""" + try: + InstallCyberPanel.stdOut("Rolling back to original binary...", 1) + + backup_binary = f"{backup_dir}/openlitespeed.backup" + + # Restore original binary if backup exists + if os.path.exists(backup_binary): + shutil.copy2(backup_binary, binary_path) + os.chmod(binary_path, 0o755) + InstallCyberPanel.stdOut("Original binary restored successfully", 1) + else: + InstallCyberPanel.stdOut("WARNING: No backup found, cannot restore", 1) + + # Remove failed custom module + if os.path.exists(module_path): + os.remove(module_path) + InstallCyberPanel.stdOut("Custom module removed", 1) + + InstallCyberPanel.stdOut("Rollback completed", 1) + return True + + except Exception as msg: + logging.InstallLog.writeToFile(str(msg) + " [rollbackCustomBinary]") + InstallCyberPanel.stdOut(f"ERROR: Rollback failed: {msg}", 1) + return False + def installCustomOLSBinaries(self): """Install custom OpenLiteSpeed binaries with PHP config support""" try: @@ -362,21 +432,36 @@ class InstallCyberPanel: logging.InstallLog.writeToFile(str(e) + " [installCustomOLSBinaries - module install]") return False - # Verify installation - if os.path.exists(OLS_BINARY_PATH) and os.path.exists(MODULE_PATH): - InstallCyberPanel.stdOut("=" * 50, 1) - InstallCyberPanel.stdOut("Custom Binaries Installed Successfully", 1) - InstallCyberPanel.stdOut("Features enabled:", 1) - InstallCyberPanel.stdOut(" - Apache-style .htaccess support", 1) - InstallCyberPanel.stdOut(" - php_value/php_flag directives", 1) - InstallCyberPanel.stdOut(" - Enhanced header control", 1) - InstallCyberPanel.stdOut(f"Backup: {backup_dir}", 1) - InstallCyberPanel.stdOut("=" * 50, 1) - return True - else: - InstallCyberPanel.stdOut("ERROR: Installation verification failed", 1) + # Verify installation files exist + if not (os.path.exists(OLS_BINARY_PATH) and os.path.exists(MODULE_PATH)): + InstallCyberPanel.stdOut("ERROR: Installation verification failed - files not found", 1) return False + # Verify binary compatibility + if not self.verifyCustomBinary(OLS_BINARY_PATH): + InstallCyberPanel.stdOut("ERROR: Custom binary verification failed", 1) + InstallCyberPanel.stdOut("This usually means wrong binary type for your OS", 1) + + # Rollback to original binary + if os.path.exists(backup_dir): + self.rollbackCustomBinary(backup_dir, OLS_BINARY_PATH, MODULE_PATH) + InstallCyberPanel.stdOut("Continuing with standard OLS", 1) + else: + InstallCyberPanel.stdOut("WARNING: Cannot rollback, no backup found", 1) + + return True # Non-fatal, continue with standard OLS + + # Success! + InstallCyberPanel.stdOut("=" * 50, 1) + InstallCyberPanel.stdOut("Custom Binaries Installed Successfully", 1) + InstallCyberPanel.stdOut("Features enabled:", 1) + InstallCyberPanel.stdOut(" - Apache-style .htaccess support", 1) + InstallCyberPanel.stdOut(" - php_value/php_flag directives", 1) + InstallCyberPanel.stdOut(" - Enhanced header control", 1) + InstallCyberPanel.stdOut(f"Backup: {backup_dir}", 1) + InstallCyberPanel.stdOut("=" * 50, 1) + return True + except Exception as msg: logging.InstallLog.writeToFile(str(msg) + " [installCustomOLSBinaries]") InstallCyberPanel.stdOut(f"ERROR: {msg}", 1) diff --git a/plogical/upgrade.py b/plogical/upgrade.py index 2d04505ff..dbfc85f35 100644 --- a/plogical/upgrade.py +++ b/plogical/upgrade.py @@ -700,6 +700,76 @@ class Upgrade: Upgrade.stdOut(f"ERROR: {msg} [downloadCustomBinary]", 0) return False + @staticmethod + def verifyCustomBinary(binary_path): + """Verify custom binary has correct dependencies and can run""" + try: + Upgrade.stdOut("Verifying custom binary compatibility...", 0) + + # Check library dependencies + command = f'ldd {binary_path}' + result = subprocess.run(command, shell=True, capture_output=True, text=True) + + if result.returncode != 0: + Upgrade.stdOut("ERROR: Failed to check binary dependencies", 0) + return False + + # Check for missing libraries + if 'not found' in result.stdout: + Upgrade.stdOut("ERROR: Binary has missing library dependencies:", 0) + for line in result.stdout.split('\n'): + if 'not found' in line: + Upgrade.stdOut(f" {line.strip()}", 0) + return False + + # Try to run the binary with -v to check if it can execute + command = f'{binary_path} -v' + result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=5) + + if result.returncode != 0: + Upgrade.stdOut("ERROR: Binary failed to execute", 0) + if result.stderr: + Upgrade.stdOut(f" Error: {result.stderr.strip()}", 0) + return False + + Upgrade.stdOut("Binary verification successful", 0) + return True + + except subprocess.TimeoutExpired: + Upgrade.stdOut("ERROR: Binary verification timed out", 0) + return False + except Exception as msg: + Upgrade.stdOut(f"ERROR: Verification failed: {msg}", 0) + return False + + @staticmethod + def rollbackCustomBinary(backup_dir, binary_path, module_path): + """Rollback to original binary if custom binary fails""" + try: + Upgrade.stdOut("Rolling back to original binary...", 0) + + backup_binary = f"{backup_dir}/openlitespeed.backup" + + # Restore original binary if backup exists + if os.path.exists(backup_binary): + shutil.copy2(backup_binary, binary_path) + os.chmod(binary_path, 0o755) + Upgrade.stdOut("Original binary restored successfully", 0) + else: + Upgrade.stdOut("WARNING: No backup found, cannot restore", 0) + + # Remove failed custom module + if os.path.exists(module_path): + os.remove(module_path) + Upgrade.stdOut("Custom module removed", 0) + + Upgrade.stdOut("Rollback completed", 0) + return True + + except Exception as msg: + Upgrade.stdOut(f"ERROR: Rollback failed: {msg}", 0) + return False + @staticmethod def installCustomOLSBinaries(): """Install custom OpenLiteSpeed binaries with PHP config support""" @@ -777,21 +847,36 @@ class Upgrade: Upgrade.stdOut(f"ERROR: Failed to install module: {e}", 0) return False - # Verify installation - if os.path.exists(OLS_BINARY_PATH) and os.path.exists(MODULE_PATH): - Upgrade.stdOut("=" * 50, 0) - Upgrade.stdOut("Custom Binaries Installed Successfully", 0) - Upgrade.stdOut("Features enabled:", 0) - Upgrade.stdOut(" - Apache-style .htaccess support", 0) - Upgrade.stdOut(" - php_value/php_flag directives", 0) - Upgrade.stdOut(" - Enhanced header control", 0) - Upgrade.stdOut(f"Backup: {backup_dir}", 0) - Upgrade.stdOut("=" * 50, 0) - return True - else: - Upgrade.stdOut("ERROR: Installation verification failed", 0) + # Verify installation files exist + if not (os.path.exists(OLS_BINARY_PATH) and os.path.exists(MODULE_PATH)): + Upgrade.stdOut("ERROR: Installation verification failed - files not found", 0) return False + # Verify binary compatibility + if not Upgrade.verifyCustomBinary(OLS_BINARY_PATH): + Upgrade.stdOut("ERROR: Custom binary verification failed", 0) + Upgrade.stdOut("This usually means wrong binary type for your OS", 0) + + # Rollback to original binary + if os.path.exists(backup_dir): + Upgrade.rollbackCustomBinary(backup_dir, OLS_BINARY_PATH, MODULE_PATH) + Upgrade.stdOut("Continuing with standard OLS", 0) + else: + Upgrade.stdOut("WARNING: Cannot rollback, no backup found", 0) + + return True # Non-fatal, continue with standard OLS + + # Success! + Upgrade.stdOut("=" * 50, 0) + Upgrade.stdOut("Custom Binaries Installed Successfully", 0) + Upgrade.stdOut("Features enabled:", 0) + Upgrade.stdOut(" - Apache-style .htaccess support", 0) + Upgrade.stdOut(" - php_value/php_flag directives", 0) + Upgrade.stdOut(" - Enhanced header control", 0) + Upgrade.stdOut(f"Backup: {backup_dir}", 0) + Upgrade.stdOut("=" * 50, 0) + return True + except Exception as msg: Upgrade.stdOut(f"ERROR: {msg} [installCustomOLSBinaries]", 0) Upgrade.stdOut("Continuing with standard OLS", 0)