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
This commit is contained in:
usmannasir
2025-11-07 13:50:47 +05:00
parent 39b74cb9b7
commit f7fc75b258
2 changed files with 196 additions and 26 deletions

View File

@@ -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)

View File

@@ -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)