Files
CyberPanel/install/install.py

4192 lines
178 KiB
Python
Raw Normal View History

2025-08-01 14:56:30 +05:00
import sys
import subprocess
import shutil
import installLog as logging
import argparse
import os
import shlex
from firewallUtilities import FirewallUtilities
import time
import string
import random
import socket
from os.path import *
from stat import *
import stat
import secrets
import install_utils
VERSION = '2.4'
2025-09-01 13:11:42 +05:00
BUILD = 4
2025-08-01 14:56:30 +05:00
# Using shared char_set from install_utils
char_set = install_utils.char_set
# Using shared function from install_utils
generate_pass = install_utils.generate_pass
# There can not be peace without first a great suffering.
# distros - using from install_utils
centos = install_utils.centos
ubuntu = install_utils.ubuntu
cent8 = install_utils.cent8
openeuler = install_utils.openeuler
2025-09-18 01:58:24 +05:00
debian12 = install_utils.debian12
2025-08-01 14:56:30 +05:00
cent9 = 4 # Not in install_utils yet
CloudLinux8 = 0 # Not in install_utils yet
# Using shared function from install_utils
FetchCloudLinuxAlmaVersionVersion = install_utils.FetchCloudLinuxAlmaVersionVersion
# Using shared function from install_utils
get_distro = install_utils.get_distro
def get_Ubuntu_release():
release = install_utils.get_Ubuntu_release(use_print=False, exit_on_error=True)
if release == -1:
preFlightsChecks.stdOut("Can't find distro release name in /etc/lsb-release - fatal error", 1, 1,
os.EX_UNAVAILABLE)
return release
class preFlightsChecks:
debug = 1
cyberPanelMirror = "mirror.cyberpanel.net/pip"
cdn = 'cyberpanel.sh'
SnappyVersion = '2.38.2'
apt_updated = False # Track if apt update has been run
def install_package(self, package_name, options="", silent=False):
"""Unified package installation across distributions"""
command, shell = install_utils.get_package_install_command(self.distro, package_name, options)
if not silent:
return preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, shell)
else:
return preFlightsChecks.call(command, self.distro, command, command, 0, 0, os.EX_OSERR, shell)
def is_centos_family(self):
"""Check if distro is CentOS, CentOS 8, or OpenEuler"""
return self.distro in [centos, cent8, openeuler]
2025-09-18 01:58:24 +05:00
def is_debian_family(self):
"""Check if distro is Ubuntu or Debian 12"""
return self.distro in [ubuntu, debian12]
2025-09-18 12:30:22 +05:00
def get_service_name(self, service):
"""Get the correct service name for the current distribution"""
service_map = {
'pdns': 'pdns',
'powerdns': 'pdns',
'pure-ftpd': 'pure-ftpd',
'pureftpd': 'pure-ftpd'
2025-09-18 12:30:22 +05:00
}
# Platform-specific service name mapping
if self.is_debian_family():
if service in ['pdns', 'powerdns']:
return 'pdns-server'
elif service in ['pure-ftpd', 'pureftpd']:
return 'pure-ftpd'
elif self.is_centos_family():
if service in ['pdns', 'powerdns']:
return 'pdns'
elif service in ['pure-ftpd', 'pureftpd']:
return 'pure-ftpd'
2025-09-18 12:30:22 +05:00
return service_map.get(service, service)
2025-08-01 14:56:30 +05:00
def manage_service(self, service_name, action="start"):
"""Unified service management with error handling"""
# Check if service exists before trying to manage it
check_command = f'systemctl list-unit-files | grep -q "{service_name}.service"'
result = subprocess.run(check_command, shell=True, capture_output=True)
if result.returncode != 0:
preFlightsChecks.stdOut(f"Service {service_name} not found, skipping {action}", 1)
return 1 # Return success since service doesn't exist
2025-08-01 14:56:30 +05:00
command = f'systemctl {action} {service_name}'
return preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
def remove_package(self, package_name, silent=False):
"""Unified package removal across distributions"""
command, shell = install_utils.get_package_remove_command(self.distro, package_name)
if not silent:
return preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, shell)
else:
return preFlightsChecks.call(command, self.distro, command, command, 0, 0, os.EX_OSERR, shell)
def __init__(self, rootPath, ip, path, cwd, cyberPanelPath, distro, remotemysql=None, mysqlhost=None, mysqldb=None,
mysqluser=None, mysqlpassword=None, mysqlport=None):
self.ipAddr = ip
self.path = path
self.cwd = cwd
self.server_root_path = rootPath
self.cyberPanelPath = cyberPanelPath
self.distro = distro
self.remotemysql = remotemysql
self.mysqlhost = mysqlhost
self.mysqluser = mysqluser
self.mysqlpassword = mysqlpassword
self.mysqlport = mysqlport
self.mysqldb = mysqldb
def installQuota(self,):
try:
if self.is_centos_family():
self.install_package("quota", silent=True)
if self.edit_fstab('/', '/') == 0:
preFlightsChecks.stdOut("Quotas will not be abled as we failed to modify fstab file.")
return 0
command = 'mount -o remount /'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'mount -o remount /'
try:
mResult = subprocess.run(command, capture_output=True,universal_newlines=True, shell=True)
except:
mResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True)
if mResult.returncode != 0:
fstab_path = '/etc/fstab'
backup_path = fstab_path + '.bak'
if os.path.exists(fstab_path):
os.remove(fstab_path)
shutil.copy(backup_path, fstab_path)
preFlightsChecks.stdOut("Re-mount failed, restoring original FSTab and existing quota setup.")
return 0
##
if self.distro == ubuntu:
self.stdOut("Install Quota on Ubuntu")
# Skip apt update as it was already done in cyberpanel.sh
self.install_package("quota", silent=True)
command = "find /lib/modules/ -type f -name '*quota_v*.ko*'"
if subprocess.check_output(command,shell=True).decode("utf-8").find("quota/") == -1:
self.install_package("linux-image-extra-virtual", silent=True)
if self.edit_fstab('/', '/') == 0:
preFlightsChecks.stdOut("Quotas will not be abled as we are are failed to modify fstab file.")
return 0
command = 'mount -o remount /'
try:
mResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True)
except:
mResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True, shell=True)
if mResult.returncode != 0:
fstab_path = '/etc/fstab'
backup_path = fstab_path + '.bak'
if os.path.exists(fstab_path):
os.remove(fstab_path)
shutil.copy(backup_path, fstab_path)
preFlightsChecks.stdOut("Re-mount failed, restoring original FSTab and existing quota setup.")
return 0
command = 'quotacheck -ugm /'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
####
command = "find /lib/modules/ -type f -name '*quota_v*.ko*'"
try:
iResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True)
except:
iResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True, shell=True)
print(repr(iResult.stdout))
# Only if the first command works, run the rest
if iResult.returncode == 0:
command = "echo '{}' | sed -n 's|/lib/modules/\\([^/]*\\)/.*|\\1|p' | sort -u".format(iResult.stdout)
try:
result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True)
except:
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True)
fResult = result.stdout.rstrip('\n')
print(repr(result.stdout.rstrip('\n')))
command = 'uname -r'
try:
ffResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True)
except:
ffResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True)
ffResult = ffResult.stdout.rstrip('\n')
command = f"DEBIAN_FRONTEND=noninteractive apt-get install linux-modules-extra-{ffResult}"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, True)
###
command = f'modprobe quota_v1 -S {ffResult}'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = f'modprobe quota_v2 -S {ffResult}'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = f'quotacheck -ugm /'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = f'quotaon -v /'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
except BaseException as msg:
logging.InstallLog.writeToFile("[ERROR] installQuota. " + str(msg))
def edit_fstab(self,mount_point, options_to_add):
try:
retValue = 1
# Backup the original fstab file
fstab_path = '/etc/fstab'
backup_path = fstab_path + '.bak'
rData = open(fstab_path, 'r').read()
if rData.find('xfs') > -1:
options_to_add = 'uquota'
else:
options_to_add = 'usrquota,grpquota'
if not os.path.exists(backup_path):
shutil.copy(fstab_path, backup_path)
# Read the fstab file
with open(fstab_path, 'r') as file:
lines = file.readlines()
# Modify the appropriate line
WriteToFile = open(fstab_path, 'w')
for i, line in enumerate(lines):
if line.find('\t') > -1:
parts = line.split('\t')
else:
parts = line.split(' ')
print(parts)
try:
if parts[1] == '/' and parts[3].find(options_to_add) == -1 and len(parts[3]) > 4:
parts[3] = f'{parts[3]},{options_to_add}'
tempParts = [item for item in parts if item.strip()]
finalString = '\t'.join(tempParts)
print(finalString)
WriteToFile.write(finalString)
elif parts[1] == '/':
for ii, p in enumerate(parts):
if p.find('defaults') > -1 or p.find('discard') > -1 or p.find('errors=') > -1:
parts[ii] = f'{parts[ii]},{options_to_add}'
tempParts = [item for item in parts if item.strip()]
finalString = '\t'.join(tempParts)
print(finalString)
WriteToFile.write(finalString)
else:
WriteToFile.write(line)
except:
WriteToFile.write(line)
WriteToFile.close()
return retValue
except:
return 0
@staticmethod
def stdOut(message, log=0, do_exit=0, code=os.EX_OK):
install_utils.stdOut(message, log, do_exit, code)
def mountTemp(self):
try:
try:
result = subprocess.run('systemd-detect-virt', capture_output=True, universal_newlines=True, shell=True)
except:
result = subprocess.run('systemd-detect-virt', stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True)
if result.stdout.find('openvz') > -1:
if self.distro == ubuntu:
self.install_package("inetutils-inetd")
# ## On OpenVZ there is an issue using .tempdisk for /tmp as it breaks network on container after reboot.
#
# if subprocess.check_output('systemd-detect-virt').decode("utf-8").find("openvz") > -1:
#
# varTmp = "/var/tmp /tmp none bind 0 0\n"
#
# fstab = "/etc/fstab"
# writeToFile = open(fstab, "a")
# writeToFile.writelines(varTmp)
# writeToFile.close()
#
# else:
#
# command = "dd if=/dev/zero of=/usr/.tempdisk bs=100M count=15"
# preFlightsChecks.call(command, self.distro, command,
# command,
# 1, 0, os.EX_OSERR)
#
# command = "mkfs.ext4 -F /usr/.tempdisk"
# preFlightsChecks.call(command, self.distro, command,
# command,
# 1, 0, os.EX_OSERR)
#
# command = "mkdir -p /usr/.tmpbak/"
# preFlightsChecks.call(command, self.distro, command,
# command,
# 1, 0, os.EX_OSERR)
#
# command = "cp -pr /tmp/* /usr/.tmpbak/"
# subprocess.call(command, shell=True)
#
# command = "mount -o loop,rw,nodev,nosuid,noexec,nofail /usr/.tempdisk /tmp"
# preFlightsChecks.call(command, self.distro, command,
# command,
# 1, 0, os.EX_OSERR)
#
# command = "chmod 1777 /tmp"
# preFlightsChecks.call(command, self.distro, command,
# command,
# 1, 0, os.EX_OSERR)
#
# command = "cp -pr /usr/.tmpbak/* /tmp/"
# subprocess.call(command, shell=True)
#
# command = "rm -rf /usr/.tmpbak"
# preFlightsChecks.call(command, self.distro, command,
# command,
# 1, 0, os.EX_OSERR)
#
# command = "mount --bind /tmp /var/tmp"
# preFlightsChecks.call(command, self.distro, command,
# command,
# 1, 0, os.EX_OSERR)
#
# tmp = "/usr/.tempdisk /tmp ext4 loop,rw,noexec,nosuid,nodev,nofail 0 0\n"
# varTmp = "/tmp /var/tmp none bind 0 0\n"
#
# fstab = "/etc/fstab"
# writeToFile = open(fstab, "a")
# writeToFile.writelines(tmp)
# writeToFile.writelines(varTmp)
# writeToFile.close()
pass
except BaseException as msg:
preFlightsChecks.stdOut('[ERROR] ' + str(msg))
return 0
@staticmethod
def pureFTPDServiceName(distro):
if distro == ubuntu:
return 'pure-ftpd-mysql'
return 'pure-ftpd'
# Using shared function from install_utils
@staticmethod
def resFailed(distro, res):
return install_utils.resFailed(distro, res)
# Using shared function from install_utils
@staticmethod
def call(command, distro, bracket, message, log=0, do_exit=0, code=os.EX_OK, shell=False):
return install_utils.call(command, distro, bracket, message, log, do_exit, code, shell)
def checkIfSeLinuxDisabled(self):
try:
command = "sestatus"
output = subprocess.check_output(shlex.split(command)).decode("utf-8")
if output.find("disabled") > -1 or output.find("permissive") > -1:
logging.InstallLog.writeToFile("SELinux Check OK. [checkIfSeLinuxDisabled]")
preFlightsChecks.stdOut("SELinux Check OK.")
return 1
else:
logging.InstallLog.writeToFile(
"SELinux is enabled, please disable SELinux and restart the installation!")
preFlightsChecks.stdOut("Installation failed, consult: /var/log/installLogs.txt")
os._exit(0)
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + "[checkIfSeLinuxDisabled]")
logging.InstallLog.writeToFile('[ERROR] ' + "SELinux Check OK. [checkIfSeLinuxDisabled]")
preFlightsChecks.stdOut('[ERROR] ' + "SELinux Check OK.")
return 1
def checkPythonVersion(self):
if sys.version_info[0] == 3:
return 1
else:
preFlightsChecks.stdOut("You are running Unsupported python version, please install python 3.x")
os._exit(0)
def setup_account_cyberpanel(self):
try:
if self.is_centos_family():
self.install_package("sudo", silent=True)
##
if self.distro == ubuntu:
self.stdOut("Add Cyberpanel user")
command = 'adduser --disabled-login --gecos "" cyberpanel'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
else:
command = "useradd -s /bin/false cyberpanel"
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
###############################
### Docker User/group
2025-09-18 23:20:05 +05:00
if self.distro == ubuntu or self.distro == debian12:
2025-08-01 14:56:30 +05:00
command = 'adduser --disabled-login --gecos "" docker'
else:
2025-09-18 23:20:05 +05:00
# For CentOS/RHEL, use useradd which is non-interactive
command = "useradd -r -s /bin/false docker"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'groupadd docker'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'usermod -aG docker docker'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'usermod -aG docker cyberpanel'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
###
command = "mkdir -p /etc/letsencrypt/live/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
except BaseException as msg:
logging.InstallLog.writeToFile("[ERROR] setup_account_cyberpanel. " + str(msg))
def installCyberPanelRepo(self):
self.stdOut("Install Cyberpanel repo")
if self.distro == ubuntu:
try:
2025-09-18 01:15:38 +05:00
# Use the new LiteSpeed repository setup method
2025-09-18 01:29:38 +05:00
command = "bash -c 'wget -O - https://repo.litespeed.sh | bash'"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
except:
logging.InstallLog.writeToFile("[ERROR] Exception during CyberPanel install")
preFlightsChecks.stdOut("[ERROR] Exception during CyberPanel install")
os._exit(os.EX_SOFTWARE)
2025-09-18 01:58:24 +05:00
elif self.distro == debian12:
try:
# Use the official LiteSpeed repository setup method for Debian 12
command = "bash -c 'wget -O - https://repo.litespeed.sh | bash'"
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
except:
logging.InstallLog.writeToFile("[ERROR] Exception during CyberPanel install - Debian 12 repository setup")
preFlightsChecks.stdOut("[ERROR] Exception during CyberPanel install - Debian 12 repository setup")
os._exit(os.EX_SOFTWARE)
2025-08-01 14:56:30 +05:00
elif self.distro == centos:
command = 'rpm -ivh http://rpms.litespeedtech.com/centos/litespeed-repo-1.2-1.el7.noarch.rpm'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
elif self.distro == cent8:
command = 'rpm -Uvh http://rpms.litespeedtech.com/centos/litespeed-repo-1.1-1.el8.noarch.rpm'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
def fix_selinux_issue(self):
try:
cmd = []
cmd.append("setsebool")
cmd.append("-P")
cmd.append("httpd_can_network_connect")
cmd.append("1")
res = subprocess.call(cmd)
if preFlightsChecks.resFailed(self.distro, res):
logging.InstallLog.writeToFile("fix_selinux_issue problem")
else:
pass
except:
logging.InstallLog.writeToFile("[ERROR] fix_selinux_issue problem")
def install_psmisc(self):
self.stdOut("Install psmisc")
self.install_package("psmisc")
def generate_secure_env_file(self, mysql_root_password, cyberpanel_db_password):
"""
Generate secure .env file with random passwords during installation
"""
try:
import sys
import socket
# Import the environment generator
sys.path.append(os.path.join(self.cyberPanelPath, 'install'))
from env_generator import create_env_file, create_env_backup
# Generate secure credentials
credentials = create_env_file(
self.cyberPanelPath,
mysql_root_password,
cyberpanel_db_password
)
# Create backup for recovery
create_env_backup(self.cyberPanelPath, credentials)
logging.InstallLog.writeToFile("✓ Secure .env file generated successfully")
logging.InstallLog.writeToFile("✓ Credentials backup created for recovery")
return credentials
except Exception as e:
logging.InstallLog.writeToFile(f"[ERROR] Failed to generate secure environment file: {str(e)}")
# Fallback to original method if environment generation fails
self.fallback_settings_update(mysql_root_password, cyberpanel_db_password)
def fallback_settings_update(self, mysqlPassword, password):
"""
Fallback method to update settings.py directly if environment generation fails
"""
logging.InstallLog.writeToFile("Using fallback method for settings.py update")
path = self.cyberPanelPath + "/CyberCP/settings.py"
data = open(path, "r").readlines()
writeDataToFile = open(path, "w")
counter = 0
for items in data:
if items.find('SECRET_KEY') > -1:
SK = "SECRET_KEY = '%s'\n" % (generate_pass(50))
writeDataToFile.writelines(SK)
continue
if items.find("'PASSWORD':") > -1:
if counter == 0:
writeDataToFile.writelines(" 'PASSWORD': '" + mysqlPassword + "'," + "\n")
counter = counter + 1
else:
writeDataToFile.writelines(" 'PASSWORD': '" + password + "'," + "\n")
elif items.find('127.0.0.1') > -1:
writeDataToFile.writelines(" 'HOST': 'localhost',\n")
elif items.find("'PORT':'3307'") > -1:
writeDataToFile.writelines(" 'PORT': '',\n")
else:
writeDataToFile.writelines(items)
if self.distro == ubuntu:
os.fchmod(writeDataToFile.fileno(), stat.S_IRUSR | stat.S_IWUSR)
writeDataToFile.close()
2025-08-01 14:56:30 +05:00
def download_install_CyberPanel(self, mysqlPassword, mysql):
##
os.chdir(self.path)
os.chdir('/usr/local')
2025-09-21 00:21:07 +05:00
# Since cyberpanel.sh has already cloned the repository and we're running from it,
# we simply need to clone a fresh copy to /usr/local/CyberCP for the application
logging.InstallLog.writeToFile("Setting up CyberPanel application directory...")
# Remove existing CyberCP directory if it exists
if os.path.exists('/usr/local/CyberCP'):
logging.InstallLog.writeToFile("Removing existing CyberCP directory...")
shutil.rmtree('/usr/local/CyberCP')
# Clone directly to /usr/local/CyberCP with explicit path
logging.InstallLog.writeToFile("Cloning repository to /usr/local/CyberCP...")
# Ensure the parent directory exists
os.makedirs("/usr/local", exist_ok=True)
# Try multiple clone methods for better reliability
clone_commands = [
"git clone https://github.com/usmannasir/cyberpanel /usr/local/CyberCP",
"git clone --depth 1 https://github.com/usmannasir/cyberpanel /usr/local/CyberCP",
"git clone --single-branch --branch stable https://github.com/usmannasir/cyberpanel /usr/local/CyberCP"
]
clone_success = False
for cmd in clone_commands:
try:
result = preFlightsChecks.call(cmd, self.distro, cmd, cmd, 1, 1, os.EX_OSERR)
if result == 1 and os.path.exists('/usr/local/CyberCP'):
clone_success = True
break
except:
continue
if not clone_success or not os.path.exists('/usr/local/CyberCP'):
logging.InstallLog.writeToFile("[ERROR] All Git clone attempts failed!")
preFlightsChecks.stdOut("[ERROR] All Git clone attempts failed!")
# Try manual download as fallback
logging.InstallLog.writeToFile("Attempting manual download as fallback...")
command = "wget -O /tmp/cyberpanel.zip https://github.com/usmannasir/cyberpanel/archive/refs/heads/stable.zip"
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = "unzip /tmp/cyberpanel.zip -d /usr/local/"
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = "mv /usr/local/cyberpanel-stable /usr/local/CyberCP"
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = "rm -f /tmp/cyberpanel.zip"
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
if not os.path.exists('/usr/local/CyberCP'):
logging.InstallLog.writeToFile("[ERROR] Manual download also failed!")
preFlightsChecks.stdOut("[ERROR] Manual download also failed!")
sys.exit(1)
2025-09-20 20:21:18 +05:00
2025-09-21 00:21:07 +05:00
logging.InstallLog.writeToFile("Successfully cloned repository to /usr/local/CyberCP")
2025-08-01 14:56:30 +05:00
##
### update password:
if self.remotemysql == 'OFF':
passFile = "/etc/cyberpanel/mysqlPassword"
f = open(passFile)
data = f.read()
password = data.split('\n', 1)[0]
else:
password = self.mysqlpassword
### Put correct mysql passwords in settings file!
# This allows root/sudo users to be able to work with MySQL/MariaDB without hunting down the password like
# all the other control panels allow
# reference: https://oracle-base.com/articles/mysql/mysql-password-less-logins-using-option-files
mysql_my_root_cnf = '/root/.my.cnf'
mysql_root_cnf_content = """
[client]
user=root
password="%s"
""" % password
with open(mysql_my_root_cnf, 'w') as f:
f.write(mysql_root_cnf_content)
os.chmod(mysql_my_root_cnf, 0o600)
command = 'chown root:root %s' % mysql_my_root_cnf
subprocess.call(shlex.split(command))
logging.InstallLog.writeToFile("Updating /root/.my.cnf!")
logging.InstallLog.writeToFile("Generating secure environment configuration!")
2025-08-01 14:56:30 +05:00
2025-09-21 00:21:07 +05:00
# For CentOS, we need to get the actual cyberpanel database password
# which is different from the root password
if self.distro == centos:
# On CentOS, InstallCyberPanel.mysqlPassword is different from root password
# We need to import and use it directly
import installCyberPanel
cyberpanel_db_password = installCyberPanel.InstallCyberPanel.mysqlPassword
else:
# On Ubuntu/Debian, the cyberpanel password is the same as root password
cyberpanel_db_password = password
# Generate secure environment file with correct passwords
self.generate_secure_env_file(mysqlPassword, cyberpanel_db_password)
2025-08-01 14:56:30 +05:00
logging.InstallLog.writeToFile("Environment configuration generated successfully!")
2025-08-01 14:56:30 +05:00
if self.remotemysql == 'ON':
2025-09-20 20:21:18 +05:00
path = self.cyberPanelPath + "/CyberCP/settings.py"
2025-08-01 14:56:30 +05:00
command = "sed -i 's|localhost|%s|g' %s" % (self.mysqlhost, path)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
# command = "sed -i 's|'mysql'|'%s'|g' %s" % (self.mysqldb, path)
# preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = "sed -i 's|root|%s|g' %s" % (self.mysqluser, path)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = "sed -i \"s|'PORT': ''|'PORT':'%s'|g\" %s" % (self.mysqlport, path)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
logging.InstallLog.writeToFile("settings.py updated!")
# self.setupVirtualEnv(self.distro)
2025-09-21 00:37:02 +05:00
# Now run Django migrations since we're in /usr/local/CyberCP and database exists
os.chdir("/usr/local/CyberCP")
logging.InstallLog.writeToFile("Running Django migrations...")
preFlightsChecks.stdOut("Running Django migrations...")
2025-09-21 13:04:51 +05:00
# Clean any existing migration files first (except __init__.py)
logging.InstallLog.writeToFile("Cleaning existing migration files...")
command = "find /usr/local/CyberCP -path '*/migrations/0*.py' -delete 2>/dev/null || true"
2025-09-21 01:07:38 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-09-21 13:04:51 +05:00
# Clean any existing migration pyc files
command = "find /usr/local/CyberCP -path '*/migrations/*.pyc' -delete 2>/dev/null || true"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Clean __pycache__ directories in migrations folders
command = "find /usr/local/CyberCP -path '*/migrations/__pycache__' -type d -exec rm -rf {} + 2>/dev/null || true"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Fix baseTemplate migrations - ensure required migration files exist
logging.InstallLog.writeToFile("Fixing baseTemplate migrations...")
self.fixBaseTemplateMigrations()
# Ensure virtual environment is properly set up
logging.InstallLog.writeToFile("Ensuring virtual environment is properly set up...")
if not self.ensureVirtualEnvironmentSetup():
logging.InstallLog.writeToFile("ERROR: Virtual environment setup failed!", 0)
preFlightsChecks.stdOut("ERROR: Virtual environment setup failed!", 0)
return False
# Find the correct Python virtual environment path
python_paths = [
"/usr/local/CyberPanel/bin/python",
"/usr/local/CyberCP/bin/python",
"/usr/local/CyberPanel-venv/bin/python"
]
python_path = None
for path in python_paths:
if os.path.exists(path):
python_path = path
logging.InstallLog.writeToFile(f"Found Python virtual environment at: {path}")
break
if not python_path:
logging.InstallLog.writeToFile("ERROR: No Python virtual environment found!", 0)
preFlightsChecks.stdOut("ERROR: No Python virtual environment found!", 0)
return False
2025-09-21 13:04:51 +05:00
# Create all migrations at once - Django will handle dependencies
logging.InstallLog.writeToFile("Creating fresh migrations for all apps...")
command = f"{python_path} manage.py makemigrations --noinput"
2025-09-21 00:37:02 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
2025-09-21 13:04:51 +05:00
# Apply all migrations
logging.InstallLog.writeToFile("Applying all migrations...")
command = f"{python_path} manage.py migrate --noinput"
2025-09-21 00:37:02 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
logging.InstallLog.writeToFile("Django migrations completed successfully!")
preFlightsChecks.stdOut("Django migrations completed successfully!")
2025-08-01 14:56:30 +05:00
if not os.path.exists("/usr/local/CyberCP/public"):
os.mkdir("/usr/local/CyberCP/public")
command = f"{python_path} manage.py collectstatic --noinput --clear"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
## Moving static content to lscpd location
command = 'mv static /usr/local/CyberCP/public/'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
try:
path = "/usr/local/CyberCP/version.txt"
writeToFile = open(path, 'w')
writeToFile.writelines('%s\n' % (VERSION))
writeToFile.writelines(str(BUILD))
writeToFile.close()
except:
pass
def fixBaseTemplateMigrations(self):
"""
Fix baseTemplate migrations to prevent NodeNotFoundError on AlmaLinux 9 and Ubuntu 24
"""
try:
# Ensure baseTemplate migrations directory exists
migrations_dir = "/usr/local/CyberCP/baseTemplate/migrations"
if not os.path.exists(migrations_dir):
os.makedirs(migrations_dir)
logging.InstallLog.writeToFile("Created baseTemplate migrations directory")
# Create __init__.py if it doesn't exist
init_file = os.path.join(migrations_dir, "__init__.py")
if not os.path.exists(init_file):
with open(init_file, 'w') as f:
f.write("")
logging.InstallLog.writeToFile("Created baseTemplate migrations __init__.py")
# Create 0001_initial.py if it doesn't exist
initial_migration = os.path.join(migrations_dir, "0001_initial.py")
if not os.path.exists(initial_migration):
initial_content = '''# Generated by Django 3.2.25 on 2024-01-01 00:00
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='CyberPanelCosmetic',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('MainDashboardCSS', models.TextField(default='')),
],
),
migrations.CreateModel(
name='UserNotificationPreferences',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('backup_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the backup notification')),
('ai_scanner_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the AI scanner notification')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'User Notification Preferences',
'verbose_name_plural': 'User Notification Preferences',
},
),
migrations.CreateModel(
name='version',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('version', models.CharField(max_length=10)),
('build', models.IntegerField()),
],
),
]
'''
with open(initial_migration, 'w') as f:
f.write(initial_content)
logging.InstallLog.writeToFile("Created baseTemplate 0001_initial.py migration")
# Create 0002_usernotificationpreferences.py if it doesn't exist
notification_migration = os.path.join(migrations_dir, "0002_usernotificationpreferences.py")
if not os.path.exists(notification_migration):
notification_content = '''# Generated by Django 3.2.25 on 2024-01-01 00:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('baseTemplate', '0001_initial'),
('loginSystem', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='usernotificationpreferences',
name='user',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='notification_preferences', to='loginSystem.administrator'),
),
]
'''
with open(notification_migration, 'w') as f:
f.write(notification_content)
logging.InstallLog.writeToFile("Created baseTemplate 0002_usernotificationpreferences.py migration")
# Set proper permissions
command = "chown -R root:root " + migrations_dir
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chmod -R 755 " + migrations_dir
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
logging.InstallLog.writeToFile("baseTemplate migrations fixed successfully")
preFlightsChecks.stdOut("baseTemplate migrations fixed successfully")
except Exception as e:
logging.InstallLog.writeToFile("Error fixing baseTemplate migrations: " + str(e))
preFlightsChecks.stdOut("Warning: Could not fix baseTemplate migrations: " + str(e))
2025-08-01 14:56:30 +05:00
def fixCyberPanelPermissions(self):
###### fix Core CyberPanel permissions
command = "usermod -G lscpd,lsadm,nobody lscpd"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "usermod -G lscpd,lsadm,nogroup lscpd"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = r"find /usr/local/CyberCP -type d -exec chmod 0755 {} \;"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = r"find /usr/local/CyberCP -type f -exec chmod 0644 {} \;"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chmod -R 755 /usr/local/CyberCP/bin"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
## change owner
command = "chown -R root:root /usr/local/CyberCP"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
########### Fix LSCPD
command = r"find /usr/local/lscp -type d -exec chmod 0755 {} \;"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = r"find /usr/local/lscp -type f -exec chmod 0644 {} \;"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chmod -R 755 /usr/local/lscp/bin"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chmod -R 755 /usr/local/lscp/fcgi-bin"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/tmp"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
## change owner
command = "chown -R root:root /usr/local/lscp"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/rainloop"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chmod 700 /usr/local/CyberCP/cli/cyberPanel.py"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chmod 700 /usr/local/CyberCP/plogical/upgradeCritical.py"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chmod 755 /usr/local/CyberCP/postfixSenderPolicy/client.py"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chmod 640 /usr/local/CyberCP/CyberCP/settings.py"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chown root:cyberpanel /usr/local/CyberCP/CyberCP/settings.py"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
files = ['/etc/yum.repos.d/MariaDB.repo', '/etc/pdns/pdns.conf', '/etc/systemd/system/lscpd.service',
'/etc/pure-ftpd/pure-ftpd.conf', '/etc/pure-ftpd/pureftpd-pgsql.conf',
'/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/pureftpd-ldap.conf',
'/etc/dovecot/dovecot.conf', '/usr/local/lsws/conf/httpd_config.xml',
'/usr/local/lsws/conf/modsec.conf', '/usr/local/lsws/conf/httpd.conf']
for items in files:
command = 'chmod 644 %s' % (items)
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
impFile = ['/etc/pure-ftpd/pure-ftpd.conf', '/etc/pure-ftpd/pureftpd-pgsql.conf',
'/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/pureftpd-ldap.conf',
'/etc/dovecot/dovecot.conf', '/etc/pdns/pdns.conf', '/etc/pure-ftpd/db/mysql.conf',
'/etc/powerdns/pdns.conf']
for items in impFile:
command = 'chmod 600 %s' % (items)
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'chmod 640 /etc/postfix/*.cf'
subprocess.call(command, shell=True)
command = 'chmod 644 /etc/postfix/main.cf'
subprocess.call(command, shell=True)
command = 'chmod 640 /etc/dovecot/*.conf'
subprocess.call(command, shell=True)
command = 'chmod 644 /etc/dovecot/dovecot.conf'
subprocess.call(command, shell=True)
command = 'chmod 640 /etc/dovecot/dovecot-sql.conf.ext'
subprocess.call(command, shell=True)
command = 'chmod 644 /etc/postfix/dynamicmaps.cf'
subprocess.call(command, shell=True)
fileM = ['/usr/local/lsws/FileManager/', '/usr/local/CyberCP/install/FileManager',
'/usr/local/CyberCP/serverStatus/litespeed/FileManager', '/usr/local/lsws/Example/html/FileManager']
for items in fileM:
try:
shutil.rmtree(items)
except:
pass
command = 'chmod 755 /etc/pure-ftpd/'
subprocess.call(command, shell=True)
command = 'chmod +x /usr/local/CyberCP/plogical/renew.py'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'chmod +x /usr/local/CyberCP/CLManager/CLPackages.py'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
clScripts = ['/usr/local/CyberCP/CLScript/panel_info.py', '/usr/local/CyberCP/CLScript/CloudLinuxPackages.py',
'/usr/local/CyberCP/CLScript/CloudLinuxUsers.py',
'/usr/local/CyberCP/CLScript/CloudLinuxDomains.py',
'/usr/local/CyberCP/CLScript/CloudLinuxResellers.py', '/usr/local/CyberCP/CLScript/CloudLinuxAdmins.py',
'/usr/local/CyberCP/CLScript/CloudLinuxDB.py', '/usr/local/CyberCP/CLScript/UserInfo.py']
for items in clScripts:
command = 'chmod +x %s' % (items)
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'chmod 600 /usr/local/CyberCP/plogical/adminPass.py'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'chmod 600 /etc/cagefs/exclude/cyberpanelexclude'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "find /usr/local/CyberCP/ -name '*.pyc' -delete"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
if self.is_centos_family():
command = 'chown root:pdns /etc/pdns/pdns.conf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'chmod 640 /etc/pdns/pdns.conf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
else:
command = 'chown root:pdns /etc/powerdns/pdns.conf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'chmod 640 /etc/powerdns/pdns.conf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'chmod 640 /usr/local/lscp/cyberpanel/logs/access.log'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-31 12:32:58 +05:00
# Create complete SnappyMail directory structure early in installation
2025-08-30 23:40:20 +05:00
command = 'mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/configs/'
2025-08-31 12:32:58 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/domains/'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/storage/'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/temp/'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/cache/'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Set proper ownership early
command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/snappymail/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-31 13:16:22 +05:00
# Set proper permissions - make all data directories group writable
command = "chmod -R 775 /usr/local/lscp/cyberpanel/snappymail/data/"
2025-08-31 12:32:58 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-31 13:16:22 +05:00
# Ensure the web server user (nobody) can access the directories
# Note: lscpd is already added to nobody group earlier in the installation
command = "usermod -a -G lscpd nobody 2>/dev/null || true"
2025-08-31 12:32:58 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-01 14:56:30 +05:00
2025-08-31 19:55:56 +05:00
# Fix SnappyMail public directory ownership early
command = "chown -R lscpd:lscpd /usr/local/CyberCP/public/snappymail/data || true"
2025-08-31 19:55:56 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-01 14:56:30 +05:00
snappymailinipath = '/usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/configs/application.ini'
command = 'chmod 600 /usr/local/CyberCP/public/snappymail.php'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
###
WriteToFile = open('/etc/fstab', 'a')
WriteToFile.write('proc /proc proc defaults,hidepid=2 0 0\n')
WriteToFile.close()
command = 'mount -o remount,rw,hidepid=2 /proc'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
## symlink protection
writeToFile = open('/usr/lib/sysctl.d/50-default.conf', 'a')
writeToFile.writelines('fs.protected_hardlinks = 1\n')
writeToFile.writelines('fs.protected_symlinks = 1\n')
writeToFile.close()
command = 'sysctl --system'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'chmod 700 %s' % ('/home/cyberpanel')
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
destPrivKey = "/usr/local/lscp/conf/key.pem"
command = 'chmod 600 %s' % (destPrivKey)
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
###
def install_unzip(self):
self.stdOut("Install unzip")
try:
self.install_package("unzip")
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_unzip]")
def install_zip(self):
self.stdOut("Install zip")
try:
self.install_package("zip")
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_zip]")
def download_install_phpmyadmin(self):
try:
if not os.path.exists("/usr/local/CyberCP/public"):
os.mkdir("/usr/local/CyberCP/public")
command = 'wget -O /usr/local/CyberCP/public/phpmyadmin.zip https://github.com/usmannasir/cyberpanel/raw/stable/phpmyadmin.zip'
preFlightsChecks.call(command, self.distro, '[download_install_phpmyadmin]',
command, 1, 0, os.EX_OSERR)
command = 'unzip /usr/local/CyberCP/public/phpmyadmin.zip -d /usr/local/CyberCP/public'
preFlightsChecks.call(command, self.distro, '[download_install_phpmyadmin]',
command, 1, 0, os.EX_OSERR)
command = 'mv /usr/local/CyberCP/public/phpMyAdmin-*-all-languages /usr/local/CyberCP/public/phpmyadmin'
subprocess.call(command, shell=True)
command = 'rm -f /usr/local/CyberCP/public/phpmyadmin.zip'
preFlightsChecks.call(command, self.distro, '[download_install_phpmyadmin]',
command, 1, 0, os.EX_OSERR)
## Write secret phrase
rString = install_utils.generate_random_string(32)
data = open('/usr/local/CyberCP/public/phpmyadmin/config.sample.inc.php', 'r').readlines()
writeToFile = open('/usr/local/CyberCP/public/phpmyadmin/config.inc.php', 'w')
writeE = 1
phpMyAdminContent = """
$cfg['Servers'][$i]['AllowNoPassword'] = false;
$cfg['Servers'][$i]['auth_type'] = 'signon';
$cfg['Servers'][$i]['SignonSession'] = 'SignonSession';
$cfg['Servers'][$i]['SignonURL'] = 'phpmyadminsignin.php';
$cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout';
"""
for items in data:
if items.find('blowfish_secret') > -1:
writeToFile.writelines(
"$cfg['blowfish_secret'] = '" + rString + "'; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */\n")
elif items.find('/* Authentication type */') > -1:
writeToFile.writelines(items)
writeToFile.write(phpMyAdminContent)
writeE = 0
elif items.find("$cfg['Servers'][$i]['AllowNoPassword']") > -1:
writeE = 1
else:
if writeE:
writeToFile.writelines(items)
writeToFile.writelines("$cfg['TempDir'] = '/usr/local/CyberCP/public/phpmyadmin/tmp';\n")
writeToFile.close()
os.mkdir('/usr/local/CyberCP/public/phpmyadmin/tmp')
command = 'chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin'
preFlightsChecks.call(command, self.distro, '[chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin]',
'chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin', 1, 0, os.EX_OSERR)
if self.remotemysql == 'ON':
command = "sed -i 's|'localhost'|'%s'|g' %s" % (
self.mysqlhost, '/usr/local/CyberCP/public/phpmyadmin/config.inc.php')
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'cp /usr/local/CyberCP/plogical/phpmyadminsignin.php /usr/local/CyberCP/public/phpmyadmin/phpmyadminsignin.php'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
if self.remotemysql == 'ON':
command = "sed -i 's|localhost|%s|g' /usr/local/CyberCP/public/phpmyadmin/phpmyadminsignin.php" % (
self.mysqlhost)
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [download_install_phpmyadmin]")
return 0
###################################################### Email setup
def install_postfix_dovecot(self):
self.stdOut("Install dovecot - first remove postfix")
try:
if self.distro == centos:
self.remove_package("postfix")
elif self.distro == ubuntu:
self.remove_package("postfix")
self.stdOut("Install dovecot - do the install")
if self.distro == centos:
command = 'yum install --enablerepo=gf-plus -y postfix3 postfix3-ldap postfix3-mysql postfix3-pcre'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
elif self.distro == cent8:
clAPVersion = FetchCloudLinuxAlmaVersionVersion()
type = clAPVersion.split('-')[0]
version = int(clAPVersion.split('-')[1])
if type == 'al' and version >= 90:
2025-08-19 18:36:48 +05:00
command = 'dnf --nogpg install -y https://mirror.ghettoforge.net/distributions/gf/gf-release-latest.gf.el9.noarch.rpm'
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
else:
2025-08-19 18:36:48 +05:00
command = 'dnf --nogpg install -y https://mirror.ghettoforge.net/distributions/gf/gf-release-latest.gf.el8.noarch.rpm'
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'dnf install --enablerepo=gf-plus postfix3 postfix3-mysql -y'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
elif self.distro == openeuler:
command = 'dnf install postfix -y'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
else:
self.install_package("debconf-utils", silent=True)
file_name = self.cwd + '/pf.unattend.text'
pf = open(file_name, 'w')
pf.write('postfix postfix/mailname string ' + str(socket.getfqdn() + '\n'))
pf.write('postfix postfix/main_mailer_type string "Internet Site"\n')
pf.close()
command = 'debconf-set-selections ' + file_name
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install postfix postfix-mysql'
# os.remove(file_name)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True)
##
# Remove conflicting dovecot packages first
try:
if self.distro == centos:
# CentOS 7 (Legacy - EOL) - use yum
preFlightsChecks.call('yum remove -y dovecot dovecot-*', self.distro,
'Remove conflicting dovecot packages',
'Remove conflicting dovecot packages', 1, 0, os.EX_OSERR)
elif self.distro in [cent8, openeuler]:
# CentOS 8, AlmaLinux 8/9/10, RockyLinux 8/9, RHEL 8/9, CloudLinux 8/9 - use dnf
preFlightsChecks.call('dnf remove -y dovecot dovecot-*', self.distro,
'Remove conflicting dovecot packages',
'Remove conflicting dovecot packages', 1, 0, os.EX_OSERR)
else:
# Ubuntu 24.04/22.04/20.04, Debian 13/12/11 - use apt
preFlightsChecks.call('apt-get remove -y dovecot dovecot-*', self.distro,
'Remove conflicting dovecot packages',
'Remove conflicting dovecot packages', 1, 0, os.EX_OSERR)
except:
pass # Continue if removal fails
2025-08-01 14:56:30 +05:00
if self.distro == centos:
# CentOS 7 (Legacy - EOL)
command = 'yum --enablerepo=gf-plus -y install dovecot23 dovecot23-mysql --allowerasing'
2025-08-01 14:56:30 +05:00
elif self.distro == cent8:
# CentOS 8, AlmaLinux 8, RockyLinux 8, RHEL 8, CloudLinux 8
command = 'dnf install --enablerepo=gf-plus dovecot23 dovecot23-mysql -y --allowerasing'
2025-08-01 14:56:30 +05:00
elif self.distro == openeuler:
# AlmaLinux 9/10, RockyLinux 9, RHEL 9, CloudLinux 9, and other modern RHEL-based systems
dovecot_commands = [
'dnf install dovecot dovecot-mysql -y --skip-broken --nobest',
'dnf install dovecot23 dovecot23-mysql -y --skip-broken --nobest',
'dnf install dovecot -y --skip-broken --nobest'
]
dovecot_installed = False
for cmd in dovecot_commands:
try:
preFlightsChecks.call(cmd, self.distro, cmd, cmd, 1, 1, os.EX_OSERR, True)
if os.path.exists('/etc/dovecot') or os.path.exists('/usr/sbin/dovecot'):
dovecot_installed = True
break
except:
continue
if not dovecot_installed:
command = 'dnf install dovecot -y --skip-broken --nobest --allowerasing'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True)
2025-08-01 14:56:30 +05:00
else:
# Ubuntu 24.04/22.04/20.04, Debian 13/12/11
2025-08-01 14:56:30 +05:00
command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install dovecot-mysql dovecot-imapd dovecot-pop3d'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True)
# Ensure Dovecot service is properly configured
self.manage_service('dovecot', 'enable')
self.manage_service('dovecot', 'start')
# Verify Dovecot installation
if os.path.exists('/usr/sbin/dovecot') or os.path.exists('/usr/bin/dovecot'):
logging.InstallLog.writeToFile("Dovecot installation successful")
else:
logging.InstallLog.writeToFile("[WARNING] Dovecot binary not found after installation")
2025-08-01 14:56:30 +05:00
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_postfix_dovecot]")
return 0
return 1
def setup_email_Passwords(self, mysqlPassword, mysql):
try:
logging.InstallLog.writeToFile("Setting up authentication for Postfix and Dovecot...")
os.chdir(self.cwd)
mysql_virtual_domains = "email-configs-one/mysql-virtual_domains.cf"
mysql_virtual_forwardings = "email-configs-one/mysql-virtual_forwardings.cf"
mysql_virtual_mailboxes = "email-configs-one/mysql-virtual_mailboxes.cf"
mysql_virtual_email2email = "email-configs-one/mysql-virtual_email2email.cf"
dovecotmysql = "email-configs-one/dovecot-sql.conf.ext"
### update password:
data = open(dovecotmysql, "r").readlines()
writeDataToFile = open(dovecotmysql, "w")
if mysql == 'Two':
dataWritten = "connect = host=127.0.0.1 dbname=cyberpanel user=cyberpanel password=" + mysqlPassword + " port=3307\n"
else:
dataWritten = "connect = host=localhost dbname=cyberpanel user=cyberpanel password=" + mysqlPassword + " port=3306\n"
for items in data:
if items.find("connect") > -1:
writeDataToFile.writelines(dataWritten)
else:
writeDataToFile.writelines(items)
writeDataToFile.close()
### update password:
data = open(mysql_virtual_domains, "r").readlines()
writeDataToFile = open(mysql_virtual_domains, "w")
dataWritten = "password = " + mysqlPassword + "\n"
for items in data:
if items.find("password") > -1:
writeDataToFile.writelines(dataWritten)
else:
writeDataToFile.writelines(items)
writeDataToFile.close()
### update password:
data = open(mysql_virtual_forwardings, "r").readlines()
writeDataToFile = open(mysql_virtual_forwardings, "w")
dataWritten = "password = " + mysqlPassword + "\n"
for items in data:
if items.find("password") > -1:
writeDataToFile.writelines(dataWritten)
else:
writeDataToFile.writelines(items)
writeDataToFile.close()
### update password:
data = open(mysql_virtual_mailboxes, "r").readlines()
writeDataToFile = open(mysql_virtual_mailboxes, "w")
dataWritten = "password = " + mysqlPassword + "\n"
for items in data:
if items.find("password") > -1:
writeDataToFile.writelines(dataWritten)
else:
writeDataToFile.writelines(items)
writeDataToFile.close()
### update password:
data = open(mysql_virtual_email2email, "r").readlines()
writeDataToFile = open(mysql_virtual_email2email, "w")
dataWritten = "password = " + mysqlPassword + "\n"
for items in data:
if items.find("password") > -1:
writeDataToFile.writelines(dataWritten)
else:
writeDataToFile.writelines(items)
writeDataToFile.close()
if self.remotemysql == 'ON':
command = "sed -i 's|host=localhost|host=%s|g' %s" % (self.mysqlhost, dovecotmysql)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = "sed -i 's|port=3306|port=%s|g' %s" % (self.mysqlport, dovecotmysql)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
##
command = "sed -i 's|localhost|%s:%s|g' %s" % (self.mysqlhost, self.mysqlport, mysql_virtual_domains)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = "sed -i 's|localhost|%s:%s|g' %s" % (
self.mysqlhost, self.mysqlport, mysql_virtual_forwardings)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = "sed -i 's|localhost|%s:%s|g' %s" % (
self.mysqlhost, self.mysqlport, mysql_virtual_mailboxes)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = "sed -i 's|localhost|%s:%s|g' %s" % (
self.mysqlhost, self.mysqlport, mysql_virtual_email2email)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
logging.InstallLog.writeToFile("Authentication for Postfix and Dovecot set.")
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR]' + str(msg) + " [setup_email_Passwords]")
return 0
return 1
def centos_lib_dir_to_ubuntu(self, filename, old, new):
try:
fd = open(filename, 'r')
lines = fd.readlines()
fd.close()
fd = open(filename, 'w')
centos_prefix = old
ubuntu_prefix = new
for line in lines:
index = line.find(centos_prefix)
if index != -1:
line = line[:index] + ubuntu_prefix + line[index + len(centos_prefix):]
fd.write(line)
fd.close()
except IOError as err:
self.stdOut(
"[ERROR] Error converting: " + filename + " from centos defaults to ubuntu defaults: " + str(err), 1,
1, os.EX_OSERR)
def setup_postfix_dovecot_config(self, mysql):
try:
logging.InstallLog.writeToFile("Configuring postfix and dovecot...")
os.chdir(self.cwd)
mysql_virtual_domains = "/etc/postfix/mysql-virtual_domains.cf"
mysql_virtual_forwardings = "/etc/postfix/mysql-virtual_forwardings.cf"
mysql_virtual_mailboxes = "/etc/postfix/mysql-virtual_mailboxes.cf"
mysql_virtual_email2email = "/etc/postfix/mysql-virtual_email2email.cf"
main = "/etc/postfix/main.cf"
master = "/etc/postfix/master.cf"
dovecot = "/etc/dovecot/dovecot.conf"
dovecotmysql = "/etc/dovecot/dovecot-sql.conf.ext"
# Ensure dovecot directory exists
os.makedirs("/etc/dovecot", exist_ok=True)
# Also ensure dovecot conf.d directory exists
os.makedirs("/etc/dovecot/conf.d", exist_ok=True)
# Check if Dovecot is installed before proceeding
if not os.path.exists('/usr/sbin/dovecot') and not os.path.exists('/usr/bin/dovecot'):
logging.InstallLog.writeToFile("[ERROR] Dovecot not installed, cannot configure")
return 0
2025-08-01 14:56:30 +05:00
if os.path.exists(mysql_virtual_domains):
os.remove(mysql_virtual_domains)
if os.path.exists(mysql_virtual_forwardings):
os.remove(mysql_virtual_forwardings)
if os.path.exists(mysql_virtual_mailboxes):
os.remove(mysql_virtual_mailboxes)
if os.path.exists(mysql_virtual_email2email):
os.remove(mysql_virtual_email2email)
if os.path.exists(main):
os.remove(main)
if os.path.exists(master):
os.remove(master)
if os.path.exists(dovecot):
os.remove(dovecot)
if os.path.exists(dovecotmysql):
os.remove(dovecotmysql)
###############Getting SSL
command = 'openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout /etc/postfix/key.pem -out /etc/postfix/cert.pem'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout /etc/dovecot/key.pem -out /etc/dovecot/cert.pem'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Cleanup config files for ubuntu
if self.distro == ubuntu:
preFlightsChecks.stdOut("Cleanup postfix/dovecot config files", 1)
self.centos_lib_dir_to_ubuntu("email-configs-one/master.cf", "/usr/libexec/", "/usr/lib/")
self.centos_lib_dir_to_ubuntu("email-configs-one/main.cf", "/usr/libexec/postfix",
"/usr/lib/postfix/sbin")
########### Copy config files
shutil.copy("email-configs-one/mysql-virtual_domains.cf", "/etc/postfix/mysql-virtual_domains.cf")
shutil.copy("email-configs-one/mysql-virtual_forwardings.cf",
"/etc/postfix/mysql-virtual_forwardings.cf")
shutil.copy("email-configs-one/mysql-virtual_mailboxes.cf", "/etc/postfix/mysql-virtual_mailboxes.cf")
shutil.copy("email-configs-one/mysql-virtual_email2email.cf",
"/etc/postfix/mysql-virtual_email2email.cf")
shutil.copy("email-configs-one/main.cf", main)
shutil.copy("email-configs-one/master.cf", master)
# Copy Dovecot configuration files with fallback
try:
shutil.copy("email-configs-one/dovecot.conf", dovecot)
shutil.copy("email-configs-one/dovecot-sql.conf.ext", dovecotmysql)
except FileNotFoundError:
# Fallback: create basic dovecot.conf if template not found
logging.InstallLog.writeToFile("[WARNING] Dovecot config templates not found, creating basic configuration")
# Create basic dovecot.conf
with open(dovecot, 'w') as f:
f.write('''protocols = imap pop3
log_timestamp = "%Y-%m-%d %H:%M:%S "
ssl_cert = <cert.pem
ssl_key = <key.pem
mail_plugins = zlib
namespace {
type = private
separator = .
prefix = INBOX.
inbox = yes
}
service auth {
unix_listener auth-master {
mode = 0600
user = vmail
}
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}
user = root
}
service auth-worker {
user = root
}
protocol lda {
log_path = /home/vmail/dovecot-deliver.log
auth_socket_path = /var/run/dovecot/auth-master
postmaster_address = postmaster@example.com
mail_plugins = zlib
}
protocol pop3 {
pop3_uidl_format = %08Xu%08Xv
mail_plugins = $mail_plugins zlib
}
protocol imap {
mail_plugins = $mail_plugins zlib imap_zlib
}
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
plugin {
zlib_save = gz
zlib_save_level = 6
}
service stats {
unix_listener stats-reader {
user = vmail
group = vmail
mode = 0660
}
unix_listener stats-writer {
user = vmail
group = vmail
mode = 0660
}
}
''')
# Create basic dovecot-sql.conf.ext
with open(dovecotmysql, 'w') as f:
f.write(f'''# Database driver: mysql, pgsql, sqlite
driver = mysql
# Database connection string
connect = host=localhost dbname=cyberpanel user=cyberpanel password={self.mysqlPassword}
# Default password scheme
default_pass_scheme = MD5-CRYPT
# SQL query to get password
password_query = SELECT email as user, password FROM mail_users WHERE email='%u';
# SQL query to get user info
user_query = SELECT email as user, password, 'vmail' as uid, 'vmail' as gid, '/home/vmail/%d/%n' as home FROM mail_users WHERE email='%u';
''')
2025-08-01 14:56:30 +05:00
########### Set custom settings
# We are going to leverage postconfig -e to edit the settings for hostname
command = "postconf -e 'myhostname = %s'" % (str(socket.getfqdn()))
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# We are explicitly going to use sed to set the hostname default from "myhostname = server.example.com"
# to the fqdn from socket if the default is still found
command = "sed -i 's|server.example.com|%s|g' %s" % (str(socket.getfqdn()), main)
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
######################################## Permissions
command = 'chmod o= /etc/postfix/mysql-virtual_domains.cf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'chmod o= /etc/postfix/mysql-virtual_forwardings.cf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'chmod o= /etc/postfix/mysql-virtual_mailboxes.cf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'chmod o= /etc/postfix/mysql-virtual_email2email.cf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'chmod o= ' + main
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'chmod o= ' + master
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
#######################################
command = 'chgrp postfix /etc/postfix/mysql-virtual_domains.cf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'chgrp postfix /etc/postfix/mysql-virtual_forwardings.cf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'chgrp postfix /etc/postfix/mysql-virtual_mailboxes.cf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'chgrp postfix /etc/postfix/mysql-virtual_email2email.cf'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'chgrp postfix ' + main
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'chgrp postfix ' + master
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
######################################## users and groups
command = 'groupadd -g 5000 vmail'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'useradd -g vmail -u 5000 vmail -d /home/vmail -m'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
######################################## Further configurations
# hostname = socket.gethostname()
################################### Restart postix
self.manage_service('postfix', 'enable')
self.manage_service('postfix', 'start')
######################################## Permissions
command = 'chgrp dovecot /etc/dovecot/dovecot-sql.conf.ext'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'chmod o= /etc/dovecot/dovecot-sql.conf.ext'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
################################### Restart dovecot
self.manage_service('dovecot', 'enable')
self.manage_service('dovecot', 'start')
# Verify Dovecot service is running
if self.manage_service('dovecot', 'status') == 0:
logging.InstallLog.writeToFile("Dovecot service started successfully")
else:
logging.InstallLog.writeToFile("[WARNING] Dovecot service may not be running properly")
2025-08-01 14:56:30 +05:00
##
self.manage_service('postfix', 'restart')
## chaging permissions for main.cf
command = "chmod 755 " + main
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
if self.distro == ubuntu:
command = "mkdir -p /etc/pki/dovecot/private/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "mkdir -p /etc/pki/dovecot/certs/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "mkdir -p /etc/opendkim/keys/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "sed -i 's/auth_mechanisms = plain/#auth_mechanisms = plain/g' /etc/dovecot/conf.d/10-auth.conf"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
## Ubuntu 18.10 ssl_dh for dovecot 2.3.2.1
if get_Ubuntu_release() == 18.10:
dovecotConf = '/etc/dovecot/dovecot.conf'
data = open(dovecotConf, 'r').readlines()
writeToFile = open(dovecotConf, 'w')
for items in data:
if items.find('ssl_key = <key.pem') > -1:
writeToFile.writelines(items)
writeToFile.writelines('ssl_dh = </usr/share/dovecot/dh.pem\n')
else:
writeToFile.writelines(items)
writeToFile.close()
self.manage_service('dovecot', 'restart')
logging.InstallLog.writeToFile("Postfix and Dovecot configured")
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setup_postfix_dovecot_config]")
return 0
return 1
def downoad_and_install_raindloop(self):
try:
#######
if not os.path.exists("/usr/local/CyberCP/public"):
os.mkdir("/usr/local/CyberCP/public")
if os.path.exists("/usr/local/CyberCP/public/snappymail"):
return 0
os.chdir("/usr/local/CyberCP/public")
command = 'wget https://github.com/the-djmaze/snappymail/releases/download/v%s/snappymail-%s.zip' % (preFlightsChecks.SnappyVersion, preFlightsChecks.SnappyVersion)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
#############
command = 'unzip snappymail-%s.zip -d /usr/local/CyberCP/public/snappymail' % (preFlightsChecks.SnappyVersion)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
try:
os.remove("snappymail-%s.zip" % (preFlightsChecks.SnappyVersion))
except:
pass
#######
os.chdir("/usr/local/CyberCP/public/snappymail")
command = r'find . -type d -exec chmod 755 {} \;'
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
#############
command = r'find . -type f -exec chmod 644 {} \;'
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
######
2025-08-30 23:40:20 +05:00
# Create SnappyMail data directories with proper structure
command = "mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/configs/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/domains/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/storage/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/temp/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/cache/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Set proper ownership for SnappyMail data directories
command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/snappymail/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-31 13:16:22 +05:00
# Set proper permissions for SnappyMail data directories (group writable)
command = "chmod -R 775 /usr/local/lscp/cyberpanel/snappymail/data/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Ensure web server users are in the lscpd group for access
command = "usermod -a -G lscpd nobody 2>/dev/null || true"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-31 19:55:56 +05:00
# Fix SnappyMail public directory ownership immediately after creation
command = "chown -R lscpd:lscpd /usr/local/CyberCP/public/snappymail/data || true"
2025-08-30 23:40:20 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-01 14:56:30 +05:00
command = "mkdir -p /usr/local/lscp/cyberpanel/rainloop/data"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
### Enable sub-folders
command = "mkdir -p /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/configs/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# labsPath = '/usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/configs/application.ini'
#
# labsData = """[labs]
# imap_folder_list_limit = 0
# autocreate_system_folders = On
# """
#
# # writeToFile = open(labsPath, 'a')
# # writeToFile.write(labsData)
# # writeToFile.close()
#
# iPath = os.listdir('/usr/local/CyberCP/public/snappymail/snappymail/v/')
#
# path = "/usr/local/CyberCP/public/snappymail/snappymail/v/%s/include.php" % (iPath[0])
#
# data = open(path, 'r').readlines()
# writeToFile = open(path, 'w')
#
# for items in data:
# if items.find("$sCustomDataPath = '';") > -1:
# writeToFile.writelines(
# " $sCustomDataPath = '/usr/local/lscp/cyberpanel/rainloop/data';\n")
# else:
# writeToFile.writelines(items)
#
# writeToFile.close()
#
# includeFileOldPath = '/usr/local/CyberCP/public/snappymail/_include.php'
# includeFileNewPath = '/usr/local/CyberCP/public/snappymail/include.php'
#
# if os.path.exists(includeFileOldPath):
# writeToFile = open(includeFileOldPath, 'a')
# writeToFile.write("\ndefine('APP_DATA_FOLDER_PATH', '/usr/local/lscp/cyberpanel/rainloop/data/');\n")
# writeToFile.close()
#
# command = 'mv %s %s' % (includeFileOldPath, includeFileNewPath)
# preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
#
# #command = "sed -i 's|autocreate_system_folders = Off|autocreate_system_folders = On|g' %s" % (labsPath)
# command = "sed -i 's|verify_certificate = On|verify_certificate = Off|g' %s" % (labsPath)
# preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
#
# ### now download and install actual plugin
#
# command = f'mkdir /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect'
# preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
#
# command = f'chmod 700 /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect'
# preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
#
# command = f'chmod 700 /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect'
# preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
#
# command = f'wget -O /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect/index.php https://raw.githubusercontent.com/the-djmaze/snappymail/master/plugins/mailbox-detect/index.php'
# preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
#
# command = f'chmod 644 /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect/index.php'
# preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
#
# command = f'chown lscpd:lscpd /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect/index.php'
# preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
#
# ### Enable plugins and enable mailbox creation plugin
#
# labsDataLines = open(labsPath, 'r').readlines()
# PluginsActivator = 0
# WriteToFile = open(labsPath, 'w')
# for lines in labsDataLines:
# if lines.find('[plugins]') > -1:
# PluginsActivator = 1
# WriteToFile.write(lines)
# elif PluginsActivator and lines.find('enable = ') > -1:
# WriteToFile.write(f'enable = On\n')
# elif PluginsActivator and lines.find('enabled_list = ') > -1:
# WriteToFile.write(f'enabled_list = "mailbox-detect"\n')
# elif PluginsActivator == 1 and lines.find('[defaults]') > -1:
# PluginsActivator = 0
# WriteToFile.write(lines)
# else:
# WriteToFile.write(lines)
# WriteToFile.close()
#
# ## enable auto create in the enabled plugin
# PluginsFilePath = '/usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/configs/plugin-mailbox-detect.json'
#
# WriteToFile = open(PluginsFilePath, 'w')
# WriteToFile.write("""{
# "plugin": {
# "autocreate_system_folders": true
# }
# }
# """)
# WriteToFile.close()
#
# command = f'chown lscpd:lscpd {PluginsFilePath}'
# preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
#
# command = f'chmod 600 {PluginsFilePath}'
# preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = f'wget -O /usr/local/CyberCP/snappymail_cyberpanel.php https://raw.githubusercontent.com/the-djmaze/snappymail/master/integrations/cyberpanel/install.php'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = f'/usr/local/lsws/lsphp80/bin/php /usr/local/CyberCP/snappymail_cyberpanel.php'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [downoad_and_install_snappymail]")
return 0
return 1
###################################################### Email setup ends!
def reStartLiteSpeed(self):
command = install_utils.format_restart_litespeed_command(self.server_root_path)
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
def removeUfw(self):
try:
preFlightsChecks.stdOut("Checking to see if ufw firewall is installed (will be removed)", 1)
status = subprocess.check_output(shlex.split('ufw status')).decode("utf-8")
preFlightsChecks.stdOut("ufw current status: " + status + "...will be removed")
except BaseException as msg:
preFlightsChecks.stdOut("[ERROR] Expected access to ufw not available, do not need to remove it", 1)
return True
try:
preFlightsChecks.call('DEBIAN_FRONTEND=noninteractive apt-get -y remove ufw', self.distro, '[remove_ufw]', 'Remove ufw firewall ' +
'(using firewalld)', 1, 0, os.EX_OSERR, True)
except:
pass
return True
def findSSHPort(self):
try:
sshData = subprocess.check_output(shlex.split('cat /etc/ssh/sshd_config')).decode("utf-8").split('\n')
for items in sshData:
if items.find('Port') > -1:
if items[0] == 0:
pass
else:
return items.split(' ')[1]
return '22'
except BaseException as msg:
return '22'
def installFirewalld(self):
2025-09-18 14:20:01 +05:00
if self.distro == ubuntu or self.distro == debian12:
2025-08-01 14:56:30 +05:00
self.removeUfw()
try:
preFlightsChecks.stdOut("Enabling Firewall!")
self.install_package("firewalld")
######
if self.distro == centos:
2025-09-18 14:20:01 +05:00
# Not available in ubuntu/debian
2025-08-01 14:56:30 +05:00
self.manage_service('dbus', 'restart')
2025-09-18 14:20:01 +05:00
elif self.distro == debian12:
# For Debian 12, ensure dbus is running for firewalld
self.manage_service('dbus', 'start')
self.manage_service('dbus', 'enable')
2025-08-01 14:56:30 +05:00
2025-09-18 14:20:01 +05:00
# Restart systemd-logind on all systems
2025-08-01 14:56:30 +05:00
self.manage_service('systemd-logind', 'restart')
self.manage_service('firewalld', 'start')
self.manage_service('firewalld', 'enable')
FirewallUtilities.addRule("tcp", "8090")
FirewallUtilities.addRule("tcp", "7080")
FirewallUtilities.addRule("tcp", "80")
FirewallUtilities.addRule("tcp", "443")
FirewallUtilities.addRule("tcp", "21")
FirewallUtilities.addRule("tcp", "25")
FirewallUtilities.addRule("tcp", "587")
FirewallUtilities.addRule("tcp", "465")
FirewallUtilities.addRule("tcp", "110")
FirewallUtilities.addRule("tcp", "143")
FirewallUtilities.addRule("tcp", "993")
FirewallUtilities.addRule("tcp", "995")
FirewallUtilities.addRule("udp", "53")
FirewallUtilities.addRule("tcp", "53")
FirewallUtilities.addRule("tcp", "8888")
FirewallUtilities.addRule("udp", "443")
FirewallUtilities.addRule("tcp", "40110-40210")
try:
SSHPort = self.findSSHPort()
if SSHPort != '22':
FirewallUtilities.addRule('tcp', SSHPort)
except BaseException as msg:
logging.InstallLog.writeToFile(f'[Error Custom SSH port] {str(msg)}')
preFlightsChecks.stdOut(f'[Error Custom SSH port] {str(msg)}')
logging.InstallLog.writeToFile("FirewallD installed and configured!")
preFlightsChecks.stdOut("FirewallD installed and configured!")
except OSError as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installFirewalld]")
return 0
except ValueError as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installFirewalld]")
return 0
return 1
## from here
def installLSCPD(self):
try:
logging.InstallLog.writeToFile("Starting LSCPD installation..")
os.chdir(self.cwd)
if self.distro == ubuntu:
self.install_package("gcc g++ make autoconf rcs")
else:
self.install_package("gcc gcc-c++ make autoconf glibc")
if self.distro == ubuntu:
self.install_package("libpcre3 libpcre3-dev openssl libexpat1 libexpat1-dev libgeoip-dev zlib1g zlib1g-dev libudns-dev whichman curl")
else:
self.install_package("pcre-devel openssl-devel expat-devel geoip-devel zlib-devel udns-devel")
command = 'tar zxf lscp.tar.gz -C /usr/local/'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
###
lscpdPath = '/usr/local/lscp/bin/lscpd'
# if subprocess.check_output('uname -a').decode("utf-8").find("aarch64") == -1:
# lscpdPath = '/usr/local/lscp/bin/lscpd'
#
# lscpdSelection = 'lscpd-0.3.1'
# if os.path.exists('/etc/lsb-release'):
# result = open('/etc/lsb-release', 'r').read()
2025-08-29 20:13:28 +05:00
# if result.find('22.04') > -1 or result.find('24.04') > -1:
2025-08-01 14:56:30 +05:00
# lscpdSelection = 'lscpd.0.4.0'
# else:
# lscpdSelection = 'lscpd.aarch64'
try:
try:
result = subprocess.run('uname -a', capture_output=True, universal_newlines=True, shell=True)
except:
result = subprocess.run('uname -a', stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True)
if result.stdout.find('aarch64') == -1:
lscpdSelection = 'lscpd-0.3.1'
if os.path.exists('/etc/lsb-release'):
result = open('/etc/lsb-release', 'r').read()
2025-08-29 20:13:28 +05:00
if result.find('22.04') > -1 or result.find('24.04') > -1:
2025-08-01 14:56:30 +05:00
lscpdSelection = 'lscpd.0.4.0'
else:
lscpdSelection = 'lscpd.aarch64'
except:
lscpdSelection = 'lscpd-0.3.1'
if os.path.exists('/etc/lsb-release'):
result = open('/etc/lsb-release', 'r').read()
2025-08-29 20:13:28 +05:00
if result.find('22.04') > -1 or result.find('24.04') > -1:
2025-08-01 14:56:30 +05:00
lscpdSelection = 'lscpd.0.4.0'
command = f'cp -f /usr/local/CyberCP/{lscpdSelection} /usr/local/lscp/bin/{lscpdSelection}'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = 'rm -f /usr/local/lscp/bin/lscpd'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = f'mv /usr/local/lscp/bin/{lscpdSelection} /usr/local/lscp/bin/lscpd'
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
command = 'chmod 755 %s' % (lscpdPath)
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
##
command = 'openssl req -newkey rsa:1024 -new -nodes -x509 -days 3650 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout /usr/local/lscp/conf/key.pem -out /usr/local/lscp/conf/cert.pem'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Create lsphp symlink for fcgi-bin with better error handling
self.setup_lsphp_symlink()
2025-08-01 14:56:30 +05:00
if self.is_centos_family():
command = 'adduser lscpd -M -d /usr/local/lscp'
else:
command = 'useradd lscpd -M -d /usr/local/lscp'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
if self.is_centos_family():
command = 'groupadd lscpd'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Added group in useradd for Ubuntu
command = 'usermod -a -G lscpd lscpd'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'usermod -a -G lsadm lscpd'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
try:
os.mkdir('/usr/local/lscp/cyberpanel')
except:
pass
try:
os.mkdir('/usr/local/lscp/cyberpanel/logs')
except:
pass
# self.setupComodoRules()
logging.InstallLog.writeToFile("LSCPD successfully installed!")
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installLSCPD]")
def setupComodoRules(self):
try:
os.chdir(self.cwd)
extractLocation = "/usr/local/lscp/modsec"
command = "mkdir -p /usr/local/lscp/modsec"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
try:
if os.path.exists('comodo.tar.gz'):
os.remove('comodo.tar.gz')
except:
pass
command = "wget https://cyberpanel.net/modsec/comodo.tar.gz"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "tar -zxf comodo.tar.gz -C /usr/local/lscp/modsec"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
###
modsecConfPath = "/usr/local/lscp/conf/modsec.conf"
modsecConfig = """
module mod_security {
ls_enabled 0
modsecurity on
modsecurity_rules `
SecDebugLogLevel 0
SecDebugLog /usr/local/lscp/logs/modsec.log
SecAuditEngine on
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts AFH
SecAuditLogType Serial
SecAuditLog /usr/local/lscp/logs/auditmodsec.log
SecRuleEngine Off
`
modsecurity_rules_file /usr/local/lscp/modsec/comodo/modsecurity.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/00_Init_Initialization.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/01_Init_AppsInitialization.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/02_Global_Generic.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/03_Global_Agents.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/04_Global_Domains.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/05_Global_Backdoor.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/06_XSS_XSS.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/07_Global_Other.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/08_Bruteforce_Bruteforce.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/09_HTTP_HTTP.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/10_HTTP_HTTPDoS.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/11_HTTP_Protocol.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/12_HTTP_Request.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/13_Outgoing_FilterGen.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/14_Outgoing_FilterASP.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/15_Outgoing_FilterPHP.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/16_Outgoing_FilterSQL.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/17_Outgoing_FilterOther.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/18_Outgoing_FilterInFrame.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/19_Outgoing_FiltersEnd.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/20_PHP_PHPGen.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/21_SQL_SQLi.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/22_Apps_Joomla.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/23_Apps_JComponent.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/24_Apps_WordPress.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/25_Apps_WPPlugin.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/26_Apps_WHMCS.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/27_Apps_Drupal.conf
modsecurity_rules_file /usr/local/lscp/modsec/comodo/28_Apps_OtherApps.conf
}
"""
writeToFile = open(modsecConfPath, 'w')
writeToFile.write(modsecConfig)
writeToFile.close()
###
command = "chown -R lscpd:lscpd /usr/local/lscp/modsec"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
return 1
except BaseException as msg:
logging.InstallLog.writeToFile("[ERROR]" + str(msg))
return 0
def setupPort(self):
try:
###
bindConfPath = "/usr/local/lscp/conf/bind.conf"
writeToFile = open(bindConfPath, 'w')
writeToFile.write("*:" + self.port)
writeToFile.close()
except:
return 0
def setupPythonWSGI(self):
try:
preFlightsChecks.stdOut("Setting up Python WSGI-LSAPI with optimized compilation...", 1)
# Ensure virtual environment is properly set up
self.ensureVirtualEnvironmentSetup()
# Upgrade pip to latest version for better package compatibility
self.upgradePip()
# Determine the correct Python path
python_paths = [
"/usr/local/CyberPanel/bin/python",
"/usr/local/CyberCP/bin/python",
"/usr/bin/python3",
"/usr/local/bin/python3"
]
python_path = None
for path in python_paths:
if os.path.exists(path):
python_path = path
preFlightsChecks.stdOut(f"Using Python at: {python_path}", 1)
break
if not python_path:
preFlightsChecks.stdOut("ERROR: No Python executable found for WSGI setup", 0)
preFlightsChecks.stdOut("Attempting to create virtual environment symlink...", 1)
# Try to create symlink for compatibility
if os.path.exists('/usr/local/CyberCP/bin/python') and not os.path.exists('/usr/local/CyberPanel'):
try:
os.symlink('/usr/local/CyberCP', '/usr/local/CyberPanel')
python_path = "/usr/local/CyberPanel/bin/python"
preFlightsChecks.stdOut(f"Created symlink, using Python at: {python_path}", 1)
except Exception as e:
preFlightsChecks.stdOut(f"Failed to create symlink: {str(e)}", 0)
return 0
else:
return 0
2025-08-01 14:56:30 +05:00
command = "wget http://www.litespeedtech.com/packages/lsapi/wsgi-lsapi-2.1.tgz"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "tar xf wsgi-lsapi-2.1.tgz"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
os.chdir("wsgi-lsapi-2.1")
command = f"{python_path} ./configure.py"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Fix Makefile to use proper optimization flags to avoid _FORTIFY_SOURCE warnings
self._fixWSGIMakefile()
2025-08-01 14:56:30 +05:00
# Compile with proper optimization flags
command = "make clean"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-01 14:56:30 +05:00
command = "make"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
if not os.path.exists('/usr/local/CyberCP/bin/'):
os.mkdir('/usr/local/CyberCP/bin/')
command = "cp lswsgi /usr/local/CyberCP/bin/"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Set proper permissions
command = "chmod +x /usr/local/CyberCP/bin/lswsgi"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-01 14:56:30 +05:00
os.chdir(self.cwd)
preFlightsChecks.stdOut("WSGI-LSAPI compiled successfully with optimized flags", 1)
2025-08-01 14:56:30 +05:00
except Exception as e:
preFlightsChecks.stdOut(f"WSGI setup error: {str(e)}", 0)
2025-08-01 14:56:30 +05:00
return 0
def ensureVirtualEnvironmentSetup(self):
"""Ensure virtual environment is properly set up and accessible"""
try:
# Check multiple possible virtual environment locations
venv_paths = [
'/usr/local/CyberCP/bin/python',
'/usr/local/CyberPanel/bin/python',
'/usr/local/CyberPanel-venv/bin/python'
]
found_venv = None
for path in venv_paths:
if os.path.exists(path):
found_venv = path
preFlightsChecks.stdOut(f"Virtual environment found at: {path}", 1)
break
if not found_venv:
preFlightsChecks.stdOut("No virtual environment found in expected locations", 0)
return False
# Create symlinks for compatibility if needed
if found_venv == '/usr/local/CyberCP/bin/python':
if not os.path.exists('/usr/local/CyberPanel/bin/python'):
if not os.path.exists('/usr/local/CyberPanel'):
preFlightsChecks.stdOut("Creating CyberPanel symlink for compatibility", 1)
os.symlink('/usr/local/CyberCP', '/usr/local/CyberPanel')
else:
preFlightsChecks.stdOut("CyberPanel directory exists but Python not found", 0)
return False
# Test if Python is executable
try:
result = os.system(f"{found_venv} --version > /dev/null 2>&1")
if result != 0:
preFlightsChecks.stdOut(f"Python at {found_venv} is not executable", 0)
return False
except Exception as e:
preFlightsChecks.stdOut(f"Error testing Python executable: {str(e)}", 0)
return False
return True
except Exception as e:
preFlightsChecks.stdOut(f"Error setting up virtual environment: {str(e)}", 0)
return False
def upgradePip(self):
"""Upgrade pip to latest version for better package compatibility"""
try:
preFlightsChecks.stdOut("Upgrading pip to latest version...", 1)
# Determine the correct Python path
python_paths = [
"/usr/local/CyberPanel/bin/python",
"/usr/local/CyberCP/bin/python",
"/usr/bin/python3",
"/usr/local/bin/python3"
]
python_path = None
for path in python_paths:
if os.path.exists(path):
python_path = path
break
if not python_path:
preFlightsChecks.stdOut("No Python executable found for pip upgrade", 0)
return False
# Upgrade pip and essential packages
upgrade_command = f"{python_path} -m pip install --upgrade pip setuptools wheel packaging"
result = preFlightsChecks.call(upgrade_command, self.distro, "Upgrade pip", upgrade_command, 1, 0, os.EX_OSERR)
if result == 1:
preFlightsChecks.stdOut("pip upgraded successfully", 1)
return True
else:
preFlightsChecks.stdOut("WARNING: pip upgrade failed, continuing with current version", 0)
return False
except Exception as e:
preFlightsChecks.stdOut(f"Error upgrading pip: {str(e)}", 0)
return False
def _fixWSGIMakefile(self):
"""Fix the Makefile to use proper compiler optimization flags"""
try:
makefile_path = "Makefile"
if not os.path.exists(makefile_path):
preFlightsChecks.stdOut("Makefile not found, skipping optimization fix", 1)
return
# Read the Makefile
with open(makefile_path, 'r') as f:
content = f.read()
# Fix compiler flags to avoid _FORTIFY_SOURCE warnings
# Replace -O0 -g3 with -O2 -g to satisfy _FORTIFY_SOURCE
content = content.replace('-O0 -g3', '-O2 -g')
# Ensure we have proper optimization flags
if 'CFLAGS' in content and '-O2' not in content:
content = content.replace('CFLAGS =', 'CFLAGS = -O2')
# Write the fixed Makefile
with open(makefile_path, 'w') as f:
f.write(content)
preFlightsChecks.stdOut("Makefile optimized for proper compilation", 1)
except Exception as e:
preFlightsChecks.stdOut(f"Warning: Could not optimize Makefile: {str(e)}", 1)
2025-08-01 14:56:30 +05:00
def setupLSCPDDaemon(self):
try:
preFlightsChecks.stdOut("Trying to setup LSCPD Daemon!")
logging.InstallLog.writeToFile("Trying to setup LSCPD Daemon!")
os.chdir(self.cwd)
shutil.copy("lscpd/lscpd.service", "/etc/systemd/system/lscpd.service")
shutil.copy("lscpd/lscpdctrl", "/usr/local/lscp/bin/lscpdctrl")
##
command = 'chmod +x /usr/local/lscp/bin/lscpdctrl'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
path = "/usr/local/lscpd/admin/"
command = "mkdir -p " + path
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
path = "/usr/local/CyberCP/conf/"
command = "mkdir -p " + path
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
path = "/usr/local/CyberCP/conf/token_env"
writeToFile = open(path, "w")
writeToFile.write("abc\n")
writeToFile.close()
command = "chmod 600 " + path
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
self.manage_service('lscpd', 'enable')
##
count = 0
# In Ubuntu, the library that lscpd looks for is libpcre.so.1, but the one it installs is libpcre.so.3...
if self.distro == ubuntu:
command = 'ln -s /lib/x86_64-linux-gnu/libpcre.so.3 /lib/x86_64-linux-gnu/libpcre.so.1'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
##
command = 'systemctl start lscpd'
# preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
preFlightsChecks.stdOut("LSCPD Daemon Set!")
logging.InstallLog.writeToFile("LSCPD Daemon Set!")
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setupLSCPDDaemon]")
return 0
return 1
def setup_cron(self):
try:
## first install crontab
if self.is_centos_family():
self.install_package('cronie')
else:
self.install_package('cron')
if self.is_centos_family():
self.manage_service('crond', 'enable')
self.manage_service('crond', 'start')
else:
self.manage_service('cron', 'enable')
self.manage_service('cron', 'start')
##
CentOSPath = '/etc/redhat-release'
openEulerPath = '/etc/openEuler-release'
if os.path.exists(CentOSPath) or os.path.exists(openEulerPath):
cronPath = '/var/spool/cron/root'
else:
cronPath = '/var/spool/cron/crontabs/root'
cronFile = open(cronPath, "w")
content = """
0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/findBWUsage.py >/dev/null 2>&1
0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py hourlyCleanup >/dev/null 2>&1
0 0 1 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py monthlyCleanup >/dev/null 2>&1
0 2 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/upgradeCritical.py >/dev/null 2>&1
0 0 * * 4 /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/renew.py >/dev/null 2>&1
7 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
0 0 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Daily
0 0 * * 0 /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Weekly
*/30 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '30 Minutes'
0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '1 Hour'
0 */6 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '6 Hours'
0 */12 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '12 Hours'
0 1 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '1 Day'
0 0 */3 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '3 Days'
0 0 * * 0 /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '1 Week'
*/3 * * * * if ! find /home/*/public_html/ -maxdepth 2 -type f -newer /usr/local/lsws/cgid -name '.htaccess' -exec false {} +; then /usr/local/lsws/bin/lswsctrl restart; fi
"""
cronFile.write(content)
cronFile.close()
### Check and remove OLS restart if lsws ent detected
if not os.path.exists('/usr/local/lsws/bin/openlitespeed'):
data = open(cronPath, 'r').readlines()
writeToFile = open(cronPath, 'w')
for items in data:
if items.find('-maxdepth 2 -type f -newer') > -1:
pass
else:
writeToFile.writelines(items)
writeToFile.close()
if not os.path.exists(CentOSPath) or not os.path.exists(openEulerPath):
command = 'chmod 600 %s' % (cronPath)
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
if self.is_centos_family():
self.manage_service('crond', 'restart')
else:
self.manage_service('cron', 'restart')
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setup_cron]")
return 0
def install_default_keys(self):
try:
path = "/root/.ssh"
if not os.path.exists(path):
os.mkdir(path)
command = "ssh-keygen -f /root/.ssh/cyberpanel -t rsa -N ''"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_default_keys]")
return 0
def install_rsync(self):
try:
self.install_package('rsync')
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_rsync]")
return 0
def test_Requests(self):
try:
import requests
getVersion = requests.get('https://cyberpanel.net/version.txt')
latest = getVersion.json()
except BaseException as msg:
2025-08-29 20:44:19 +05:00
# Handle Ubuntu 24.04's externally-managed-environment policy
pip_flags = ""
if self.distro == ubuntu:
try:
release = install_utils.get_Ubuntu_release(use_print=False, exit_on_error=False)
if release and release >= 24.04:
pip_flags = " --break-system-packages"
except:
pass # If version detection fails, try without flags
command = f"pip uninstall --yes{pip_flags} urllib3"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-29 20:44:19 +05:00
command = f"pip uninstall --yes{pip_flags} requests"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-29 20:44:19 +05:00
command = f"pip install{pip_flags} http://mirror.cyberpanel.net/urllib3-1.22.tar.gz"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-29 20:44:19 +05:00
command = f"pip install{pip_flags} http://mirror.cyberpanel.net/requests-2.18.4.tar.gz"
2025-08-01 14:56:30 +05:00
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
def installation_successfull(self):
print("###################################################################")
print(" CyberPanel Successfully Installed ")
print(" ")
print(" ")
print(" ")
print((" Visit: https://" + self.ipAddr + ":8090 "))
print(" Username: admin ")
print(" Password: 1234567 ")
print("###################################################################")
def modSecPreReqs(self):
try:
pathToRemoveGarbageFile = os.path.join(self.server_root_path, "modules/mod_security.so")
os.remove(pathToRemoveGarbageFile)
except OSError as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [modSecPreReqs]")
return 0
def installOpenDKIM(self):
try:
# Install dependencies first
if self.distro == ubuntu:
deps = ['libmilter-dev', 'libmemcached-dev']
for dep in deps:
try:
self.install_package(dep)
except:
pass
2025-08-01 14:56:30 +05:00
self.install_package('opendkim opendkim-tools')
else:
# Install dependencies for RHEL-based systems
deps = ['sendmail-milter', 'sendmail-milter-devel', 'libmemcached', 'libmemcached-devel']
for dep in deps:
try:
self.install_package(dep, '--skip-broken')
except:
pass
if self.distro == cent8 or self.distro == openeuler:
self.install_package('opendkim opendkim-tools', '--skip-broken')
else:
self.install_package('opendkim', '--skip-broken')
2025-08-01 14:56:30 +05:00
command = 'mkdir -p /etc/opendkim/keys/'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installOpenDKIM]")
return 0
return 1
def configureOpenDKIM(self):
try:
## Configure OpenDKIM specific settings
openDKIMConfigurePath = "/etc/opendkim.conf"
configData = """
Mode sv
Canonicalization relaxed/simple
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
"""
writeToFile = open(openDKIMConfigurePath, 'a')
writeToFile.write(configData)
writeToFile.close()
## Configure postfix specific settings
postfixFilePath = "/etc/postfix/main.cf"
configData = """
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
"""
writeToFile = open(postfixFilePath, 'a')
writeToFile.write(configData)
writeToFile.close()
if self.distro == ubuntu or self.distro == cent8:
data = open(openDKIMConfigurePath, 'r').readlines()
writeToFile = open(openDKIMConfigurePath, 'w')
for items in data:
if items.find('Socket') > -1 and items.find('local:'):
writeToFile.writelines('Socket inet:8891@localhost\n')
else:
writeToFile.writelines(items)
writeToFile.close()
#### Restarting Postfix and OpenDKIM
self.manage_service('opendkim', 'start')
self.manage_service('opendkim', 'enable')
self.manage_service('postfix', 'start')
except BaseException as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [configureOpenDKIM]")
return 0
return 1
def setupCLI(self):
command = "ln -s /usr/local/CyberCP/cli/cyberPanel.py /usr/bin/cyberpanel"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "chmod +x /usr/local/CyberCP/cli/cyberPanel.py"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
def setupPHPSymlink(self):
try:
# Ensure LiteSpeed repository is available for PHP packages
if self.distro == centos or self.distro == cent8 or self.distro == openeuler:
# Check if LiteSpeed repository is available
command = 'dnf repolist | grep -q litespeed'
result = preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
if result != 1:
logging.InstallLog.writeToFile("[setupPHPSymlink] LiteSpeed repository not found, attempting to add it...")
# Add LiteSpeed repository
repo_command = 'rpm -Uvh http://rpms.litespeedtech.com/centos/litespeed-repo-1.1-1.el8.noarch.rpm'
preFlightsChecks.call(repo_command, self.distro, repo_command, repo_command, 1, 0, os.EX_OSERR)
# Check if PHP 8.2 exists
if not os.path.exists('/usr/local/lsws/lsphp82/bin/php'):
logging.InstallLog.writeToFile("[setupPHPSymlink] PHP 8.2 not found, ensuring it's installed...")
# Install PHP 8.2 based on OS
if self.distro == centos or self.distro == cent8 or self.distro == openeuler:
command = 'dnf install lsphp82 lsphp82-* -y --skip-broken --nobest'
else:
command = 'DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp82 lsphp82-*'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-07 06:58:48 +05:00
# Check if PHP 8.3 exists
if not os.path.exists('/usr/local/lsws/lsphp83/bin/php'):
logging.InstallLog.writeToFile("[setupPHPSymlink] PHP 8.3 not found, ensuring it's installed...")
# Install PHP 8.3 based on OS
if self.distro == centos or self.distro == cent8 or self.distro == openeuler:
command = 'dnf install lsphp83 lsphp83-* -y --skip-broken --nobest'
2025-08-07 06:58:48 +05:00
else:
command = 'DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp83 lsphp83-*'
result = preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-07 06:58:48 +05:00
# Verify installation
if not os.path.exists('/usr/local/lsws/lsphp83/bin/php'):
logging.InstallLog.writeToFile('[ERROR] Failed to install PHP 8.3, trying alternative method...')
# Try alternative installation method
alt_command = 'dnf install lsphp83 -y --skip-broken --nobest --allowerasing'
preFlightsChecks.call(alt_command, self.distro, alt_command, alt_command, 1, 0, os.EX_OSERR)
if not os.path.exists('/usr/local/lsws/lsphp83/bin/php'):
logging.InstallLog.writeToFile('[ERROR] Alternative PHP 8.3 installation also failed')
return 0
# Install PHP 8.4
if not os.path.exists('/usr/local/lsws/lsphp84/bin/php'):
logging.InstallLog.writeToFile("[setupPHPSymlink] PHP 8.4 not found, ensuring it's installed...")
if self.distro == centos or self.distro == cent8 or self.distro == openeuler:
command = 'dnf install lsphp84 lsphp84-* -y --skip-broken --nobest'
else:
command = 'DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp84 lsphp84-*'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Install PHP 8.5
if not os.path.exists('/usr/local/lsws/lsphp85/bin/php'):
logging.InstallLog.writeToFile("[setupPHPSymlink] PHP 8.5 not found, ensuring it's installed...")
if self.distro == centos or self.distro == cent8 or self.distro == openeuler:
command = 'dnf install lsphp85 lsphp85-* -y --skip-broken --nobest'
else:
command = 'DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp85 lsphp85-*'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-07 06:58:48 +05:00
2025-08-01 14:56:30 +05:00
# Remove existing PHP symlink if it exists
if os.path.exists('/usr/bin/php'):
os.remove('/usr/bin/php')
# Create symlink to the best available PHP version
# Try to find and use the best available PHP version
# Priority: 85 (beta), 84, 83, 82, 81, 80, 74 (newest to oldest)
php_versions = ['85', '84', '83', '82', '81', '80', '74']
php_symlink_source = None
for php_ver in php_versions:
candidate_path = f"/usr/local/lsws/lsphp{php_ver}/bin/php"
if os.path.exists(candidate_path):
php_symlink_source = candidate_path
logging.InstallLog.writeToFile(f"[setupPHPSymlink] Found PHP {php_ver} binary: {candidate_path}")
break
if php_symlink_source:
command = f'ln -sf {php_symlink_source} /usr/bin/php'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
logging.InstallLog.writeToFile(f"[setupPHPSymlink] PHP symlink updated to {php_symlink_source} successfully.")
# Verify symlink works
verify_command = f'{php_symlink_source} --version'
result = preFlightsChecks.call(verify_command, self.distro, verify_command, verify_command, 1, 1, os.EX_OSERR)
if result == 1:
logging.InstallLog.writeToFile("[setupPHPSymlink] PHP symlink verification successful")
else:
logging.InstallLog.writeToFile("[WARNING] PHP symlink verification failed")
else:
logging.InstallLog.writeToFile("[ERROR] No PHP versions found for symlink creation")
# List available PHP versions for debugging
command = 'find /usr/local/lsws -name "lsphp*" -type d 2>/dev/null || true'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
return 0
2025-08-01 14:56:30 +05:00
except OSError as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setupPHPSymlink]")
return 0
def setup_lsphp_symlink(self):
"""Create lsphp symlink in fcgi-bin directory with robust error handling"""
try:
fcgi_bin_dir = "/usr/local/lscp/fcgi-bin"
lsphp_target = os.path.join(fcgi_bin_dir, "lsphp")
# Ensure fcgi-bin directory exists
if not os.path.exists(fcgi_bin_dir):
os.makedirs(fcgi_bin_dir, exist_ok=True)
logging.InstallLog.writeToFile(f"[setup_lsphp_symlink] Created fcgi-bin directory: {fcgi_bin_dir}")
# Remove existing lsphp file/symlink if it exists
if os.path.exists(lsphp_target) or os.path.islink(lsphp_target):
os.remove(lsphp_target)
logging.InstallLog.writeToFile("[setup_lsphp_symlink] Removed existing lsphp file/symlink")
# Try to find and use the best available PHP version
# Priority: 85 (beta), 84, 83, 82, 81, 80, 74 (newest to oldest)
php_versions = ['85', '84', '83', '82', '81', '80', '74']
lsphp_source = None
for php_ver in php_versions:
candidate_path = f"/usr/local/lsws/lsphp{php_ver}/bin/lsphp"
if os.path.exists(candidate_path):
lsphp_source = candidate_path
logging.InstallLog.writeToFile(f"[setup_lsphp_symlink] Found lsphp binary: {candidate_path}")
break
# If no lsphp binary found, try to find php binary as fallback
if not lsphp_source:
for php_ver in php_versions:
candidate_path = f"/usr/local/lsws/lsphp{php_ver}/bin/php"
if os.path.exists(candidate_path):
lsphp_source = candidate_path
logging.InstallLog.writeToFile(f"[setup_lsphp_symlink] Using php binary as fallback: {candidate_path}")
break
# If still no source found, try admin_php as last resort
if not lsphp_source:
admin_php_path = "/usr/local/lscp/admin/fcgi-bin/admin_php"
if os.path.exists(admin_php_path):
lsphp_source = admin_php_path
logging.InstallLog.writeToFile(f"[setup_lsphp_symlink] Using admin_php as fallback: {admin_php_path}")
admin_php5_path = "/usr/local/lscp/admin/fcgi-bin/admin_php5"
if not lsphp_source and os.path.exists(admin_php5_path):
lsphp_source = admin_php5_path
logging.InstallLog.writeToFile(f"[setup_lsphp_symlink] Using admin_php5 as fallback: {admin_php5_path}")
# Create the symlink/copy
if lsphp_source:
try:
# Try to create symlink first (preferred)
os.symlink(lsphp_source, lsphp_target)
logging.InstallLog.writeToFile(f"[setup_lsphp_symlink] Created symlink: {lsphp_target} -> {lsphp_source}")
except OSError:
# If symlink fails (e.g., cross-filesystem), copy the file
shutil.copy2(lsphp_source, lsphp_target)
logging.InstallLog.writeToFile(f"[setup_lsphp_symlink] Copied file: {lsphp_source} -> {lsphp_target}")
# Set proper permissions
os.chmod(lsphp_target, 0o755)
logging.InstallLog.writeToFile("[setup_lsphp_symlink] Set permissions to 755")
# Verify the file was created successfully
if os.path.exists(lsphp_target):
logging.InstallLog.writeToFile("[setup_lsphp_symlink] lsphp symlink creation successful")
return True
else:
logging.InstallLog.writeToFile("[setup_lsphp_symlink] ERROR: lsphp file was not created")
return False
else:
logging.InstallLog.writeToFile("[setup_lsphp_symlink] ERROR: No suitable PHP binary found")
return False
except Exception as e:
logging.InstallLog.writeToFile(f"[setup_lsphp_symlink] ERROR: {str(e)}")
return False
2025-08-01 14:56:30 +05:00
def setupPHPAndComposer(self):
try:
# First setup the PHP symlink
self.setupPHPSymlink()
if self.distro == ubuntu:
if not os.access('/usr/local/lsws/lsphp70/bin/php', os.R_OK):
if os.access('/usr/local/lsws/lsphp70/bin/php7.0', os.R_OK):
os.symlink('/usr/local/lsws/lsphp70/bin/php7.0', '/usr/local/lsws/lsphp70/bin/php')
if not os.access('/usr/local/lsws/lsphp71/bin/php', os.R_OK):
if os.access('/usr/local/lsws/lsphp71/bin/php7.1', os.R_OK):
os.symlink('/usr/local/lsws/lsphp71/bin/php7.1', '/usr/local/lsws/lsphp71/bin/php')
if not os.access('/usr/local/lsws/lsphp72/bin/php', os.R_OK):
if os.access('/usr/local/lsws/lsphp72/bin/php7.2', os.R_OK):
os.symlink('/usr/local/lsws/lsphp72/bin/php7.2', '/usr/local/lsws/lsphp72/bin/php')
#command = "cp /usr/local/lsws/lsphp71/bin/php /usr/bin/"
#preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
os.chdir(self.cwd)
command = "chmod +x composer.sh"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = "./composer.sh"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
except OSError as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setupPHPAndComposer]")
return 0
@staticmethod
def installOne(package):
res = subprocess.call(shlex.split('DEBIAN_FRONTEND=noninteractive apt-get -y install ' + package))
if res != 0:
preFlightsChecks.stdOut("Error #" + str(res) + ' installing:' + package + '. This may not be an issue ' \
'but may affect installation of something later',
1)
return res # Though probably not used
@staticmethod
def enableDisableDNS(state):
try:
servicePath = '/home/cyberpanel/powerdns'
if state == 'off':
pdns_service = preFlightsChecks.get_service_name('pdns')
2025-09-18 12:30:22 +05:00
command = f'sudo systemctl stop {pdns_service}'
2025-08-01 14:56:30 +05:00
subprocess.call(shlex.split(command))
2025-09-18 12:30:22 +05:00
command = f'sudo systemctl disable {pdns_service}'
2025-08-01 14:56:30 +05:00
subprocess.call(shlex.split(command))
try:
os.remove(servicePath)
except:
pass
else:
writeToFile = open(servicePath, 'w+')
writeToFile.close()
except OSError as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [enableDisableDNS]")
return 0
@staticmethod
def enableDisableEmail(state):
try:
servicePath = '/home/cyberpanel/postfix'
if state == 'off':
command = 'sudo systemctl stop postfix'
subprocess.call(shlex.split(command))
command = 'sudo systemctl disable postfix'
subprocess.call(shlex.split(command))
try:
os.remove(servicePath)
except:
pass
else:
writeToFile = open(servicePath, 'w+')
writeToFile.close()
except OSError as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [enableDisableEmail]")
return 0
@staticmethod
def enableDisableFTP(state, distro):
try:
servicePath = '/home/cyberpanel/pureftpd'
if state == 'off':
command = 'sudo systemctl stop ' + preFlightsChecks.pureFTPDServiceName(distro)
subprocess.call(shlex.split(command))
command = 'sudo systemctl disable ' + preFlightsChecks.pureFTPDServiceName(distro)
subprocess.call(shlex.split(command))
try:
os.remove(servicePath)
except:
pass
else:
writeToFile = open(servicePath, 'w+')
writeToFile.close()
except OSError as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [enableDisableEmail]")
return 0
@staticmethod
def fixSudoers():
try:
distroPath = '/etc/lsb-release'
if not os.path.exists(distroPath):
fileName = '/etc/sudoers'
data = open(fileName, 'r').readlines()
writeDataToFile = open(fileName, 'w')
for line in data:
if line.find("root") > -1 and line.find("ALL=(ALL)") > -1 and line[0] != '#':
writeDataToFile.writelines('root ALL=(ALL:ALL) ALL\n')
else:
writeDataToFile.write(line)
writeDataToFile.close()
except IOError as err:
pass
@staticmethod
def setUpFirstAccount():
try:
command = 'python /usr/local/CyberCP/plogical/adminPass.py --password 1234567'
subprocess.call(shlex.split(command))
except:
pass
def installRestic(self):
try:
CentOSPath = '/etc/redhat-release'
openEulerPath = '/etc/openEuler-release'
if os.path.exists(CentOSPath) or os.path.exists(openEulerPath):
if self.distro == centos:
command = 'yum install -y yum-plugin-copr'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'yum copr enable -y copart/restic'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'yum install -y restic'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'restic self-update'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
else:
# Skip apt-get update as it was already done in cyberpanel.sh
# Just install the package directly
command = 'DEBIAN_FRONTEND=noninteractive apt-get install restic -y'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, True)
command = 'restic self-update'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
except:
pass
def installCLScripts(self):
try:
CentOSPath = '/etc/redhat-release'
openEulerPath = '/etc/openEuler-release'
if os.path.exists(CentOSPath) or os.path.exists(openEulerPath):
command = 'mkdir -p /opt/cpvendor/etc/'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
content = """[integration_scripts]
panel_info = /usr/local/CyberCP/CLScript/panel_info.py
packages = /usr/local/CyberCP/CLScript/CloudLinuxPackages.py
users = /usr/local/CyberCP/CLScript/CloudLinuxUsers.py
domains = /usr/local/CyberCP/CLScript/CloudLinuxDomains.py
resellers = /usr/local/CyberCP/CLScript/CloudLinuxResellers.py
admins = /usr/local/CyberCP/CLScript/CloudLinuxAdmins.py
db_info = /usr/local/CyberCP/CLScript/CloudLinuxDB.py
[lvemanager_config]
ui_user_info =/usr/local/CyberCP/CLScript/UserInfo.py
base_path = /usr/local/lvemanager
run_service = 1
service_port = 9000
"""
writeToFile = open('/opt/cpvendor/etc/integration.ini', 'w')
writeToFile.write(content)
writeToFile.close()
command = 'mkdir -p /etc/cagefs/exclude'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
content = """cyberpanel
docker
ftpuser
lscpd
opendkim
pdns
vmail
"""
writeToFile = open('/etc/cagefs/exclude/cyberpanelexclude', 'w')
writeToFile.write(content)
writeToFile.close()
except:
pass
def installAcme(self):
command = 'wget -O - https://get.acme.sh | sh'
subprocess.call(command, shell=True)
command = '/root/.acme.sh/acme.sh --upgrade --auto-upgrade'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
def installRedis(self):
if self.distro == ubuntu:
command = 'apt install redis-server -y'
elif self.distro == centos:
command = 'yum install redis -y'
else:
command = 'dnf install redis -y'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
## install redis conf
redisConf = '/usr/local/lsws/conf/dvhost_redis.conf'
writeToFile = open(redisConf, 'w')
writeToFile.write('127.0.0.1,6379,<auth_password>\n')
writeToFile.close()
##
os.chdir(self.cwd)
confPath = '/usr/local/lsws/conf/'
if os.path.exists('%shttpd.conf' % (confPath)):
os.remove('%shttpd.conf' % (confPath))
shutil.copy('litespeed/httpd-redis.conf', '%shttpd.conf' % (confPath))
## start and enable
command = 'systemctl start redis'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = 'systemctl enable redis'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
def disablePackegeUpdates(self):
if self.distro == centos:
mainConfFile = '/etc/yum.conf'
content = 'exclude=MariaDB-client MariaDB-common MariaDB-devel MariaDB-server MariaDB-shared ' \
'pdns pdns-backend-mysql dovecot dovecot-mysql postfix3 postfix3-ldap postfix3-mysql ' \
'postfix3-pcre restic opendkim libopendkim pure-ftpd ftp\n'
writeToFile = open(mainConfFile, 'a')
writeToFile.write(content)
writeToFile.close()
def installDNS_CyberPanelACMEFile(self):
os.chdir(self.cwd)
filePath = '/root/.acme.sh/dns_cyberpanel.sh'
shutil.copy('dns_cyberpanel.sh', filePath)
command = f'chmod +x {filePath}'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
def startDeferredServices(self):
"""Start services that were deferred during installation (PowerDNS and Pure-FTPd)
These services require database tables that are created by Django migrations"""
preFlightsChecks.stdOut("Starting deferred services that depend on database tables...")
# Ensure database is ready first
self.ensureDatabaseReady()
2025-08-01 14:56:30 +05:00
# Start PowerDNS if it was installed
if os.path.exists('/home/cyberpanel/powerdns'):
self.fixAndStartPowerDNS()
# Start Pure-FTPd if it was installed
if os.path.exists('/home/cyberpanel/pureftpd'):
self.fixAndStartPureFTPd()
# Ensure LiteSpeed services are running
self.ensureLiteSpeedServicesRunning()
# Final service verification
self.verifyCriticalServices()
def ensureDatabaseReady(self):
"""Ensure database is ready before starting dependent services"""
preFlightsChecks.stdOut("Ensuring database is ready...")
# Wait for MySQL/MariaDB to be ready
max_attempts = 30
for attempt in range(max_attempts):
try:
if subprocess.run(['mysqladmin', 'ping', '-h', 'localhost', '--silent'],
capture_output=True).returncode == 0:
preFlightsChecks.stdOut("Database is ready")
return
except:
pass
preFlightsChecks.stdOut(f"Waiting for database... ({attempt + 1}/{max_attempts})")
time.sleep(2)
preFlightsChecks.stdOut("[WARNING] Database may not be fully ready")
def ensurePowerDNSDatabaseAccess(self):
"""Ensure PowerDNS database tables and access are properly set up"""
try:
preFlightsChecks.stdOut("Setting up PowerDNS database access...", 1)
# Create PowerDNS database tables if they don't exist
db_commands = [
"mysql -e \"CREATE DATABASE IF NOT EXISTS powerdns;\"",
"mysql -e \"CREATE USER IF NOT EXISTS 'powerdns'@'localhost' IDENTIFIED BY 'cyberpanel';\"",
"mysql -e \"GRANT ALL PRIVILEGES ON powerdns.* TO 'powerdns'@'localhost';\"",
"mysql -e \"FLUSH PRIVILEGES;\""
]
for cmd in db_commands:
preFlightsChecks.call(cmd, self.distro, f"PowerDNS DB: {cmd}", cmd, 1, 0, os.EX_OSERR)
# Import PowerDNS schema if tables don't exist
schema_check = "mysql -e \"USE powerdns; SHOW TABLES;\""
result = subprocess.run(schema_check, shell=True, capture_output=True, text=True)
if not result.stdout.strip() or 'domains' not in result.stdout:
preFlightsChecks.stdOut("Importing PowerDNS database schema...", 1)
# Try to find and import PowerDNS schema
schema_files = [
'/usr/share/doc/pdns-backend-mysql/schema.mysql.sql',
'/usr/share/doc/powerdns/schema.mysql.sql',
'/usr/share/pdns/schema.mysql.sql'
]
for schema_file in schema_files:
if os.path.exists(schema_file):
import_cmd = f"mysql powerdns < {schema_file}"
preFlightsChecks.call(import_cmd, self.distro, f"Import PowerDNS schema", import_cmd, 1, 0, os.EX_OSERR)
break
else:
preFlightsChecks.stdOut("PowerDNS schema not found, creating basic tables...", 1)
# Create basic PowerDNS tables
basic_schema = """
CREATE TABLE IF NOT EXISTS domains (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
master VARCHAR(128) DEFAULT NULL,
last_check INT DEFAULT NULL,
type VARCHAR(6) NOT NULL,
notified_serial INT DEFAULT NULL,
account VARCHAR(40) DEFAULT NULL,
UNIQUE KEY name (name)
);
CREATE TABLE IF NOT EXISTS records (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
domain_id INT DEFAULT NULL,
name VARCHAR(255) DEFAULT NULL,
type VARCHAR(10) DEFAULT NULL,
content TEXT DEFAULT NULL,
ttl INT DEFAULT NULL,
prio INT DEFAULT NULL,
change_date INT DEFAULT NULL,
disabled TINYINT(1) DEFAULT 0,
ordername VARCHAR(255) DEFAULT NULL,
auth TINYINT(1) DEFAULT 1,
KEY domain_id (domain_id),
KEY name (name),
KEY type (type)
);
"""
with open('/tmp/powerdns_schema.sql', 'w') as f:
f.write(basic_schema)
preFlightsChecks.call("mysql powerdns < /tmp/powerdns_schema.sql", self.distro, "Create PowerDNS tables", "mysql powerdns < /tmp/powerdns_schema.sql", 1, 0, os.EX_OSERR)
except Exception as e:
preFlightsChecks.stdOut(f"Warning: Could not set up PowerDNS database access: {str(e)}", 0)
def fixAndStartPowerDNS(self):
"""Fix PowerDNS configuration and start the service"""
preFlightsChecks.stdOut("Fixing and starting PowerDNS service...")
# Determine correct service name
pdns_service = None
if subprocess.run(['systemctl', 'list-unit-files'], capture_output=True, text=True).stdout.find('pdns.service') != -1:
pdns_service = 'pdns'
elif subprocess.run(['systemctl', 'list-unit-files'], capture_output=True, text=True).stdout.find('powerdns.service') != -1:
pdns_service = 'powerdns'
if not pdns_service:
preFlightsChecks.stdOut("[WARNING] PowerDNS service not found")
return
# Fix PowerDNS configuration
config_files = ['/etc/pdns/pdns.conf', '/etc/powerdns/pdns.conf']
for config_file in config_files:
if os.path.exists(config_file):
preFlightsChecks.stdOut(f"Configuring PowerDNS: {config_file}")
# Read existing content
try:
with open(config_file, 'r') as f:
content = f.read()
except:
content = ""
# Add missing configuration if not present
config_additions = []
if 'gmysql-password=' not in content:
config_additions.append('gmysql-password=cyberpanel')
if 'launch=' not in content:
config_additions.append('launch=gmysql')
if 'gmysql-host=' not in content:
config_additions.append('gmysql-host=localhost')
if 'gmysql-user=' not in content:
config_additions.append('gmysql-user=cyberpanel')
if 'gmysql-dbname=' not in content:
config_additions.append('gmysql-dbname=cyberpanel')
if config_additions:
with open(config_file, 'a') as f:
f.write('\n# CyberPanel configuration\n')
for config in config_additions:
f.write(config + '\n')
# Ensure proper permissions
os.chmod(config_file, 0o644)
break
# Ensure PowerDNS can connect to database
self.ensurePowerDNSDatabaseAccess()
# Start PowerDNS service with retry mechanism
max_retries = 3
for attempt in range(max_retries):
preFlightsChecks.stdOut(f"Starting PowerDNS service (attempt {attempt + 1}/{max_retries})...", 1)
# Stop service first to ensure clean start
command = f'systemctl stop {pdns_service}'
preFlightsChecks.call(command, self.distro, f"Stop {pdns_service}", command, 0, 0, os.EX_OSERR)
time.sleep(2)
# Start service
command = f'systemctl start {pdns_service}'
result = preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Wait a moment for service to start
time.sleep(3)
# Check if service is actually running
command = f'systemctl is-active {pdns_service}'
try:
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=10)
output = result.stdout.strip()
if output == 'active':
preFlightsChecks.stdOut("PowerDNS service started successfully!", 1)
# Double-check with systemctl status for more details
status_command = f'systemctl status {pdns_service} --no-pager -l'
try:
status_result = subprocess.run(status_command, shell=True, capture_output=True, text=True, timeout=10)
if status_result.returncode == 0:
preFlightsChecks.stdOut("PowerDNS service status verified", 1)
else:
preFlightsChecks.stdOut("PowerDNS service running but status check failed", 0)
except:
preFlightsChecks.stdOut("PowerDNS service running but status verification failed", 0)
break
else:
preFlightsChecks.stdOut(f"PowerDNS service status: {output} (attempt {attempt + 1})", 0)
if attempt < max_retries - 1:
preFlightsChecks.stdOut("Retrying PowerDNS startup...", 1)
time.sleep(5)
except subprocess.TimeoutExpired:
preFlightsChecks.stdOut(f"PowerDNS service status check timed out (attempt {attempt + 1})", 0)
if attempt < max_retries - 1:
time.sleep(5)
except Exception as e:
preFlightsChecks.stdOut(f"Could not verify PowerDNS service status (attempt {attempt + 1}): {str(e)}", 0)
if attempt < max_retries - 1:
time.sleep(5)
else:
preFlightsChecks.stdOut("[WARNING] PowerDNS service failed to start after all retries", 0)
# Try to get more details about the failure
command = f'systemctl status {pdns_service} --no-pager'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Enable service for auto-start (with error handling)
command = f'systemctl enable {pdns_service}'
enable_result = preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
if enable_result != 1:
preFlightsChecks.stdOut(f"Warning: Could not enable {pdns_service} for auto-start", 0)
def fixAndStartPureFTPd(self):
"""Fix Pure-FTPd configuration and start the service"""
preFlightsChecks.stdOut("Fixing and starting Pure-FTPd service...")
# Determine correct service name
ftp_service = None
if subprocess.run(['systemctl', 'list-unit-files'], capture_output=True, text=True).stdout.find('pure-ftpd.service') != -1:
ftp_service = 'pure-ftpd'
elif subprocess.run(['systemctl', 'list-unit-files'], capture_output=True, text=True).stdout.find('pureftpd.service') != -1:
ftp_service = 'pureftpd'
if not ftp_service:
preFlightsChecks.stdOut("[WARNING] Pure-FTPd service not found")
return
# Fix Pure-FTPd configuration
config_files = ['/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/db/mysql.conf']
for config_file in config_files:
if os.path.exists(config_file):
preFlightsChecks.stdOut(f"Configuring Pure-FTPd: {config_file}")
# Fix MySQL password configuration
command = f"sed -i 's/MYSQLPassword.*/MYSQLPassword cyberpanel/' {config_file}"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Fix MySQL crypt method for Ubuntu 24.04 compatibility
if self.distro == ubuntu:
import install_utils
try:
release = install_utils.get_Ubuntu_release(use_print=False, exit_on_error=False)
if release and release >= 24.04:
preFlightsChecks.stdOut("Configuring Pure-FTPd for Ubuntu 24.04...")
command = f"sed -i 's/MYSQLCrypt md5/MYSQLCrypt crypt/g' {config_file}"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
except:
pass
# Start Pure-FTPd service with retry mechanism
max_retries = 3
for attempt in range(max_retries):
preFlightsChecks.stdOut(f"Starting Pure-FTPd service (attempt {attempt + 1}/{max_retries})...", 1)
# Stop service first to ensure clean start
command = f'systemctl stop {ftp_service}'
preFlightsChecks.call(command, self.distro, f"Stop {ftp_service}", command, 0, 0, os.EX_OSERR)
time.sleep(2)
# Start service
command = f'systemctl start {ftp_service}'
result = preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Wait a moment for service to start
time.sleep(3)
# Check if service is actually running
command = f'systemctl is-active {ftp_service}'
try:
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=10)
output = result.stdout.strip()
if output == 'active':
preFlightsChecks.stdOut("Pure-FTPd service started successfully!", 1)
# Double-check with systemctl status for more details
status_command = f'systemctl status {ftp_service} --no-pager -l'
try:
status_result = subprocess.run(status_command, shell=True, capture_output=True, text=True, timeout=10)
if status_result.returncode == 0:
preFlightsChecks.stdOut("Pure-FTPd service status verified", 1)
else:
preFlightsChecks.stdOut("Pure-FTPd service running but status check failed", 0)
except:
preFlightsChecks.stdOut("Pure-FTPd service running but status verification failed", 0)
break
else:
preFlightsChecks.stdOut(f"Pure-FTPd service status: {output} (attempt {attempt + 1})", 0)
if attempt < max_retries - 1:
preFlightsChecks.stdOut("Retrying Pure-FTPd startup...", 1)
time.sleep(5)
except subprocess.TimeoutExpired:
preFlightsChecks.stdOut(f"Pure-FTPd service status check timed out (attempt {attempt + 1})", 0)
if attempt < max_retries - 1:
time.sleep(5)
except Exception as e:
preFlightsChecks.stdOut(f"Could not verify Pure-FTPd service status (attempt {attempt + 1}): {str(e)}", 0)
if attempt < max_retries - 1:
time.sleep(5)
else:
preFlightsChecks.stdOut("[WARNING] Pure-FTPd service failed to start after all retries", 0)
# Try to get more details about the failure
command = f'systemctl status {ftp_service} --no-pager'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Enable service for auto-start (with error handling)
command = f'systemctl enable {ftp_service}'
enable_result = preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
if enable_result != 1:
preFlightsChecks.stdOut(f"Warning: Could not enable {ftp_service} for auto-start", 0)
def ensureLiteSpeedServicesRunning(self):
"""Ensure LiteSpeed services are running properly"""
preFlightsChecks.stdOut("Ensuring LiteSpeed services are running...")
# Fix LiteSpeed permissions first
self.fixLiteSpeedPermissions()
# Restart LiteSpeed services
litespeed_services = ['lsws', 'lscpd']
for service in litespeed_services:
if subprocess.run(['systemctl', 'list-unit-files'], capture_output=True, text=True).stdout.find(f'{service}.service') != -1:
preFlightsChecks.stdOut(f"Restarting {service}...")
command = f'systemctl restart {service}'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Enable service for auto-start
command = f'systemctl enable {service}'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Verify service is running
command = f'systemctl is-active {service}'
2025-08-01 14:56:30 +05:00
try:
output = subprocess.check_output(shlex.split(command)).decode("utf-8").strip()
if output == 'active':
preFlightsChecks.stdOut(f"{service} is running successfully!")
2025-08-01 14:56:30 +05:00
else:
preFlightsChecks.stdOut(f"[WARNING] {service} status: {output}")
2025-08-01 14:56:30 +05:00
except:
preFlightsChecks.stdOut(f"[WARNING] Could not verify {service} status")
def fixLiteSpeedPermissions(self):
"""Fix LiteSpeed directory permissions"""
preFlightsChecks.stdOut("Fixing LiteSpeed permissions...")
2025-08-01 14:56:30 +05:00
litespeed_dirs = ['/usr/local/lsws', '/usr/local/lscp', '/usr/local/CyberCP']
for directory in litespeed_dirs:
if os.path.exists(directory):
command = f'chown -R lscpd:lscpd {directory}'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
command = f'chmod -R 755 {directory}'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
2025-08-29 22:30:06 +05:00
def verifyCriticalServices(self):
"""Verify that all critical services are running"""
preFlightsChecks.stdOut("Verifying critical services...")
critical_services = ['lsws', 'lscpd']
all_services_ok = True
for service in critical_services:
if subprocess.run(['systemctl', 'list-unit-files'], capture_output=True, text=True).stdout.find(f'{service}.service') != -1:
command = f'systemctl is-active {service}'
2025-08-01 14:56:30 +05:00
try:
output = subprocess.check_output(shlex.split(command)).decode("utf-8").strip()
if output == 'active':
preFlightsChecks.stdOut(f"{service} is running")
2025-08-01 14:56:30 +05:00
else:
preFlightsChecks.stdOut(f"{service} is not running (status: {output})")
all_services_ok = False
2025-08-01 14:56:30 +05:00
except:
preFlightsChecks.stdOut(f"✗ Could not verify {service} status")
all_services_ok = False
if all_services_ok:
preFlightsChecks.stdOut("All critical services are running successfully!")
preFlightsChecks.stdOut("CyberPanel should now be accessible at https://your-server-ip:8090")
else:
preFlightsChecks.stdOut("[WARNING] Some critical services are not running properly")
preFlightsChecks.stdOut("Please check the logs and consider running: systemctl restart lsws lscpd")
2025-08-01 14:56:30 +05:00
def configure_jwt_secret():
try:
import secrets
secret = secrets.token_urlsafe(32)
fastapi_file = '/usr/local/CyberCP/fastapi_ssh_server.py'
with open(fastapi_file, 'r') as f:
lines = f.readlines()
with open(fastapi_file, 'w') as f:
for line in lines:
if line.strip().startswith('JWT_SECRET'):
f.write(f'JWT_SECRET = "{secret}"\n')
else:
f.write(line)
print(f"Configured JWT_SECRET in fastapi_ssh_server.py")
except:
pass
def main():
parser = argparse.ArgumentParser(description='CyberPanel Installer')
parser.add_argument('publicip', help='Please enter public IP for your VPS or dedicated server.')
parser.add_argument('--mysql', help='Specify number of MySQL instances to be used.')
parser.add_argument('--postfix', help='Enable or disable Email Service.')
parser.add_argument('--powerdns', help='Enable or disable DNS Service.')
parser.add_argument('--ftp', help='Enable or disable ftp Service.')
parser.add_argument('--ent', help='Install LS Ent or OpenLiteSpeed')
parser.add_argument('--serial', help='Install LS Ent or OpenLiteSpeed')
parser.add_argument('--port', help='LSCPD Port')
parser.add_argument('--redis', help='vHosts on Redis - Requires LiteSpeed Enterprise')
parser.add_argument('--remotemysql', help='Opt to choose local or remote MySQL')
parser.add_argument('--mysqlhost', help='MySQL host if remote is chosen.')
parser.add_argument('--mysqldb', help='MySQL DB if remote is chosen.')
parser.add_argument('--mysqluser', help='MySQL user if remote is chosen.')
parser.add_argument('--mysqlpassword', help='MySQL password if remote is chosen.')
parser.add_argument('--mysqlport', help='MySQL port if remote is chosen.')
args = parser.parse_args()
logging.InstallLog.ServerIP = args.publicip
logging.InstallLog.writeToFile("Starting CyberPanel installation..,10")
preFlightsChecks.stdOut("Starting CyberPanel installation..")
if args.ent is None:
ent = 0
preFlightsChecks.stdOut("OpenLiteSpeed web server will be installed.")
else:
if args.ent == 'ols':
ent = 0
preFlightsChecks.stdOut("OpenLiteSpeed web server will be installed.")
else:
preFlightsChecks.stdOut("LiteSpeed Enterprise web server will be installed.")
ent = 1
if args.serial is not None:
serial = args.serial
preFlightsChecks.stdOut("LiteSpeed Enterprise Serial detected: " + serial)
else:
preFlightsChecks.stdOut("Installation failed, please specify LiteSpeed Enterprise key using --serial")
os._exit(0)
## Writing public IP
try:
os.mkdir("/etc/cyberpanel")
except:
pass
machineIP = open("/etc/cyberpanel/machineIP", "w")
machineIP.writelines(args.publicip)
machineIP.close()
cwd = os.getcwd()
if args.remotemysql == 'ON':
remotemysql = args.remotemysql
mysqlhost = args.mysqlhost
mysqluser = args.mysqluser
mysqlpassword = args.mysqlpassword
mysqlport = args.mysqlport
mysqldb = args.mysqldb
if preFlightsChecks.debug:
print('mysqlhost: %s, mysqldb: %s, mysqluser: %s, mysqlpassword: %s, mysqlport: %s' % (
mysqlhost, mysqldb, mysqluser, mysqlpassword, mysqlport))
time.sleep(10)
else:
remotemysql = args.remotemysql
mysqlhost = ''
mysqluser = ''
mysqlpassword = ''
mysqlport = ''
mysqldb = ''
distro = get_distro()
checks = preFlightsChecks("/usr/local/lsws/", args.publicip, "/usr/local", cwd, "/usr/local/CyberCP", distro,
remotemysql, mysqlhost, mysqldb, mysqluser, mysqlpassword, mysqlport)
checks.mountTemp()
checks.installQuota()
if args.port is None:
port = "8090"
else:
port = args.port
if args.mysql is None:
mysql = 'One'
preFlightsChecks.stdOut("Single MySQL instance version will be installed.")
else:
mysql = args.mysql
preFlightsChecks.stdOut("Dobule MySQL instance version will be installed.")
checks.checkPythonVersion()
checks.setup_account_cyberpanel()
checks.installCyberPanelRepo()
import installCyberPanel
if ent == 0:
installCyberPanel.Main(cwd, mysql, distro, ent, None, port, args.ftp, args.powerdns, args.publicip, remotemysql,
mysqlhost, mysqldb, mysqluser, mysqlpassword, mysqlport)
else:
installCyberPanel.Main(cwd, mysql, distro, ent, serial, port, args.ftp, args.powerdns, args.publicip,
remotemysql, mysqlhost, mysqldb, mysqluser, mysqlpassword, mysqlport)
checks.setupPHPAndComposer()
checks.fix_selinux_issue()
checks.install_psmisc()
checks.fixSudoers()
if args.postfix is None:
checks.install_postfix_dovecot()
checks.setup_email_Passwords(installCyberPanel.InstallCyberPanel.mysqlPassword, mysql)
checks.setup_postfix_dovecot_config(mysql)
else:
if args.postfix == 'ON':
checks.install_postfix_dovecot()
checks.setup_email_Passwords(installCyberPanel.InstallCyberPanel.mysqlPassword, mysql)
checks.setup_postfix_dovecot_config(mysql)
checks.install_unzip()
checks.install_zip()
checks.install_rsync()
checks.installFirewalld()
checks.install_default_keys()
checks.download_install_CyberPanel(installCyberPanel.InstallCyberPanel.mysqlPassword, mysql)
checks.downoad_and_install_raindloop()
checks.download_install_phpmyadmin()
checks.setupCLI()
checks.setup_cron()
checks.installRestic()
checks.installAcme()
# Fix Django AutoField warnings
fix_django_autofield_warnings()
2025-08-01 14:56:30 +05:00
## Install and Configure OpenDKIM.
if args.postfix is None:
checks.installOpenDKIM()
checks.configureOpenDKIM()
else:
if args.postfix == 'ON':
checks.installOpenDKIM()
checks.configureOpenDKIM()
checks.modSecPreReqs()
checks.installLSCPD()
checks.setupPort()
checks.setupPythonWSGI()
checks.setupLSCPDDaemon()
checks.installDNS_CyberPanelACMEFile()
if args.redis is not None:
checks.installRedis()
if args.postfix is not None:
checks.enableDisableEmail(args.postfix.lower())
else:
preFlightsChecks.stdOut("Postfix will be installed and enabled.")
checks.enableDisableEmail('on')
if args.powerdns is not None:
checks.enableDisableDNS(args.powerdns.lower())
else:
preFlightsChecks.stdOut("PowerDNS will be installed and enabled.")
checks.enableDisableDNS('on')
if args.ftp is not None:
checks.enableDisableFTP(args.ftp.lower(), distro)
else:
preFlightsChecks.stdOut("Pure-FTPD will be installed and enabled.")
checks.enableDisableFTP('on', distro)
checks.installCLScripts()
# checks.disablePackegeUpdates()
try:
# command = 'mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/data/default/configs/'
# subprocess.call(shlex.split(command))
writeToFile = open('/usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/configs/application.ini', 'a')
writeToFile.write("""
[security]
admin_login = "admin"
admin_password = "12345"
""")
writeToFile.close()
content = r"""<?php
2025-08-01 14:56:30 +05:00
$_ENV['snappymail_INCLUDE_AS_API'] = true;
include '/usr/local/CyberCP/public/snappymail/index.php';
$oConfig = \snappymail\Api::Config();
$oConfig->SetPassword('%s');
echo $oConfig->Save() ? 'Done' : 'Error';
?>""" % (generate_pass())
writeToFile = open('/usr/local/CyberCP/public/snappymail.php', 'w')
writeToFile.write(content)
writeToFile.close()
command = '/usr/local/lsws/lsphp83/bin/php /usr/local/CyberCP/public/snappymail.php'
2025-08-01 14:56:30 +05:00
subprocess.call(shlex.split(command))
command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/snappymail/data"
subprocess.call(shlex.split(command))
2025-08-31 13:16:22 +05:00
# Ensure all data directories have group write permissions
command = "chmod -R 775 /usr/local/lscp/cyberpanel/snappymail/data"
subprocess.call(shlex.split(command))
# Ensure web server users are in the lscpd group
command = "usermod -a -G lscpd nobody 2>/dev/null || true"
subprocess.call(shlex.split(command))
2025-08-31 19:55:56 +05:00
# Fix SnappyMail public directory ownership (critical fix)
command = "chown -R lscpd:lscpd /usr/local/CyberCP/public/snappymail/data || true"
2025-08-31 13:16:22 +05:00
subprocess.call(shlex.split(command))
2025-08-01 14:56:30 +05:00
except:
pass
checks.fixCyberPanelPermissions()
configure_jwt_secret()
# Start services that were enabled but not started during installation
# These services require database tables that are created by Django migrations
checks.startDeferredServices()
# Installation summary
show_installation_summary()
2025-08-01 14:56:30 +05:00
logging.InstallLog.writeToFile("CyberPanel installation successfully completed!,80")
def show_installation_summary():
"""Display comprehensive installation summary"""
try:
import time
import subprocess
print("\n" + "="*80)
print("📊 CYBERPANEL INSTALLATION SUMMARY")
print("="*80)
# Check component status
components = {
"CyberPanel Core": check_service_status("lscpd"),
"OpenLiteSpeed": check_openlitespeed_status(),
"MariaDB/MySQL": check_service_status("mysql") or check_service_status("mariadb"),
"PowerDNS": check_powerdns_status(),
"Pure-FTPd": check_pureftpd_status(),
"Postfix": check_service_status("postfix"),
"Dovecot": check_service_status("dovecot"),
"LSMCD": check_service_status("lsmcd"),
"SnappyMail": check_file_exists("/usr/local/CyberCP/public/snappymail"),
"phpMyAdmin": check_file_exists("/usr/local/CyberCP/public/phpmyadmin")
}
print("\n🔧 COMPONENT STATUS:")
print("-" * 50)
for component, status in components.items():
if status:
print(f"{component:<20} - INSTALLED & RUNNING")
else:
print(f"{component:<20} - NOT AVAILABLE")
# System information
try:
memory_info = subprocess.check_output("free -m", shell=True).decode()
memory_line = [line for line in memory_info.split('\n') if 'Mem:' in line][0]
memory_parts = memory_line.split()
total_mem = memory_parts[1]
used_mem = memory_parts[2]
mem_percent = (int(used_mem) / int(total_mem)) * 100
disk_info = subprocess.check_output("df -h /", shell=True).decode()
disk_line = [line for line in disk_info.split('\n') if '/dev/' in line][0]
disk_parts = disk_line.split()
disk_usage = disk_parts[4]
print(f"\n📈 SYSTEM RESOURCES:")
print(f" • Memory Usage: {used_mem}MB / {total_mem}MB ({mem_percent:.1f}%)")
print(f" • Disk Usage: {disk_usage}")
print(f" • CPU Cores: {subprocess.check_output('nproc', shell=True).decode().strip()}")
except Exception as e:
print(f"\n📈 SYSTEM RESOURCES: Unable to retrieve ({str(e)})")
# Installation time
try:
if hasattr(show_installation_summary, 'start_time'):
elapsed = time.time() - show_installation_summary.start_time
minutes = int(elapsed // 60)
seconds = int(elapsed % 60)
print(f" • Install Time: {minutes}m {seconds}s")
except:
pass
print("\n" + "="*80)
except Exception as e:
print(f"\n⚠️ Could not generate installation summary: {str(e)}")
def check_service_status(service_name):
"""Check if a service is running"""
try:
result = subprocess.run(['systemctl', 'is-active', service_name],
capture_output=True, text=True)
return result.returncode == 0
except:
return False
def check_openlitespeed_status():
"""Check if OpenLiteSpeed is running (special case)"""
try:
# Check if lsws process is running
result = subprocess.run(['pgrep', '-f', 'litespeed'], capture_output=True, text=True)
if result.returncode == 0:
return True
# Check if lsws service is active
result = subprocess.run(['systemctl', 'is-active', 'lsws'], capture_output=True, text=True)
if result.returncode == 0:
return True
# Check if openlitespeed service is active
result = subprocess.run(['systemctl', 'is-active', 'openlitespeed'], capture_output=True, text=True)
if result.returncode == 0:
return True
return False
except:
return False
def check_powerdns_status():
"""Check if PowerDNS is running (special case)"""
try:
# Check if pdns process is running
result = subprocess.run(['pgrep', '-f', 'pdns'], capture_output=True, text=True)
if result.returncode == 0:
return True
# Check various PowerDNS service names
for service in ['pdns', 'pdns-server', 'powerdns']:
result = subprocess.run(['systemctl', 'is-active', service], capture_output=True, text=True)
if result.returncode == 0:
return True
return False
except:
return False
def check_pureftpd_status():
"""Check if Pure-FTPd is running (special case)"""
try:
# Check if pure-ftpd process is running
result = subprocess.run(['pgrep', '-f', 'pure-ftpd'], capture_output=True, text=True)
if result.returncode == 0:
return True
# Check various Pure-FTPd service names
for service in ['pure-ftpd', 'pureftpd']:
result = subprocess.run(['systemctl', 'is-active', service], capture_output=True, text=True)
if result.returncode == 0:
return True
return False
except:
return False
def fix_django_autofield_warnings():
"""Fix Django AutoField warnings by setting DEFAULT_AUTO_FIELD"""
try:
settings_file = '/usr/local/CyberCP/cyberpanel/settings.py'
if os.path.exists(settings_file):
with open(settings_file, 'r') as f:
content = f.read()
# Add DEFAULT_AUTO_FIELD setting if not present
if 'DEFAULT_AUTO_FIELD' not in content:
with open(settings_file, 'a') as f:
f.write('\n# Fix Django AutoField warnings\n')
f.write('DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"\n')
logging.InstallLog.writeToFile("[fix_django_autofield_warnings] Added DEFAULT_AUTO_FIELD setting")
except Exception as e:
logging.InstallLog.writeToFile(f"[ERROR] {str(e)} [fix_django_autofield_warnings]")
def check_file_exists(file_path):
"""Check if a file or directory exists"""
try:
return os.path.exists(file_path)
except:
return False
# Set installation start time
import time
show_installation_summary.start_time = time.time()
2025-08-01 14:56:30 +05:00
if __name__ == "__main__":
main()