cyberpanel/plogical/installUtilities.py

706 lines
24 KiB
Python

import subprocess
import sys
from plogical import CyberCPLogFileWriter as logging
import shutil
import pexpect
import os
import shlex
from plogical.processUtilities import ProcessUtilities
from datetime import datetime
class installUtilities:
Server_root_path = "/usr/local/lsws"
@staticmethod
def enableEPELRepo():
try:
cmd = []
cmd.append("yum")
cmd.append("-y")
cmd.append("install")
cmd.append("epel-release")
res = subprocess.call(cmd)
if res == 1:
print("###############################################")
print(" Could not add EPEL repo " )
print("###############################################")
else:
print("###############################################")
print(" EPEL Repo Added ")
print("###############################################")
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [enableEPELRepo]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [enableEPELRepo]")
return 0
return 1
@staticmethod
def addLiteSpeedRepo():
try:
# Use the official LiteSpeed repository installation script
# This supports all OS versions including CentOS/AlmaLinux/Rocky 7, 8, and 9
cmd = "wget -O - https://repo.litespeed.sh | bash"
res = subprocess.call(cmd, shell=True)
if res == 1:
print("###############################################")
print(" Could not add Litespeed repo " )
print("###############################################")
else:
print("###############################################")
print(" Litespeed Repo Added ")
print("###############################################")
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [addLiteSpeedRepo]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [addLiteSpeedRepo]")
return 0
return 1
@staticmethod
def installLiteSpeed():
try:
cmd = []
cmd.append("yum")
cmd.append("-y")
cmd.append("install")
cmd.append("openlitespeed")
res = subprocess.call(cmd)
if res == 1:
print("###############################################")
print(" Could not install Litespeed " )
print("###############################################")
sys.exit()
else:
print("###############################################")
print(" Litespeed Installed ")
print("###############################################")
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installLiteSpeed]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installLiteSpeed]")
return 0
return 1
@staticmethod
def startLiteSpeed():
try:
cmd = []
cmd.append("/usr/local/lsws/bin/lswsctrl")
cmd.append("start")
res = subprocess.call(cmd)
if res == 1:
print("###############################################")
print(" Could not start Litespeed server ")
print("###############################################")
sys.exit()
else:
print("###############################################")
print(" Litespeed Started ")
print("###############################################")
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startLiteSpeed]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startLiteSpeed]")
return 0
return 1
@staticmethod
def reStartLiteSpeed():
try:
if ProcessUtilities.decideServer() == ProcessUtilities.OLS:
command = "systemctl restart lsws"
else:
command = "/usr/local/lsws/bin/lswsctrl restart"
ProcessUtilities.normalExecutioner(command)
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]")
return 0
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]")
return 0
return 1
@staticmethod
def reStartLiteSpeedSocket():
try:
if ProcessUtilities.decideServer() == ProcessUtilities.OLS:
command = "sudo systemctl restart lsws"
else:
command = "sudo /usr/local/lsws/bin/lswsctrl restart"
return ProcessUtilities.executioner(command)
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]")
return 0
@staticmethod
def stopLiteSpeedSocket():
try:
if ProcessUtilities.decideServer() == ProcessUtilities.OLS:
command = "sudo systemctl stop lsws"
else:
command = "sudo /usr/local/lsws/bin/lswsctrl stop"
return ProcessUtilities.executioner(command)
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]")
return 0
@staticmethod
def reStartOpenLiteSpeed(restart,orestart):
try:
if ProcessUtilities.decideServer() == ProcessUtilities.OLS:
command = "sudo systemctl restart lsws"
else:
command = "sudo /usr/local/lsws/bin/lswsctrl restart"
cmd = shlex.split(command)
res = subprocess.call(cmd)
if res == 1:
print("###############################################")
print(" Could not restart Litespeed serve ")
print("###############################################")
sys.exit()
else:
print("###############################################")
print(" Litespeed Re-Started ")
print("###############################################")
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartOpenLiteSpeed]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartOpenLiteSpeed]")
return 0
return 1
@staticmethod
def safeModifyHttpdConfig(config_modifier, description="config modification"):
"""
Safely modify httpd_config.conf with backup, validation, and rollback on failure.
Prevents corrupted configs that cause OpenLiteSpeed to fail binding ports 80/443.
Args:
config_modifier: A function that takes file content (list of lines) and returns modified content
description: Description of the modification for logging
Returns:
tuple: (success: bool, error_message: str or None)
Reference: https://github.com/usmannasir/cyberpanel/issues/1609
"""
config_file = "/usr/local/lsws/conf/httpd_config.conf"
if not os.path.exists(config_file):
error_msg = f"Config file not found: {config_file}"
logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}")
return False, error_msg
# Create backup with timestamp
try:
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
backup_file = f"{config_file}.backup-{timestamp}"
shutil.copy2(config_file, backup_file)
logging.writeToFile(f"[safeModifyHttpdConfig] Created backup: {backup_file} for {description}")
except Exception as e:
error_msg = f"Failed to create backup: {str(e)}"
logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}")
return False, error_msg
# Read current config
try:
with open(config_file, 'r') as f:
original_content = f.readlines()
except Exception as e:
error_msg = f"Failed to read config file: {str(e)}"
logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}")
return False, error_msg
# Modify config using callback
try:
modified_content = config_modifier(original_content)
if not isinstance(modified_content, list):
error_msg = "Config modifier must return a list of lines"
logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}")
return False, error_msg
except Exception as e:
error_msg = f"Config modifier function failed: {str(e)}"
logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}")
return False, error_msg
# Write modified config
try:
with open(config_file, 'w') as f:
f.writelines(modified_content)
except Exception as e:
error_msg = f"Failed to write modified config: {str(e)}"
logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}")
# Restore backup
try:
shutil.copy2(backup_file, config_file)
logging.writeToFile(f"[safeModifyHttpdConfig] Restored backup due to write failure")
except:
pass
return False, error_msg
# Validate config using openlitespeed -t
try:
if ProcessUtilities.decideServer() == ProcessUtilities.OLS:
validate_cmd = ['/usr/local/lsws/bin/openlitespeed', '-t', '-f', config_file]
else:
# For LiteSpeed Enterprise, use lswsctrl
validate_cmd = ['/usr/local/lsws/bin/lswsctrl', '-t', '-f', config_file]
result = subprocess.run(validate_cmd, capture_output=True, text=True, timeout=30)
if result.returncode != 0:
error_msg = f"Config validation failed: {result.stderr}"
logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}")
# Restore backup
try:
shutil.copy2(backup_file, config_file)
logging.writeToFile(f"[safeModifyHttpdConfig] Restored backup due to validation failure")
except Exception as restore_error:
logging.writeToFile(f"[safeModifyHttpdConfig] CRITICAL: Failed to restore backup: {str(restore_error)}")
return False, error_msg
except subprocess.TimeoutExpired:
error_msg = "Config validation timed out"
logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}")
# Restore backup
try:
shutil.copy2(backup_file, config_file)
logging.writeToFile(f"[safeModifyHttpdConfig] Restored backup due to validation timeout")
except:
pass
return False, error_msg
except Exception as e:
error_msg = f"Config validation error: {str(e)}"
logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}")
# Restore backup
try:
shutil.copy2(backup_file, config_file)
logging.writeToFile(f"[safeModifyHttpdConfig] Restored backup due to validation error")
except:
pass
return False, error_msg
logging.writeToFile(f"[safeModifyHttpdConfig] Successfully modified and validated config: {description}")
return True, None
@staticmethod
def changePortTo80():
try:
def modify_config(lines):
modified = []
for line in lines:
if "*:8088" in line:
modified.append(line.replace("*:8088", "*:80"))
else:
modified.append(line)
return modified
success, error = installUtilities.safeModifyHttpdConfig(
modify_config,
"Change port from 8088 to 80"
)
if not success:
error_msg = error if error else "Unknown error"
logging.writeToFile(f"[changePortTo80] Failed: {error_msg}")
return 0
return installUtilities.reStartLiteSpeed()
except Exception as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [changePortTo80]")
return 0
@staticmethod
def installAllPHPVersion():
try:
cmd = []
cmd.append("yum")
cmd.append("groupinstall")
cmd.append("lsphp-all")
res = subprocess.call(cmd)
if res == 1:
print("###############################################")
print(" Could not install PHP Binaries ")
print("###############################################")
sys.exit()
else:
print("###############################################")
print(" PHP Binaries installed ")
print("###############################################")
writeDataToFile = open(installUtilities.Server_root_path + "/conf/httpd_config.conf", "a")
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installAllPHPVersion]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installAllPHPVersion]")
return 0
return 1
@staticmethod
def installAllPHPToLitespeed():
try:
path = installUtilities.Server_root_path + "/conf/"
if not os.path.exists(path):
shutil.copytree("phpconfigs",path+"phpconfigs")
php53 = "include phpconfigs/php53.conf\n"
php54 = "include phpconfigs/php54.conf\n"
php55 = "include phpconfigs/php55.conf\n"
php56 = "include phpconfigs/php56.conf\n"
php70 = "include phpconfigs/php70.conf\n"
writeDataToFile = open(path+"httpd_config.conf", 'a')
writeDataToFile.writelines(php53)
writeDataToFile.writelines(php54)
writeDataToFile.writelines(php55)
writeDataToFile.writelines(php56)
writeDataToFile.writelines(php70)
writeDataToFile.close()
except IOError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installAllPHPToLitespeed]")
return 0
return 1
@staticmethod
def installMainWebServer():
if installUtilities.enableEPELRepo() == 1:
if installUtilities.addLiteSpeedRepo() == 1:
if installUtilities.installLiteSpeed() == 1:
if installUtilities.startLiteSpeed() == 1:
if installUtilities.installAllPHPVersion():
if installUtilities.installAllPHPToLitespeed():
return 1
else:
return 0
else:
return 0
else:
return 0
else:
return 0
else:
return 0
else:
return 0
@staticmethod
def removeWebServer():
try:
cmd = []
cmd.append("yum")
cmd.append("-y")
cmd.append("remove")
cmd.append("openlitespeed")
res = subprocess.call(cmd)
if res == 1:
print("###############################################")
print(" Could not remove Litespeed ")
print("###############################################")
sys.exit()
else:
print("###############################################")
print(" Litespeed Removed ")
print("###############################################")
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [removeWebServer]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [removeWebServer]")
return 0
try:
cmd = []
cmd.append("yum")
cmd.append("-y")
cmd.append("remove")
cmd.append("lsphp*")
res = subprocess.call(cmd)
if res == 1:
print("###############################################")
print(" Could not PHP Binaries ")
print("###############################################")
else:
print("###############################################")
print(" PHP Binaries Removed ")
print("###############################################")
sys.exit()
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [removeWebServer]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [removeWebServer]")
return 0
try:
shutil.rmtree(installUtilities.Server_root_path)
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [removeWebServer]")
return 0
return 1
@staticmethod
def startMariaDB():
############## Start mariadb ######################
try:
cmd = []
cmd.append("systemctl")
cmd.append("start")
cmd.append("mariadb")
res = subprocess.call(cmd)
if res == 1:
print("###############################################")
print(" Could not start MariaDB ")
print("###############################################")
sys.exit()
else:
print("###############################################")
print(" MariaDB Started ")
print("###############################################")
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startMariaDB]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startMariaDB]")
return 0
return 1
@staticmethod
def installMySQL(password):
try:
############## Install mariadb ######################
cmd = []
cmd.append("yum")
cmd.append("-y")
cmd.append("install")
cmd.append("mariadb-server")
res = subprocess.call(cmd)
if res == 1:
print("###############################################")
print(" Could not install MariaDB ")
print("###############################################")
sys.exit()
else:
print("###############################################")
print(" MariaDB Installed ")
print("###############################################")
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installMySQL]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installMySQL]")
return 0
############## Start mariadb ######################
installUtilities.startMariaDB()
############## Enable mariadb at system startup ######################
try:
cmd = []
cmd.append("systemctl")
cmd.append("enable")
cmd.append("mariadb")
res = subprocess.call(cmd)
if res == 1:
print("###############################################")
print(" Could not add mariadb to startup ")
print("###############################################")
sys.exit()
else:
print("###############################################")
print(" MariaDB Addded to startup ")
print("###############################################")
except OSError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " Could not add mariadb to startup [installMySQL]")
return 0
except ValueError as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " Could not add mariadb to startup [installMySQL]")
return 0
if installUtilities.secureMysqlInstallation(password) == 1:
return 1
return 0
@staticmethod
def secureMysqlInstallation(password):
try:
expectation = "(enter for none):"
securemysql = pexpect.spawn("mysql_secure_installation",maxread=20000)
securemysql.expect(expectation)
securemysql.sendcontrol('j')
expectation = "password? [Y/n]"
securemysql.expect(expectation)
securemysql.sendline("Y")
expectation = "New password:"
securemysql.expect(expectation)
securemysql.sendline("1qaz@9xvps")
expectation = "new password:"
securemysql.expect(expectation)
securemysql.sendline(password)
expectation = "anonymous users? [Y/n]"
securemysql.expect(expectation)
securemysql.sendline("Y")
expectation = "root login remotely? [Y/n]"
securemysql.expect(expectation)
securemysql.sendline("Y")
expectation = "test database and access to it? [Y/n]"
securemysql.expect(expectation)
securemysql.sendline("Y")
expectation = "Reload privilege tables now? [Y/n]"
securemysql.expect(expectation)
securemysql.sendline("Y")
securemysql.wait()
if (securemysql.before.find("Thanks for using MariaDB!") > -1 or securemysql.after.find("Thanks for using MariaDB!")>-1):
return 1
except pexpect.EOF as msg:
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " Exception EOF [installMySQL]")
print("###########################Before########################################")
print(securemysql.before)
print("###########################After########################################")
print(securemysql.after)
print("########################################################################")
except BaseException as msg:
print("#############################Before#####################################")
print(securemysql.before)
print("############################After######################################")
print(securemysql.after)
print("########################################################################")
logging.CyberCPLogFileWriter.writeToFile(str(msg) + "[installMySQL]")
return 0
#installUtilities.installAllPHPToLitespeed()