diff --git a/cyberpanel.sh b/cyberpanel.sh index 1e527f023..1b7d9007e 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -1572,12 +1572,22 @@ if [[ "$Server_OS" =~ ^(CentOS|RHEL|AlmaLinux|RockyLinux|CloudLinux|openEuler) ] dnf clean all dnf makecache + # Note: OS-specific fixes are now handled by the Python installation process + # STEP 4: Setup MariaDB repository for RHEL 9+ based systems (AlmaLinux 9/10, RockyLinux 9, RHEL 9) if [[ "$Server_OS" =~ ^(AlmaLinux9|AlmaLinux10|RockyLinux9|RHEL9|RHEL10) ]] ; then # Use the official MariaDB repository setup script for better compatibility + log_info "Setting up MariaDB repository for $Server_OS..." curl -sS "https://downloads.mariadb.com/MariaDB/mariadb_repo_setup" | bash -s -- --mariadb-server-version="10.11" --skip-maxscale --skip-tools Check_Return "MariaDB repository setup" "no_exit" + # Verify MariaDB repository was added successfully + if dnf repolist | grep -q "mariadb"; then + log_info "MariaDB repository added successfully" + else + log_warning "MariaDB repository setup may have failed, continuing with installation" + fi + # Fallback manual repository setup if the script fails if [ ! -f /etc/yum.repos.d/mariadb.repo ]; then cat </etc/yum.repos.d/MariaDB.repo diff --git a/install/install.py b/install/install.py index a0d04b107..978621a8a 100644 --- a/install/install.py +++ b/install/install.py @@ -77,6 +77,776 @@ class preFlightsChecks: def is_debian_family(self): """Check if distro is Ubuntu or Debian 12""" return self.distro in [ubuntu, debian12] + + def detect_os_info(self): + """Detect OS information for all supported platforms""" + os_info = { + 'name': 'unknown', + 'version': 'unknown', + 'major_version': 0, + 'family': 'unknown' + } + + # Check for Ubuntu + if os.path.exists('/etc/os-release'): + with open('/etc/os-release', 'r') as f: + content = f.read() + if 'Ubuntu' in content: + os_info['family'] = 'ubuntu' + os_info['name'] = 'ubuntu' + # Extract version + for line in content.split('\n'): + if line.startswith('VERSION_ID='): + version = line.split('=')[1].strip('"') + os_info['version'] = version + os_info['major_version'] = int(version.split('.')[0]) + break + elif 'Debian' in content: + os_info['family'] = 'debian' + os_info['name'] = 'debian' + for line in content.split('\n'): + if line.startswith('VERSION_ID='): + version = line.split('=')[1].strip('"') + os_info['version'] = version + os_info['major_version'] = int(version) + break + elif 'AlmaLinux' in content: + os_info['family'] = 'rhel' + os_info['name'] = 'almalinux' + for line in content.split('\n'): + if line.startswith('VERSION_ID='): + version = line.split('=')[1].strip('"') + os_info['version'] = version + os_info['major_version'] = int(version.split('.')[0]) + break + elif 'Rocky Linux' in content: + os_info['family'] = 'rhel' + os_info['name'] = 'rocky' + for line in content.split('\n'): + if line.startswith('VERSION_ID='): + version = line.split('=')[1].strip('"') + os_info['version'] = version + os_info['major_version'] = int(version.split('.')[0]) + break + elif 'Red Hat Enterprise Linux' in content: + os_info['family'] = 'rhel' + os_info['name'] = 'rhel' + for line in content.split('\n'): + if line.startswith('VERSION_ID='): + version = line.split('=')[1].strip('"') + os_info['version'] = version + os_info['major_version'] = int(version.split('.')[0]) + break + elif 'CloudLinux' in content: + os_info['family'] = 'rhel' + os_info['name'] = 'cloudlinux' + for line in content.split('\n'): + if line.startswith('VERSION_ID='): + version = line.split('=')[1].strip('"') + os_info['version'] = version + os_info['major_version'] = int(version.split('.')[0]) + break + + # Check for CentOS (legacy) + if os.path.exists('/etc/redhat-release'): + with open('/etc/redhat-release', 'r') as f: + content = f.read() + if 'CentOS' in content: + os_info['family'] = 'rhel' + os_info['name'] = 'centos' + # Extract version from CentOS release + import re + match = re.search(r'CentOS.*?(\d+)', content) + if match: + os_info['major_version'] = int(match.group(1)) + os_info['version'] = match.group(1) + + return os_info + + def is_almalinux9(self): + """Check if running on AlmaLinux 9""" + os_info = self.detect_os_info() + return os_info['name'] == 'almalinux' and os_info['major_version'] == 9 + + def is_ubuntu(self): + """Check if running on Ubuntu""" + os_info = self.detect_os_info() + return os_info['family'] == 'ubuntu' + + def is_debian(self): + """Check if running on Debian""" + os_info = self.detect_os_info() + return os_info['family'] == 'debian' + + def is_rhel_family(self): + """Check if running on RHEL family (RHEL, AlmaLinux, Rocky, CloudLinux, CentOS)""" + os_info = self.detect_os_info() + return os_info['family'] == 'rhel' + + def get_os_specific_fixes_needed(self): + """Determine which fixes are needed for the current OS""" + os_info = self.detect_os_info() + fixes = [] + + if os_info['name'] == 'almalinux' and os_info['major_version'] == 9: + fixes.extend(['mariadb', 'services', 'litespeed', 'mysql_gpg']) + elif os_info['name'] == 'almalinux' and os_info['major_version'] == 10: + fixes.extend(['mariadb', 'services', 'litespeed']) + elif os_info['name'] == 'rocky' and os_info['major_version'] >= 8: + fixes.extend(['mariadb', 'services']) + elif os_info['name'] == 'rhel' and os_info['major_version'] >= 8: + fixes.extend(['mariadb', 'services']) + elif os_info['name'] == 'cloudlinux' and os_info['major_version'] >= 8: + fixes.extend(['mariadb', 'services']) + elif os_info['name'] == 'centos' and os_info['major_version'] == 7: + fixes.extend(['legacy_centos']) + elif os_info['family'] == 'ubuntu': + fixes.extend(['ubuntu_specific']) + elif os_info['family'] == 'debian': + fixes.extend(['debian_specific']) + + return fixes + + def fix_almalinux9_mariadb(self): + """Fix AlmaLinux 9 MariaDB installation issues""" + if not self.is_almalinux9(): + return + + self.stdOut("Applying AlmaLinux 9 MariaDB fixes...", 1) + + # Apply mesa package fixes for AlmaLinux 9 + self.stdOut("Applying AlmaLinux 9 mesa package fixes...", 1) + try: + # Remove potentially conflicting mesa packages + command = "dnf remove -y mesa-* 2>/dev/null || true" + self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + + # Install mesa packages with conflict resolution + command = "dnf install -y --allowerasing mesa-dri-drivers mesa-filesystem mesa-libEGL mesa-libGL mesa-libgbm mesa-libglapi mesa-libglapi-devel mesa-libGL-devel mesa-libEGL-devel libdrm libglvnd libglvnd-glx libglvnd-egl" + self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + + self.stdOut("AlmaLinux 9 mesa package fixes applied successfully", 1) + except Exception as e: + self.stdOut(f"Warning: Mesa package fixes failed: {str(e)}", 1) + + try: + # Disable problematic MariaDB MaxScale repository + self.stdOut("Disabling problematic MariaDB MaxScale repository...", 1) + command = "dnf config-manager --disable mariadb-maxscale 2>/dev/null || true" + self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + + # Remove problematic repository files + self.stdOut("Removing problematic repository files...", 1) + problematic_repos = [ + '/etc/yum.repos.d/mariadb-maxscale.repo', + '/etc/yum.repos.d/mariadb-maxscale.repo.rpmnew' + ] + for repo_file in problematic_repos: + if os.path.exists(repo_file): + os.remove(repo_file) + self.stdOut(f"Removed {repo_file}", 1) + + # Clean DNF cache + self.stdOut("Cleaning DNF cache...", 1) + command = "dnf clean all" + self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + + # Install MariaDB from official repository using shell=True for pipe commands + self.stdOut("Setting up official MariaDB repository...", 1) + command = "curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash -s -- --mariadb-server-version='10.11'" + self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, shell=True) + + # Install MariaDB packages - use correct package names for AlmaLinux 9 + self.stdOut("Installing MariaDB packages...", 1) + # Try different package combinations for AlmaLinux 9 + mariadb_packages_attempts = [ + "mariadb-server mariadb-devel mariadb-client", + "mariadb-server mariadb-devel", + "mariadb-server mariadb-devel mariadb-common" + ] + + installed = False + for packages in mariadb_packages_attempts: + command = f"dnf install -y {packages}" + result = self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + if result == 0: + self.stdOut(f"Successfully installed MariaDB packages: {packages}", 1) + installed = True + break + else: + self.stdOut(f"Failed to install packages: {packages}, trying next combination...", 1) + + if not installed: + self.stdOut("Warning: Some MariaDB packages may not have installed correctly", 0) + + # Check if MariaDB service exists and create it if needed + self.stdOut("Checking MariaDB service configuration...", 1) + if not os.path.exists('/usr/lib/systemd/system/mariadb.service'): + # Try to find the correct service file + possible_services = [ + '/usr/lib/systemd/system/mysqld.service', + '/usr/lib/systemd/system/mysql.service' + ] + + for service_file in possible_services: + if os.path.exists(service_file): + self.stdOut(f"Found service file: {service_file}", 1) + # Create symlink to mariadb.service + try: + os.symlink(service_file, '/usr/lib/systemd/system/mariadb.service') + self.stdOut("Created symlink for mariadb.service", 1) + except OSError as e: + self.stdOut(f"Could not create symlink: {str(e)}", 0) + break + + self.stdOut("AlmaLinux 9 MariaDB fixes completed", 1) + + except Exception as e: + self.stdOut(f"Error applying AlmaLinux 9 MariaDB fixes: {str(e)}", 0) + + def fix_rhel_family_common(self): + """Fix common RHEL family (AlmaLinux, Rocky, RHEL, CloudLinux) issues""" + try: + self.stdOut("Applying RHEL family common fixes...", 1) + + # Install EPEL repository + self.stdOut("Installing EPEL repository...", 1) + try: + self.call('dnf install -y epel-release', self.distro, 'Installing EPEL', 'Installing EPEL', 1, 1, os.EX_OSERR) + except: + try: + self.call('yum install -y epel-release', self.distro, 'Installing EPEL (yum)', 'Installing EPEL (yum)', 1, 1, os.EX_OSERR) + except: + self.stdOut("Warning: Could not install EPEL", 1) + + # Apply graphics library fixes for RHEL 9+ systems + os_info = self.detect_os_info() + if os_info['major_version'] >= 9: + self.stdOut("Applying RHEL 9+ graphics library fixes...", 1) + try: + command = "dnf install -y --allowerasing mesa-dri-drivers mesa-filesystem mesa-libEGL mesa-libGL mesa-libgbm mesa-libglapi libdrm libglvnd libglvnd-glx libglvnd-egl" + self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + self.stdOut("RHEL 9+ graphics library fixes applied successfully", 1) + except Exception as e: + self.stdOut(f"Warning: Graphics library fixes failed: {str(e)}", 1) + + # Install common dependencies + self.stdOut("Installing common RHEL dependencies...", 1) + common_deps = [ + 'curl', + 'wget', + 'git', + 'python3', + 'python3-pip', + 'gcc', + 'gcc-c++', + 'make', + 'cmake', + 'pcre2-devel', + 'openssl-devel', + 'zlib-devel' + ] + + for dep in common_deps: + try: + self.call(f'dnf install -y {dep}', self.distro, f'Installing {dep}', f'Installing {dep}', 1, 0, os.EX_OSERR) + except: + try: + self.call(f'yum install -y {dep}', self.distro, f'Installing {dep} (yum)', f'Installing {dep} (yum)', 1, 0, os.EX_OSERR) + except: + self.stdOut(f"Warning: Could not install {dep}", 1) + + return True + + except Exception as e: + self.stdOut(f"Error in fix_rhel_family_common: {str(e)}", 0) + return False + + def fix_ubuntu_specific(self): + """Fix Ubuntu-specific installation issues""" + try: + self.stdOut("Applying Ubuntu-specific fixes...", 1) + + # Install required dependencies + self.stdOut("Installing Ubuntu dependencies...", 1) + ubuntu_deps = [ + 'software-properties-common', + 'apt-transport-https', + 'curl', + 'wget', + 'gnupg', + 'lsb-release' + ] + + for dep in ubuntu_deps: + try: + self.call(f'apt-get install -y {dep}', self.distro, f'Installing {dep}', f'Installing {dep}', 1, 0, os.EX_OSERR) + except: + self.stdOut(f"Warning: Could not install {dep}", 1) + + # Ubuntu 24.04 specific fixes + os_info = self.detect_os_info() + if os_info['name'] == 'ubuntu' and os_info['major_version'] >= 24: + self.stdOut("Applying Ubuntu 24.04 specific fixes...", 1) + try: + command = "DEBIAN_FRONTEND=noninteractive apt-get install -y python3-venv python3-dev" + self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + self.stdOut("Ubuntu 24.04 specific fixes applied successfully", 1) + except Exception as e: + self.stdOut(f"Warning: Ubuntu 24.04 fixes failed: {str(e)}", 1) + + # Update package lists + self.call('apt-get update', self.distro, 'Updating package lists', 'Updating package lists', 1, 1, os.EX_OSERR) + + return True + + except Exception as e: + self.stdOut(f"Error in fix_ubuntu_specific: {str(e)}", 0) + return False + + def fix_debian_specific(self): + """Fix Debian-specific installation issues""" + try: + self.stdOut("Applying Debian-specific fixes...", 1) + + # Install required dependencies + self.stdOut("Installing Debian dependencies...", 1) + debian_deps = [ + 'software-properties-common', + 'apt-transport-https', + 'curl', + 'wget', + 'gnupg', + 'lsb-release' + ] + + for dep in debian_deps: + try: + self.call(f'apt-get install -y {dep}', self.distro, f'Installing {dep}', f'Installing {dep}', 1, 0, os.EX_OSERR) + except: + self.stdOut(f"Warning: Could not install {dep}", 1) + + # Debian 13 specific fixes + os_info = self.detect_os_info() + if os_info['name'] == 'debian' and os_info['major_version'] >= 13: + self.stdOut("Applying Debian 13 specific fixes...", 1) + try: + command = "DEBIAN_FRONTEND=noninteractive apt-get install -y python3-venv python3-dev build-essential" + self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + self.stdOut("Debian 13 specific fixes applied successfully", 1) + except Exception as e: + self.stdOut(f"Warning: Debian 13 fixes failed: {str(e)}", 1) + + # Update package lists + self.call('apt-get update', self.distro, 'Updating package lists', 'Updating package lists', 1, 1, os.EX_OSERR) + + return True + + except Exception as e: + self.stdOut(f"Error in fix_debian_specific: {str(e)}", 0) + return False + + def apply_os_specific_fixes(self): + """Apply OS-specific fixes based on detected OS""" + try: + os_info = self.detect_os_info() + fixes_needed = self.get_os_specific_fixes_needed() + + self.stdOut(f"Detected OS: {os_info['name']} {os_info['version']} (family: {os_info['family']})", 1) + self.stdOut(f"Applying fixes: {', '.join(fixes_needed)}", 1) + + # Apply common RHEL family fixes first + if self.is_rhel_family(): + self.fix_rhel_family_common() + + # Apply specific fixes + for fix in fixes_needed: + if fix == 'mariadb' and self.is_almalinux9(): + self.fix_almalinux9_mariadb() + elif fix == 'ubuntu_specific' and self.is_ubuntu(): + self.fix_ubuntu_specific() + elif fix == 'debian_specific' and self.is_debian(): + self.fix_debian_specific() + + self.stdOut("OS-specific fixes completed successfully", 1) + return True + + except Exception as e: + self.stdOut(f"Error applying OS-specific fixes: {str(e)}", 0) + return False + + def installLiteSpeed(self, ent, serial): + """Install LiteSpeed Web Server (OpenLiteSpeed or Enterprise)""" + try: + self.stdOut("Installing LiteSpeed Web Server...", 1) + + if ent == 0: + # Install OpenLiteSpeed + self.stdOut("Installing OpenLiteSpeed...", 1) + if self.distro == ubuntu: + self.install_package('openlitespeed') + else: + self.install_package('openlitespeed') + + # Configure OpenLiteSpeed + self.fix_ols_configs() + self.changePortTo80() + else: + # Install LiteSpeed Enterprise + self.stdOut("Installing LiteSpeed Enterprise...", 1) + self.installLiteSpeedEnterprise(serial) + + self.stdOut("LiteSpeed Web Server installed successfully", 1) + return True + + except Exception as e: + self.stdOut(f"Error installing LiteSpeed: {str(e)}", 0) + return False + + def installLiteSpeedEnterprise(self, serial): + """Install LiteSpeed Enterprise""" + try: + # Get the latest LSWS Enterprise version + lsws_version = self.getLatestLSWSVersion() + + # Download and install LiteSpeed Enterprise + if self.ISARM(): + command = f'wget https://www.litespeedtech.com/packages/6.0/lsws-{lsws_version}-ent-aarch64-linux.tar.gz' + else: + command = f'wget https://www.litespeedtech.com/packages/6.0/lsws-{lsws_version}-ent-x86_64-linux.tar.gz' + + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + + if self.ISARM(): + command = f'tar zxf lsws-{lsws_version}-ent-aarch64-linux.tar.gz' + else: + command = f'tar zxf lsws-{lsws_version}-ent-x86_64-linux.tar.gz' + + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + + # Handle license + if str.lower(serial) == 'trial': + command = f'wget -q --output-document=lsws-{lsws_version}/trial.key http://license.litespeedtech.com/reseller/trial.key' + elif serial == '1111-2222-3333-4444': + command = f'wget -q --output-document=/root/cyberpanel/install/lsws-{lsws_version}/trial.key http://license.litespeedtech.com/reseller/trial.key' + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + else: + writeSerial = open(f'lsws-{lsws_version}/serial.no', 'w') + writeSerial.writelines(serial) + writeSerial.close() + + # Install LiteSpeed Enterprise + os.chdir(f'lsws-{lsws_version}') + command = 'chmod +x install.sh' + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + command = 'chmod +x functions.sh' + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + command = './install.sh' + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + os.chdir(self.cwd) + + return True + + except Exception as e: + self.stdOut(f"Error installing LiteSpeed Enterprise: {str(e)}", 0) + return False + + def getLatestLSWSVersion(self): + """Fetch the latest LSWS Enterprise version""" + try: + import urllib.request + import re + url = "https://www.litespeedtech.com/products/litespeed-web-server/download" + req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) + with urllib.request.urlopen(req, timeout=10) as response: + html = response.read().decode('utf-8') + + version_pattern = r'lsws-(\d+\.\d+\.\d+)-ent' + versions = re.findall(version_pattern, html) + + if versions: + latest_version = sorted(versions, key=lambda v: [int(x) for x in v.split('.')])[-1] + return latest_version + except: + pass + + return "6.3.4" # Fallback version + + def ISARM(self): + """Check if running on ARM architecture""" + try: + command = 'uname -a' + result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) + return 'aarch64' in result.stdout + except: + return False + + def installMySQL(self, mysql): + """Install MySQL/MariaDB""" + try: + self.stdOut("Installing MySQL/MariaDB...", 1) + + if self.distro == ubuntu: + # Ubuntu MariaDB installation + command = 'DEBIAN_FRONTEND=noninteractive apt-get install software-properties-common apt-transport-https curl -y' + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + + command = "mkdir -p /etc/apt/keyrings" + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + + command = "curl -o /etc/apt/keyrings/mariadb-keyring.pgp 'https://mariadb.org/mariadb_release_signing_key.pgp'" + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + + # Setup MariaDB repository + command = 'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=10.11' + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + + command = 'DEBIAN_FRONTEND=noninteractive apt-get update -y' + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + + command = "DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server -y" + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + + else: + # RHEL-based MariaDB installation + command = 'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=10.11' + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + + command = 'dnf install mariadb-server mariadb-devel mariadb-client-utils -y' + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + + # Start and enable MariaDB + self.startMariaDB() + self.changeMYSQLRootPassword() + self.fixMariaDB() + + self.stdOut("MySQL/MariaDB installed successfully", 1) + return True + + except Exception as e: + self.stdOut(f"Error installing MySQL/MariaDB: {str(e)}", 0) + return False + + def startMariaDB(self): + """Start MariaDB service""" + try: + self.stdOut("Starting MariaDB service...", 1) + self.manage_service('mariadb', 'start') + self.manage_service('mariadb', 'enable') + return True + except Exception as e: + self.stdOut(f"Error starting MariaDB: {str(e)}", 0) + return False + + def changeMYSQLRootPassword(self): + """Change MySQL root password""" + try: + if self.remotemysql == 'OFF': + passwordCMD = "use mysql;DROP DATABASE IF EXISTS test;DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%%';GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY '%s';flush privileges;" % (self.mysql_Root_password) + + mysql_commands = ['mysql', 'mariadb', '/usr/bin/mysql', '/usr/bin/mariadb'] + mysql_cmd = None + + for cmd in mysql_commands: + if os.path.exists(cmd) or self.command_exists(cmd): + mysql_cmd = cmd + break + + if mysql_cmd: + command = f'{mysql_cmd} -u root -e "{passwordCMD}"' + self.call(command, self.distro, command, command, 0, 0, os.EX_OSERR) + + except Exception as e: + self.stdOut(f"Error changing MySQL root password: {str(e)}", 0) + + def command_exists(self, command): + """Check if a command exists in PATH""" + try: + result = subprocess.run(['which', command], capture_output=True, text=True) + return result.returncode == 0 + except: + return False + + def fixMariaDB(self): + """Configure MariaDB for CyberPanel""" + try: + self.stdOut("Configuring MariaDB for CyberPanel...", 1) + + # Connect to MySQL and configure + import MySQLdb as mariadb + conn = mariadb.connect(user='root', passwd=self.mysql_Root_password) + cursor = conn.cursor() + cursor.execute('set global innodb_file_per_table = on;') + try: + cursor.execute('set global innodb_file_format = Barracuda;') + cursor.execute('set global innodb_large_prefix = on;') + except: + pass + cursor.close() + conn.close() + + # Update MariaDB configuration + try: + fileName = '/etc/mysql/mariadb.conf.d/50-server.cnf' + if os.path.exists(fileName): + with open(fileName, 'r') as f: + data = f.readlines() + + with open(fileName, 'w') as f: + for line in data: + f.write(line.replace('utf8mb4', 'utf8')) + except: + pass + + # Restart MariaDB + self.manage_service('mariadb', 'restart') + + self.stdOut("MariaDB configured successfully", 1) + return True + + except Exception as e: + self.stdOut(f"Error configuring MariaDB: {str(e)}", 0) + return False + + def installPowerDNS(self): + """Install PowerDNS""" + try: + self.stdOut("Installing PowerDNS...", 1) + + if self.distro == ubuntu: + # Stop and disable systemd-resolved + self.manage_service('systemd-resolved', 'stop') + self.manage_service('systemd-resolved.service', 'disable') + + # Create temporary resolv.conf + try: + os.rename('/etc/resolv.conf', '/etc/resolv.conf.bak') + except: + pass + + with open('/etc/resolv.conf', 'w') as f: + f.write('nameserver 8.8.8.8\n') + f.write('nameserver 8.8.4.4\n') + + # Install PowerDNS + command = "DEBIAN_FRONTEND=noninteractive apt-get update" + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + + command = "DEBIAN_FRONTEND=noninteractive apt-get -y install pdns-server pdns-backend-mysql" + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + + command = 'systemctl stop pdns || true' + self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, True) + + else: + # RHEL-based PowerDNS installation + self.install_package('pdns pdns-backend-mysql') + + self.stdOut("PowerDNS installed successfully", 1) + return True + + except Exception as e: + self.stdOut(f"Error installing PowerDNS: {str(e)}", 0) + return False + + def installPureFTPD(self): + """Install Pure-FTPd""" + try: + self.stdOut("Installing Pure-FTPd...", 1) + + if self.distro == ubuntu: + self.install_package('pure-ftpd-mysql') + else: + self.install_package('pure-ftpd') + + # Enable service + service_name = self.pureFTPDServiceName(self.distro) + command = f"systemctl enable {service_name}" + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + + # Create FTP groups and users + command = 'groupadd -g 2001 ftpgroup' + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + + command = 'useradd -u 2001 -s /bin/false -d /bin/null -c "pureftpd user" -g ftpgroup ftpuser' + self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + + self.stdOut("Pure-FTPd installed successfully", 1) + return True + + except Exception as e: + self.stdOut(f"Error installing Pure-FTPd: {str(e)}", 0) + return False + + def fix_ols_configs(self): + """Fix OpenLiteSpeed configurations""" + try: + self.stdOut("Fixing OpenLiteSpeed configurations...", 1) + + # Remove example virtual host + data = open(self.server_root_path + "conf/httpd_config.conf", 'r').readlines() + writeDataToFile = open(self.server_root_path + "conf/httpd_config.conf", 'w') + + for items in data: + if items.find("map") > -1 and items.find("Example") > -1: + continue + else: + writeDataToFile.writelines(items) + + writeDataToFile.close() + + self.stdOut("OpenLiteSpeed configurations fixed", 1) + return True + + except Exception as e: + self.stdOut(f"Error fixing OpenLiteSpeed configs: {str(e)}", 0) + return False + + def changePortTo80(self): + """Change OpenLiteSpeed port to 80""" + try: + self.stdOut("Changing OpenLiteSpeed port to 80...", 1) + + file_path = self.server_root_path + "conf/httpd_config.conf" + if self.modify_file_content(file_path, {"*:8088": "*:80"}): + self.stdOut("OpenLiteSpeed port changed to 80", 1) + self.reStartLiteSpeed() + return True + else: + return False + + except Exception as e: + self.stdOut(f"Error changing port to 80: {str(e)}", 0) + return False + + def modify_file_content(self, file_path, replacements): + """Generic file content modification""" + try: + with open(file_path, 'r') as f: + data = f.readlines() + + with open(file_path, 'w') as f: + for line in data: + modified_line = line + for old, new in replacements.items(): + if old in line: + modified_line = line.replace(old, new) + break + f.write(modified_line) + return True + except Exception as e: + self.stdOut(f"Error modifying file {file_path}: {str(e)}", 0) + return False + + def reStartLiteSpeed(self): + """Restart LiteSpeed""" + try: + command = f"{self.server_root_path}bin/lswsctrl restart" + self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + return True + except Exception as e: + self.stdOut(f"Error restarting LiteSpeed: {str(e)}", 0) + return False def get_service_name(self, service): """Get the correct service name for the current distribution""" @@ -137,6 +907,8 @@ class preFlightsChecks: self.mysqlpassword = mysqlpassword self.mysqlport = mysqlport self.mysqldb = mysqldb + self.cyberpanel_db_password = None # Will be set during password generation + self.mysql_Root_password = install_utils.generate_pass() # Generate MySQL root password def installQuota(self,): @@ -715,16 +1487,14 @@ password="%s" # 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 + # On CentOS, generate a separate password for cyberpanel database + self.cyberpanel_db_password = install_utils.generate_pass() else: # On Ubuntu/Debian, the cyberpanel password is the same as root password - cyberpanel_db_password = password + self.cyberpanel_db_password = password # Generate secure environment file with correct passwords - self.generate_secure_env_file(mysqlPassword, cyberpanel_db_password) + self.generate_secure_env_file(mysqlPassword, self.cyberpanel_db_password) logging.InstallLog.writeToFile("Environment configuration generated successfully!") @@ -3854,6 +4624,10 @@ def main(): distro = get_distro() checks = preFlightsChecks("/usr/local/lsws/", args.publicip, "/usr/local", cwd, "/usr/local/CyberCP", distro, remotemysql, mysqlhost, mysqldb, mysqluser, mysqlpassword, mysqlport) + + # Apply OS-specific fixes early in the installation process + checks.apply_os_specific_fixes() + checks.mountTemp() checks.installQuota() @@ -3873,14 +4647,14 @@ def main(): checks.setup_account_cyberpanel() checks.installCyberPanelRepo() - import installCyberPanel + # Note: OS-specific fixes are now applied earlier in the installation process + # The installCyberPanel.Main() functionality has been integrated into the main installation flow - 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) + # Install core services in the correct order + checks.installLiteSpeed(ent, serial) + checks.installMySQL(mysql) + checks.installPowerDNS() + checks.installPureFTPD() checks.setupPHPAndComposer() checks.fix_selinux_issue() @@ -3889,12 +4663,12 @@ def main(): if args.postfix is None: checks.install_postfix_dovecot() - checks.setup_email_Passwords(installCyberPanel.InstallCyberPanel.mysqlPassword, mysql) + checks.setup_email_Passwords(checks.cyberpanel_db_password, 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_email_Passwords(checks.cyberpanel_db_password, mysql) checks.setup_postfix_dovecot_config(mysql) checks.install_unzip() @@ -3904,7 +4678,7 @@ def main(): checks.installFirewalld() checks.install_default_keys() - checks.download_install_CyberPanel(installCyberPanel.InstallCyberPanel.mysqlPassword, mysql) + checks.download_install_CyberPanel(checks.cyberpanel_db_password, mysql) checks.downoad_and_install_raindloop() checks.download_install_phpmyadmin() checks.setupCLI() diff --git a/install/installCyberPanel.py b/install/installCyberPanel.py deleted file mode 100644 index c2e6e04ca..000000000 --- a/install/installCyberPanel.py +++ /dev/null @@ -1,1947 +0,0 @@ -import shutil -import subprocess -import os -from mysqlUtilities import mysqlUtilities -import installLog as logging -import errno -import MySQLdb as mariadb -import install -from os.path import exists -import time -import install_utils -import urllib.request -import re - -# distros - using from install_utils -centos = install_utils.centos -ubuntu = install_utils.ubuntu -cent8 = install_utils.cent8 -openeuler = install_utils.openeuler -debian12 = install_utils.debian12 - - -def get_Ubuntu_release(): - return install_utils.get_Ubuntu_release(use_print=True, exit_on_error=True) - - -def get_Ubuntu_code_name(): - """Get Ubuntu codename based on version""" - release = get_Ubuntu_release() - if release >= 24.04: - return "noble" - elif release >= 22.04: - return "jammy" - elif release >= 20.04: - return "focal" - elif release >= 18.04: - return "bionic" - else: - return "xenial" - - -# Using shared function from install_utils -FetchCloudLinuxAlmaVersionVersion = install_utils.FetchCloudLinuxAlmaVersionVersion - -class InstallCyberPanel: - mysql_Root_password = "" - mysqlPassword = "" - - def detect_os_info(self): - """Detect OS information for all supported platforms""" - os_info = { - 'name': 'unknown', - 'version': 'unknown', - 'major_version': 0, - 'family': 'unknown' - } - - # Check for Ubuntu - if os.path.exists('/etc/os-release'): - with open('/etc/os-release', 'r') as f: - content = f.read() - if 'Ubuntu' in content: - os_info['family'] = 'ubuntu' - os_info['name'] = 'ubuntu' - # Extract version - for line in content.split('\n'): - if line.startswith('VERSION_ID='): - version = line.split('=')[1].strip('"') - os_info['version'] = version - os_info['major_version'] = int(version.split('.')[0]) - break - elif 'Debian' in content: - os_info['family'] = 'debian' - os_info['name'] = 'debian' - for line in content.split('\n'): - if line.startswith('VERSION_ID='): - version = line.split('=')[1].strip('"') - os_info['version'] = version - os_info['major_version'] = int(version) - break - elif 'AlmaLinux' in content: - os_info['family'] = 'rhel' - os_info['name'] = 'almalinux' - for line in content.split('\n'): - if line.startswith('VERSION_ID='): - version = line.split('=')[1].strip('"') - os_info['version'] = version - os_info['major_version'] = int(version.split('.')[0]) - break - elif 'Rocky Linux' in content: - os_info['family'] = 'rhel' - os_info['name'] = 'rocky' - for line in content.split('\n'): - if line.startswith('VERSION_ID='): - version = line.split('=')[1].strip('"') - os_info['version'] = version - os_info['major_version'] = int(version.split('.')[0]) - break - elif 'Red Hat Enterprise Linux' in content: - os_info['family'] = 'rhel' - os_info['name'] = 'rhel' - for line in content.split('\n'): - if line.startswith('VERSION_ID='): - version = line.split('=')[1].strip('"') - os_info['version'] = version - os_info['major_version'] = int(version.split('.')[0]) - break - elif 'CloudLinux' in content: - os_info['family'] = 'rhel' - os_info['name'] = 'cloudlinux' - for line in content.split('\n'): - if line.startswith('VERSION_ID='): - version = line.split('=')[1].strip('"') - os_info['version'] = version - os_info['major_version'] = int(version.split('.')[0]) - break - - # Check for CentOS (legacy) - if os.path.exists('/etc/redhat-release'): - with open('/etc/redhat-release', 'r') as f: - content = f.read() - if 'CentOS' in content: - os_info['family'] = 'rhel' - os_info['name'] = 'centos' - # Extract version from CentOS release - import re - match = re.search(r'CentOS.*?(\d+)', content) - if match: - os_info['major_version'] = int(match.group(1)) - os_info['version'] = match.group(1) - - return os_info - - def is_almalinux9(self): - """Check if running on AlmaLinux 9""" - os_info = self.detect_os_info() - return os_info['name'] == 'almalinux' and os_info['major_version'] == 9 - - def is_ubuntu(self): - """Check if running on Ubuntu""" - os_info = self.detect_os_info() - return os_info['family'] == 'ubuntu' - - def is_debian(self): - """Check if running on Debian""" - os_info = self.detect_os_info() - return os_info['family'] == 'debian' - - def is_rhel_family(self): - """Check if running on RHEL family (RHEL, AlmaLinux, Rocky, CloudLinux, CentOS)""" - os_info = self.detect_os_info() - return os_info['family'] == 'rhel' - - def get_os_specific_fixes_needed(self): - """Determine which fixes are needed for the current OS""" - os_info = self.detect_os_info() - fixes = [] - - if os_info['name'] == 'almalinux' and os_info['major_version'] == 9: - fixes.extend(['mariadb', 'services', 'litespeed', 'mysql_gpg']) - elif os_info['name'] == 'almalinux' and os_info['major_version'] == 10: - fixes.extend(['mariadb', 'services', 'litespeed']) - elif os_info['name'] == 'rocky' and os_info['major_version'] >= 8: - fixes.extend(['mariadb', 'services']) - elif os_info['name'] == 'rhel' and os_info['major_version'] >= 8: - fixes.extend(['mariadb', 'services']) - elif os_info['name'] == 'cloudlinux' and os_info['major_version'] >= 8: - fixes.extend(['mariadb', 'services']) - elif os_info['name'] == 'centos' and os_info['major_version'] == 7: - fixes.extend(['legacy_centos']) - elif os_info['family'] == 'ubuntu': - fixes.extend(['ubuntu_specific']) - elif os_info['family'] == 'debian': - fixes.extend(['debian_specific']) - - return fixes - - def fix_almalinux9_mariadb(self): - """Fix AlmaLinux 9 MariaDB installation issues""" - if not self.is_almalinux9(): - return - - self.stdOut("Applying AlmaLinux 9 MariaDB fixes...", 1) - - try: - # Disable problematic MariaDB MaxScale repository - self.stdOut("Disabling problematic MariaDB MaxScale repository...", 1) - command = "dnf config-manager --disable mariadb-maxscale 2>/dev/null || true" - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - # Remove problematic repository files - self.stdOut("Removing problematic repository files...", 1) - problematic_repos = [ - '/etc/yum.repos.d/mariadb-maxscale.repo', - '/etc/yum.repos.d/mariadb-maxscale.repo.rpmnew' - ] - for repo_file in problematic_repos: - if os.path.exists(repo_file): - os.remove(repo_file) - self.stdOut(f"Removed {repo_file}", 1) - - # Clean DNF cache - self.stdOut("Cleaning DNF cache...", 1) - command = "dnf clean all" - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - # Install MariaDB from official repository using shell=True for pipe commands - self.stdOut("Setting up official MariaDB repository...", 1) - command = "curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash -s -- --mariadb-server-version='10.11'" - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, shell=True) - - # Install MariaDB packages - use correct package names for AlmaLinux 9 - self.stdOut("Installing MariaDB packages...", 1) - # Try different package combinations for AlmaLinux 9 - mariadb_packages_attempts = [ - "mariadb-server mariadb-devel mariadb-client", - "mariadb-server mariadb-devel", - "mariadb-server mariadb-devel mariadb-common" - ] - - installed = False - for packages in mariadb_packages_attempts: - command = f"dnf install -y {packages}" - result = install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - if result == 0: - self.stdOut(f"Successfully installed MariaDB packages: {packages}", 1) - installed = True - break - else: - self.stdOut(f"Failed to install packages: {packages}, trying next combination...", 1) - - if not installed: - self.stdOut("Warning: Some MariaDB packages may not have installed correctly", 0) - - # Check if MariaDB service exists and create it if needed - self.stdOut("Checking MariaDB service configuration...", 1) - if not os.path.exists('/usr/lib/systemd/system/mariadb.service'): - # Try to find the correct service file - possible_services = [ - '/usr/lib/systemd/system/mysqld.service', - '/usr/lib/systemd/system/mysql.service' - ] - - for service_file in possible_services: - if os.path.exists(service_file): - self.stdOut(f"Found service file: {service_file}", 1) - # Create symlink to mariadb.service - try: - os.symlink(service_file, '/usr/lib/systemd/system/mariadb.service') - self.stdOut("Created symlink for mariadb.service", 1) - except OSError as e: - self.stdOut(f"Could not create symlink: {str(e)}", 0) - break - - self.stdOut("AlmaLinux 9 MariaDB fixes completed", 1) - - except Exception as e: - self.stdOut(f"Error applying AlmaLinux 9 MariaDB fixes: {str(e)}", 0) - - def fix_almalinux9_services(self): - """Fix service installation and configuration issues on AlmaLinux 9""" - try: - self.stdOut("Fixing AlmaLinux 9 service issues...", 1) - - # Install PowerDNS - self.stdOut("Installing PowerDNS...", 1) - try: - install_utils.call('dnf install -y pdns pdns-backend-mysql', self.distro, 'Installing PowerDNS', 'Installing PowerDNS', 1, 1, os.EX_OSERR) - except: - self.stdOut("Warning: PowerDNS installation failed, trying alternative...", 1) - try: - install_utils.call('dnf install -y pdns pdns-backend-mysql --skip-broken', self.distro, 'Installing PowerDNS (skip-broken)', 'Installing PowerDNS (skip-broken)', 1, 1, os.EX_OSERR) - except: - pass - - # Install Pure-FTPd - self.stdOut("Installing Pure-FTPd...", 1) - try: - install_utils.call('dnf install -y pure-ftpd pure-ftpd-mysql', self.distro, 'Installing Pure-FTPd', 'Installing Pure-FTPd', 1, 1, os.EX_OSERR) - except: - self.stdOut("Warning: Pure-FTPd installation failed, trying alternative...", 1) - try: - install_utils.call('dnf install -y pure-ftpd pure-ftpd-mysql --skip-broken', self.distro, 'Installing Pure-FTPd (skip-broken)', 'Installing Pure-FTPd (skip-broken)', 1, 1, os.EX_OSERR) - except: - pass - - # Install Dovecot23 with dependencies - self.stdOut("Installing Dovecot23...", 1) - try: - # First install clucene-core if not already installed - install_utils.call('dnf install -y clucene-core clucene-shared', self.distro, 'Installing clucene dependencies', 'Installing clucene dependencies', 1, 1, os.EX_OSERR) - - # Then install dovecot23 - install_utils.call('dnf install -y dovecot23 dovecot23-mysql --enablerepo=gf-plus', self.distro, 'Installing Dovecot23', 'Installing Dovecot23', 1, 1, os.EX_OSERR) - except: - self.stdOut("Warning: Dovecot23 installation failed, trying alternative...", 1) - try: - install_utils.call('dnf install -y dovecot23 dovecot23-mysql --enablerepo=gf-plus --skip-broken', self.distro, 'Installing Dovecot23 (skip-broken)', 'Installing Dovecot23 (skip-broken)', 1, 1, os.EX_OSERR) - except: - pass - - # Create required directories - self.stdOut("Creating required directories...", 1) - directories = [ - '/usr/local/lsws', - '/usr/local/lsws/cyberpanel-tmp', - '/usr/local/lsws/conf', - '/usr/local/lsws/logs' - ] - - for directory in directories: - try: - os.makedirs(directory, exist_ok=True) - self.stdOut(f"Created directory: {directory}", 1) - except Exception as e: - self.stdOut(f"Warning: Could not create directory {directory}: {str(e)}", 1) - - # Set proper ownership for directories - try: - subprocess.run(['chown', '-R', 'newst3922:nobody', '/usr/local/lsws'], check=False) - except: - pass - - return True - - except Exception as e: - self.stdOut(f"Error in fix_almalinux9_services: {str(e)}", 0) - return False - - def fix_almalinux9_litespeed(self): - """Fix OpenLiteSpeed installation issues on AlmaLinux 9""" - try: - self.stdOut("Fixing OpenLiteSpeed installation...", 1) - - # Install required build dependencies - self.stdOut("Installing build dependencies...", 1) - build_deps = [ - 'gcc', - 'gcc-c++', - 'make', - 'cmake', - 'pcre2-devel', - 'openssl-devel', - 'zlib-devel', - 'libxml2-devel', - 'libcurl-devel', - 'libpng-devel', - 'libjpeg-turbo-devel', - 'freetype-devel', - 'libXpm-devel', - 'libX11-devel', - 'libXext-devel', - 'libXrender-devel', - 'libXrandr-devel', - 'libXinerama-devel', - 'libXi-devel', - 'libXt-devel', - 'libXmu-devel', - 'libXaw-devel', - 'libXfixes-devel', - 'libXdamage-devel', - 'libXcomposite-devel', - 'libXcursor-devel', - 'libXxf86vm-devel', - 'libXv-devel', - 'libXtst-devel', - 'libXss-devel', - 'libXxf86dga-devel', - 'libXxf86misc-devel' - ] - - for dep in build_deps: - try: - install_utils.call(f'dnf install -y {dep}', self.distro, f'Installing {dep}', f'Installing {dep}', 1, 0, os.EX_OSERR) - except: - self.stdOut(f"Warning: Could not install {dep}", 1) - - # Download and install OpenLiteSpeed - self.stdOut("Downloading OpenLiteSpeed...", 1) - try: - install_utils.call('wget -O /tmp/openlitespeed.rpm https://openlitespeed.org/packages/openlitespeed-1.7.18-1.x86_64.rpm', self.distro, 'Downloading OpenLiteSpeed', 'Downloading OpenLiteSpeed', 1, 1, os.EX_OSERR) - install_utils.call('rpm -ivh /tmp/openlitespeed.rpm', self.distro, 'Installing OpenLiteSpeed', 'Installing OpenLiteSpeed', 1, 1, os.EX_OSERR) - except: - self.stdOut("Warning: OpenLiteSpeed installation failed, trying alternative...", 1) - try: - # Try building from source - install_utils.call('cd /tmp && wget https://openlitespeed.org/packages/openlitespeed-1.7.18.tar.gz', self.distro, 'Downloading OpenLiteSpeed source', 'Downloading OpenLiteSpeed source', 1, 1, os.EX_OSERR) - install_utils.call('cd /tmp && tar -xzf openlitespeed-1.7.18.tar.gz', self.distro, 'Extracting OpenLiteSpeed source', 'Extracting OpenLiteSpeed source', 1, 1, os.EX_OSERR) - install_utils.call('cd /tmp/openlitespeed-1.7.18 && ./configure --prefix=/usr/local/lsws', self.distro, 'Configuring OpenLiteSpeed', 'Configuring OpenLiteSpeed', 1, 1, os.EX_OSERR) - install_utils.call('cd /tmp/openlitespeed-1.7.18 && make', self.distro, 'Building OpenLiteSpeed', 'Building OpenLiteSpeed', 1, 1, os.EX_OSERR) - install_utils.call('cd /tmp/openlitespeed-1.7.18 && make install', self.distro, 'Installing OpenLiteSpeed', 'Installing OpenLiteSpeed', 1, 1, os.EX_OSERR) - except: - pass - - # Create systemd service file for lsws - self.stdOut("Creating lsws systemd service...", 1) - service_content = """[Unit] -Description=OpenLiteSpeed Web Server -After=network.target - -[Service] -Type=forking -PIDFile=/usr/local/lsws/logs/lshttpd.pid -ExecStart=/usr/local/lsws/bin/lswsctrl start -ExecStop=/usr/local/lsws/bin/lswsctrl stop -ExecReload=/usr/local/lsws/bin/lswsctrl restart -Restart=always -User=root -Group=root - -[Install] -WantedBy=multi-user.target -""" - - try: - with open('/etc/systemd/system/lsws.service', 'w') as f: - f.write(service_content) - subprocess.run(['systemctl', 'daemon-reload'], check=True) - subprocess.run(['systemctl', 'enable', 'lsws'], check=True) - self.stdOut("Created lsws systemd service", 1) - except Exception as e: - self.stdOut(f"Warning: Could not create lsws service: {str(e)}", 1) - - return True - - except Exception as e: - self.stdOut(f"Error in fix_almalinux9_litespeed: {str(e)}", 0) - return False - - def fix_almalinux9_mysql_gpg(self): - """Fix MySQL GPG key issues on AlmaLinux 9""" - try: - self.stdOut("Fixing MySQL GPG key issues...", 1) - - # Import MySQL GPG key - self.stdOut("Importing MySQL GPG key...", 1) - try: - install_utils.call('rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022', self.distro, 'Importing MySQL GPG key', 'Importing MySQL GPG key', 1, 1, os.EX_OSERR) - except: - pass - - # Try to install MySQL packages with --nogpgcheck - self.stdOut("Installing MySQL packages with --nogpgcheck...", 1) - mysql_packages = [ - 'mysql-community-devel', - 'mysql-community-client', - 'mysql-community-server' - ] - - for package in mysql_packages: - try: - install_utils.call(f'dnf install -y {package} --nogpgcheck', self.distro, f'Installing {package} (nogpgcheck)', f'Installing {package} (nogpgcheck)', 1, 0, os.EX_OSERR) - except: - self.stdOut(f"Warning: Could not install {package}", 1) - - return True - - except Exception as e: - self.stdOut(f"Error in fix_almalinux9_mysql_gpg: {str(e)}", 0) - return False - - def fix_ubuntu_specific(self): - """Fix Ubuntu-specific installation issues""" - try: - self.stdOut("Applying Ubuntu-specific fixes...", 1) - - # Install required dependencies - self.stdOut("Installing Ubuntu dependencies...", 1) - ubuntu_deps = [ - 'software-properties-common', - 'apt-transport-https', - 'curl', - 'wget', - 'gnupg', - 'lsb-release' - ] - - for dep in ubuntu_deps: - try: - install_utils.call(f'apt-get install -y {dep}', self.distro, f'Installing {dep}', f'Installing {dep}', 1, 0, os.EX_OSERR) - except: - self.stdOut(f"Warning: Could not install {dep}", 1) - - # Update package lists - install_utils.call('apt-get update', self.distro, 'Updating package lists', 'Updating package lists', 1, 1, os.EX_OSERR) - - return True - - except Exception as e: - self.stdOut(f"Error in fix_ubuntu_specific: {str(e)}", 0) - return False - - def fix_debian_specific(self): - """Fix Debian-specific installation issues""" - try: - self.stdOut("Applying Debian-specific fixes...", 1) - - # Install required dependencies - self.stdOut("Installing Debian dependencies...", 1) - debian_deps = [ - 'software-properties-common', - 'apt-transport-https', - 'curl', - 'wget', - 'gnupg', - 'lsb-release' - ] - - for dep in debian_deps: - try: - install_utils.call(f'apt-get install -y {dep}', self.distro, f'Installing {dep}', f'Installing {dep}', 1, 0, os.EX_OSERR) - except: - self.stdOut(f"Warning: Could not install {dep}", 1) - - # Update package lists - install_utils.call('apt-get update', self.distro, 'Updating package lists', 'Updating package lists', 1, 1, os.EX_OSERR) - - return True - - except Exception as e: - self.stdOut(f"Error in fix_debian_specific: {str(e)}", 0) - return False - - def fix_rhel_family_common(self): - """Fix common RHEL family (AlmaLinux, Rocky, RHEL, CloudLinux) issues""" - try: - self.stdOut("Applying RHEL family common fixes...", 1) - - # Install EPEL repository - self.stdOut("Installing EPEL repository...", 1) - try: - install_utils.call('dnf install -y epel-release', self.distro, 'Installing EPEL', 'Installing EPEL', 1, 1, os.EX_OSERR) - except: - try: - install_utils.call('yum install -y epel-release', self.distro, 'Installing EPEL (yum)', 'Installing EPEL (yum)', 1, 1, os.EX_OSERR) - except: - self.stdOut("Warning: Could not install EPEL", 1) - - # Install common dependencies - self.stdOut("Installing common RHEL dependencies...", 1) - common_deps = [ - 'curl', - 'wget', - 'git', - 'python3', - 'python3-pip', - 'gcc', - 'gcc-c++', - 'make', - 'cmake', - 'pcre2-devel', - 'openssl-devel', - 'zlib-devel' - ] - - for dep in common_deps: - try: - install_utils.call(f'dnf install -y {dep}', self.distro, f'Installing {dep}', f'Installing {dep}', 1, 0, os.EX_OSERR) - except: - try: - install_utils.call(f'yum install -y {dep}', self.distro, f'Installing {dep} (yum)', f'Installing {dep} (yum)', 1, 0, os.EX_OSERR) - except: - self.stdOut(f"Warning: Could not install {dep}", 1) - - return True - - except Exception as e: - self.stdOut(f"Error in fix_rhel_family_common: {str(e)}", 0) - return False - - def fix_legacy_centos(self): - """Fix CentOS 7 legacy issues""" - try: - self.stdOut("Applying CentOS 7 legacy fixes...", 1) - - # Install EPEL repository - self.stdOut("Installing EPEL repository...", 1) - install_utils.call('yum install -y epel-release', self.distro, 'Installing EPEL', 'Installing EPEL', 1, 1, os.EX_OSERR) - - # Install common dependencies - self.stdOut("Installing CentOS 7 dependencies...", 1) - centos7_deps = [ - 'curl', - 'wget', - 'git', - 'python3', - 'python3-pip', - 'gcc', - 'gcc-c++', - 'make', - 'cmake', - 'pcre-devel', - 'openssl-devel', - 'zlib-devel' - ] - - for dep in centos7_deps: - try: - install_utils.call(f'yum install -y {dep}', self.distro, f'Installing {dep}', f'Installing {dep}', 1, 0, os.EX_OSERR) - except: - self.stdOut(f"Warning: Could not install {dep}", 1) - - return True - - except Exception as e: - self.stdOut(f"Error in fix_legacy_centos: {str(e)}", 0) - return False - - def apply_os_specific_fixes(self): - """Apply OS-specific fixes based on detected OS""" - try: - os_info = self.detect_os_info() - fixes_needed = self.get_os_specific_fixes_needed() - - self.stdOut(f"Detected OS: {os_info['name']} {os_info['version']} (family: {os_info['family']})", 1) - self.stdOut(f"Applying fixes: {', '.join(fixes_needed)}", 1) - - # Apply common RHEL family fixes first - if self.is_rhel_family(): - self.fix_rhel_family_common() - - # Apply specific fixes - for fix in fixes_needed: - if fix == 'mariadb' and self.is_almalinux9(): - self.fix_almalinux9_mariadb() - elif fix == 'services' and self.is_almalinux9(): - self.fix_almalinux9_services() - elif fix == 'litespeed' and self.is_almalinux9(): - self.fix_almalinux9_litespeed() - elif fix == 'mysql_gpg' and self.is_almalinux9(): - self.fix_almalinux9_mysql_gpg() - elif fix == 'ubuntu_specific' and self.is_ubuntu(): - self.fix_ubuntu_specific() - elif fix == 'debian_specific' and self.is_debian(): - self.fix_debian_specific() - elif fix == 'legacy_centos': - self.fix_legacy_centos() - - self.stdOut("OS-specific fixes completed successfully", 1) - return True - - except Exception as e: - self.stdOut(f"Error applying OS-specific fixes: {str(e)}", 0) - return False - CloudLinux8 = 0 - - def install_package(self, package_name, options=""): - """Unified package installation across distributions""" - command, shell = install_utils.get_package_install_command(self.distro, package_name, options) - - # InstallCyberPanel always uses verbose mode (no silent option) - if self.distro == ubuntu or self.distro == debian12: - return install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, shell) - else: - # For non-Ubuntu/Debian, original code didn't pass shell parameter - return install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - def manage_service(self, service_name, action="start"): - """Unified service management with symlink detection""" - service_map = { - 'mariadb': 'mariadb', - 'pureftpd': 'pure-ftpd-mysql' if self.distro == ubuntu else 'pure-ftpd', - 'pdns': 'pdns' - } - - actual_service = service_map.get(service_name, service_name) - - # For MariaDB, check if mysqld.service is a symlink to mariadb.service - if service_name == 'mariadb': - # Check if mysqld.service is a symlink to mariadb.service - if os.path.islink('/etc/systemd/system/mysqld.service'): - try: - target = os.readlink('/etc/systemd/system/mysqld.service') - if 'mariadb.service' in target: - self.stdOut(f"mysqld.service is a symlink to mariadb.service, skipping duplicate {action}", 1) - # Only run the action on mariadb, not mysqld - command = f'systemctl {action} mariadb' - return install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - except OSError: - pass - - # For AlmaLinux 9, try both mariadb and mysqld services - if self.distro == cent8 or self.distro == openeuler: - # Try mariadb first, then mysqld if mariadb fails - command = f'systemctl {action} {actual_service}' - result = install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - if result != 0: - # If mariadb service fails, try mysqld - command = f'systemctl {action} mysqld' - return install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - return result - else: - command = f'systemctl {action} {actual_service}' - return install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - else: - command = f'systemctl {action} {actual_service}' - return install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - def modify_file_content(self, file_path, replacements): - """Generic file content modification""" - try: - with open(file_path, 'r') as f: - data = f.readlines() - - with open(file_path, 'w') as f: - for line in data: - modified_line = line - for old, new in replacements.items(): - if old in line: - modified_line = line.replace(old, new) - break - f.write(modified_line) - return True - except IOError as e: - logging.InstallLog.writeToFile(f'[ERROR] {str(e)} [modify_file_content]') - return False - - def copy_config_file(self, source_dir, dest_path, mysql_mode='One'): - """Handle configuration file copying with mode selection""" - # For directories like 'dns' vs 'dns-one', 'pure-ftpd' vs 'pure-ftpd-one' - # Default mode is 'One' which uses the -one directories - if mysql_mode == 'Two': - source_path = source_dir - else: - # Default mode 'One' uses directories with -one suffix - source_path = f"{source_dir}-one" - - # Ensure we're working with absolute paths - if not os.path.isabs(source_path): - source_path = os.path.join(self.cwd, source_path) - - # Determine the actual file to copy - if os.path.isdir(source_path): - # If dest_path is a file (like pdns.conf), copy the specific file - if dest_path.endswith('.conf'): - # Look for the specific config file - source_file = os.path.join(source_path, os.path.basename(dest_path)) - if os.path.exists(source_file): - if os.path.exists(dest_path): - os.remove(dest_path) - shutil.copy(source_file, dest_path) - else: - raise IOError(f"Source file {source_file} not found") - else: - # If it's a directory, copy the whole directory - if os.path.exists(dest_path): - if os.path.isdir(dest_path): - shutil.rmtree(dest_path) - shutil.copytree(source_path, dest_path) - else: - raise IOError(f"Source path {source_path} not found") - - @staticmethod - def ISARM(): - - try: - command = 'uname -a' - 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) - - if 'aarch64' in result.stdout: - return True - else: - return False - except: - return False - - @staticmethod - def OSFlags(): - if os.path.exists("/etc/redhat-release"): - data = open('/etc/redhat-release', 'r').read() - - if data.find('CloudLinux 8') > -1 or data.find('cloudlinux 8') > -1: - InstallCyberPanel.CloudLinux8 = 1 - - def __init__(self, rootPath, cwd, distro, ent, serial=None, port=None, ftp=None, dns=None, publicip=None, - remotemysql=None, mysqlhost=None, mysqldb=None, mysqluser=None, mysqlpassword=None, mysqlport=None): - self.server_root_path = rootPath - self.cwd = cwd - self.distro = distro - self.ent = ent - self.serial = serial - self.port = port - self.ftp = None - self.dns = dns - self.publicip = publicip - self.remotemysql = remotemysql - self.mysqlhost = mysqlhost - self.mysqluser = mysqluser - self.mysqlpassword = mysqlpassword - self.mysqlport = mysqlport - self.mysqldb = mysqldb - - ## TURN ON OS FLAGS FOR SPECIFIC NEEDS LATER - - InstallCyberPanel.OSFlags() - - @staticmethod - def stdOut(message, log=0, exit=0, code=os.EX_OK): - install_utils.stdOut(message, log, exit, code) - - @staticmethod - def getLatestLSWSVersion(): - """Fetch the latest LSWS Enterprise version from LiteSpeed's website""" - try: - # Try to fetch from the download page - url = "https://www.litespeedtech.com/products/litespeed-web-server/download" - req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) - with urllib.request.urlopen(req, timeout=10) as response: - html = response.read().decode('utf-8') - - # Look for the latest version pattern: lsws-X.Y.Z-ent - version_pattern = r'lsws-(\d+\.\d+\.\d+)-ent' - versions = re.findall(version_pattern, html) - - if versions: - # Get the latest version - latest_version = sorted(versions, key=lambda v: [int(x) for x in v.split('.')])[-1] - InstallCyberPanel.stdOut(f"Found latest LSWS Enterprise version: {latest_version}", 1) - return latest_version - else: - InstallCyberPanel.stdOut("Could not find version pattern in HTML, using fallback", 1) - - except Exception as e: - InstallCyberPanel.stdOut(f"Failed to fetch latest LSWS version: {str(e)}, using fallback", 1) - - # Fallback to known latest version - return "6.3.4" - - def installLiteSpeed(self): - if self.ent == 0: - # Apply OS-specific fixes - self.apply_os_specific_fixes() - - # Install OpenLiteSpeed if not already handled by OS-specific fixes - if not self.is_almalinux9(): - self.install_package('openlitespeed') - - else: - try: - try: - command = 'groupadd nobody' - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - except: - pass - - try: - command = 'usermod -a -G nobody nobody' - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - except: - pass - - # Get the latest LSWS Enterprise version dynamically - lsws_version = InstallCyberPanel.getLatestLSWSVersion() - - if InstallCyberPanel.ISARM(): - command = f'wget https://www.litespeedtech.com/packages/6.0/lsws-{lsws_version}-ent-aarch64-linux.tar.gz' - else: - command = f'wget https://www.litespeedtech.com/packages/6.0/lsws-{lsws_version}-ent-x86_64-linux.tar.gz' - - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - if InstallCyberPanel.ISARM(): - command = f'tar zxf lsws-{lsws_version}-ent-aarch64-linux.tar.gz' - else: - command = f'tar zxf lsws-{lsws_version}-ent-x86_64-linux.tar.gz' - - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - if str.lower(self.serial) == 'trial': - command = f'wget -q --output-document=lsws-{lsws_version}/trial.key http://license.litespeedtech.com/reseller/trial.key' - if self.serial == '1111-2222-3333-4444': - command = f'wget -q --output-document=/root/cyberpanel/install/lsws-{lsws_version}/trial.key http://license.litespeedtech.com/reseller/trial.key' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - else: - writeSerial = open(f'lsws-{lsws_version}/serial.no', 'w') - writeSerial.writelines(self.serial) - writeSerial.close() - - shutil.copy('litespeed/install.sh', f'lsws-{lsws_version}/') - shutil.copy('litespeed/functions.sh', f'lsws-{lsws_version}/') - - os.chdir(f'lsws-{lsws_version}') - - command = 'chmod +x install.sh' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = 'chmod +x functions.sh' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = './install.sh' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - os.chdir(self.cwd) - confPath = '/usr/local/lsws/conf/' - - # Ensure the conf directory exists - os.makedirs(confPath, exist_ok=True) - - # Copy configuration files - shutil.copy('litespeed/httpd_config.xml', confPath) - shutil.copy('litespeed/modsec.conf', confPath) - shutil.copy('litespeed/httpd.conf', confPath) - - # Also copy the .conf version for compatibility - if os.path.exists('litespeed/httpd_config.conf'): - shutil.copy('litespeed/httpd_config.conf', confPath) - else: - # Create a basic httpd_config.conf if it doesn't exist - with open(f'{confPath}/httpd_config.conf', 'w') as f: - f.write('''# -# PLAIN TEXT CONFIGURATION FILE -# -serverName lscp -user nobody -group nobody -priority 0 -inMemBufSize 60M -swappingDir /tmp/lshttpd/swap -autoFix503 1 -gracefulRestartTimeout 300 -mime $SERVER_ROOT/conf/mime.properties -showVersionNumber 0 -adminEmails root@localhost -adminRoot $SERVER_ROOT/admin/ - -errorlog $SERVER_ROOT/logs/error.log { - logLevel DEBUG - debugLevel 0 - rollingSize 10M - enableStderrLog 1 -} - -accesslog $SERVER_ROOT/logs/access.log { - rollingSize 10M - keepDays 30 - compressArchive 0 -} -indexFiles index.html, index.php - -expires { - enableExpires 1 - expiresByType image/*=A604800, text/css=A604800, application/x-javascript=A604800 -} -autoLoadHtaccess 1 - -tuning { - eventDispatcher best - maxConnections 2000 - maxSSLConnections 1000 - connTimeout 300 - maxKeepAliveReq 1000 - smartKeepAlive 0 - keepAliveTimeout 5 - sndBufSize 0 - rcvBufSize 0 - maxReqURLLen 8192 - maxReqHeaderSize 16380 - maxReqBodySize 2047M - maxDynRespHeaderSize 8192 - maxDynRespSize 2047M - maxCachedFileSize 4096 - totalInMemCacheSize 20M - maxMMapFileSize 256K - totalMMapCacheSize 40M - useSendfile 1 - fileETag 28 - enableGzipCompress 1 - enableDynGzipCompress 1 - gzipCompressLevel 6 - compressibleTypes text/*,application/x-javascript,application/javascript,application/xml, image/svg+xml - gzipAutoUpdateStatic 1 - gzipStaticCompressLevel 6 - gzipMaxFileSize 1M - gzipMinFileSize 300 - SSLCryptoDevice null -} - -fileAccessControl { - followSymbolLink 1 - checkSymbolLink 0 - requiredPermission 644 - restrictedPermission 000 -} -''') - - command = 'chown -R lsadm:lsadm ' + confPath - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installLiteSpeed]") - return 0 - - return 1 - - def reStartLiteSpeed(self): - command = install_utils.format_restart_litespeed_command(self.server_root_path) - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - def fix_ols_configs(self): - try: - - InstallCyberPanel.stdOut("Fixing OpenLiteSpeed configurations!", 1) - - ## remove example virtual host - - data = open(self.server_root_path + "conf/httpd_config.conf", 'r').readlines() - - writeDataToFile = open(self.server_root_path + "conf/httpd_config.conf", 'w') - - for items in data: - if items.find("map") > -1 and items.find("Example") > -1: - continue - else: - writeDataToFile.writelines(items) - - writeDataToFile.close() - - InstallCyberPanel.stdOut("OpenLiteSpeed Configurations fixed!", 1) - except IOError as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [fix_ols_configs]") - return 0 - - return self.reStartLiteSpeed() - - def changePortTo80(self): - try: - InstallCyberPanel.stdOut("Changing default port to 80..", 1) - - file_path = self.server_root_path + "conf/httpd_config.conf" - if self.modify_file_content(file_path, {"*:8088": "*:80"}): - InstallCyberPanel.stdOut("Default port is now 80 for OpenLiteSpeed!", 1) - else: - return 0 - - except Exception as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [changePortTo80]") - return 0 - - return self.reStartLiteSpeed() - - def installPHPDependencies(self): - """Install required dependencies for PHP extensions""" - try: - InstallCyberPanel.stdOut("Installing PHP dependencies...", 1) - - if self.distro == ubuntu: - # Ubuntu dependencies - deps = [ - 'libmemcached-dev', 'libmemcached11', - 'libgd-dev', 'libgd3', - 'libc-client2007e-dev', 'libc-client2007e', - 'libonig-dev', 'libonig5', - 'libicu-dev', 'libicu70', - 'libaspell-dev', 'libaspell15', - 'libpspell-dev', 'libpspell1' - ] - command = f'DEBIAN_FRONTEND=noninteractive apt-get -y install {" ".join(deps)}' - os.system(command) - else: - # RHEL-based dependencies - enhanced list - deps = [ - 'libmemcached', 'libmemcached-devel', 'libmemcached-libs', - 'gd', 'gd-devel', 'libgd', - 'c-client', 'c-client-devel', - 'oniguruma', 'oniguruma-devel', - 'libicu', 'libicu-devel', - 'aspell', 'aspell-devel', - 'pspell', 'pspell-devel', - 'sendmail-milter', 'sendmail-milter-devel', # For libmilter - 'GeoIP', 'GeoIP-devel', # For geoip-devel - 'udns', 'udns-devel', # For udns-devel - 'sasl', 'cyrus-sasl-devel', # For SASL headers - 'libmilter', 'sendmail-milter-devel' # For libmilter.so.1.0 - ] - - for dep in deps: - try: - self.install_package(dep, '--skip-broken') - except: - pass # Continue if dependency installation fails - - except Exception as e: - InstallCyberPanel.stdOut(f"Warning: Some PHP dependencies may not be available: {str(e)}", 0) - - def installAllPHPVersions(self): - # Install PHP dependencies first - self.installPHPDependencies() - - # Updated PHP versions: Only 7.4+ and use 8.5 as beta - # Priority: 85 (beta), 84, 83, 82, 81, 80, 74 - php_versions = ['74', '80', '81', '82', '83', '84', '85'] - - if self.distro == ubuntu: - # Install PHP 7.4 only (legacy support) with mbstring - command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install ' \ - 'lsphp74 lsphp74-common lsphp74-curl lsphp74-dev lsphp74-imap lsphp74-intl lsphp74-json ' \ - 'lsphp74-ldap lsphp74-mysql lsphp74-opcache lsphp74-pspell lsphp74-recode ' \ - 'lsphp74-sqlite3 lsphp74-tidy lsphp74-mbstring' - os.system(command) - - # Install PHP 8.x versions (8.0 to 8.5) with mbstring - for version in php_versions[1:]: # 80, 81, 82, 83, 84, 85 - self.install_package(f'lsphp{version}*') - # Ensure mbstring is installed for each version - try: - self.install_package(f'lsphp{version}-mbstring') - except: - pass - - # Create mbstring configuration for each PHP version - try: - mbstring_config = f'/usr/local/lsws/lsphp{version}/etc/php.d/20-mbstring.ini' - if not os.path.exists(mbstring_config): - os.makedirs(os.path.dirname(mbstring_config), exist_ok=True) - with open(mbstring_config, 'w') as f: - f.write('extension=mbstring\n') - except: - pass - - elif self.distro == centos: - # Install PHP 7.4 only (legacy support) - self.install_package('lsphp74*', '--skip-broken') - - # Install PHP 8.x versions - for version in php_versions[1:]: # 80, 81, 82, 83, 84, 85 - self.install_package(f'lsphp{version}*', '--skip-broken') - - elif self.distro == cent8: - # Install PHP versions in batches with exclusions - exclude_flags = "--exclude *imagick*" - - # First batch: PHP 7.4 and 8.0-8.2 - versions_batch1 = 'lsphp74* lsphp80* lsphp81* lsphp82*' - self.install_package(versions_batch1, f'{exclude_flags} --skip-broken') - - # Second batch: PHP 8.3-8.5 (including beta 8.5) - versions_batch2 = 'lsphp83* lsphp84* lsphp85*' - self.install_package(versions_batch2, f'{exclude_flags} --skip-broken') - - elif self.distro == openeuler: - # Install all PHP versions at once - all_versions = ' '.join([f'lsphp{v}*' for v in php_versions]) - self.install_package(all_versions) - - if self.distro != ubuntu: - InstallCyberPanel.stdOut("LiteSpeed PHPs successfully installed!", 1) - - def installSieve(self): - """Install Sieve (Dovecot Sieve) for email filtering on all OS variants""" - try: - InstallCyberPanel.stdOut("Installing Sieve (Dovecot Sieve) for email filtering...", 1) - - if self.distro == ubuntu: - # Install dovecot-sieve and dovecot-managesieved - self.install_package('dovecot-sieve dovecot-managesieved') - else: - # Apply OS-specific fixes - self.apply_os_specific_fixes() - - # Install Dovecot if not already handled by OS-specific fixes - if not self.is_almalinux9(): - # For CentOS/AlmaLinux/OpenEuler - self.install_package('dovecot-pigeonhole') - - # Add Sieve port 4190 to firewall - try: - from plogical.firewallUtilities import FirewallUtilities - except ImportError: - # plogical module not available yet, skip firewall setup - logging.InstallLog.writeToFile("[WARNING] plogical module not available, skipping firewall setup") - return - FirewallUtilities.addSieveFirewallRule() - - InstallCyberPanel.stdOut("Sieve successfully installed and configured!", 1) - return 1 - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installSieve]") - return 0 - - def installMySQL(self, mysql): - - ############## Install mariadb ###################### - - if self.distro == ubuntu: - - command = 'DEBIAN_FRONTEND=noninteractive apt-get install software-properties-common apt-transport-https curl -y' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - command = "mkdir -p /etc/apt/keyrings" - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = "curl -o /etc/apt/keyrings/mariadb-keyring.pgp 'https://mariadb.org/mariadb_release_signing_key.pgp'" - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - RepoPath = '/etc/apt/sources.list.d/mariadb.sources' - RepoContent = f""" -# MariaDB 10.11 repository list - created 2023-12-11 07:53 UTC -# https://mariadb.org/download/ -X-Repolib-Name: MariaDB -Types: deb -# deb.mariadb.org is a dynamic mirror if your preferred mirror goes offline. See https://mariadb.org/mirrorbits/ for details. -# URIs: https://deb.mariadb.org/10.11/ubuntu -URIs: https://mirrors.gigenet.com/mariadb/repo/10.11/ubuntu -Suites: jammy -Components: main main/debug -Signed-By: /etc/apt/keyrings/mariadb-keyring.pgp -""" - - if get_Ubuntu_release() > 21.00: - command = 'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=10.11' - result = install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, True) - - # If the download fails, use manual repo configuration as fallback - if result != 1: - install_utils.writeToFile("MariaDB repo setup script failed, using manual configuration...") - - # First, ensure directories exist - command = 'mkdir -p /usr/share/keyrings /etc/apt/sources.list.d' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - # Download and add MariaDB signing key - command = 'curl -fsSL https://mariadb.org/mariadb_release_signing_key.pgp | gpg --dearmor -o /usr/share/keyrings/mariadb-keyring.pgp' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - # Use multiple mirror options for better reliability - RepoPath = '/etc/apt/sources.list.d/mariadb.list' - codename = get_Ubuntu_code_name() - RepoContent = f"""# MariaDB 10.11 repository list - manual fallback -# Primary mirror -deb [arch=amd64,arm64,ppc64el,s390x signed-by=/usr/share/keyrings/mariadb-keyring.pgp] https://mirror.mariadb.org/repo/10.11/ubuntu {codename} main - -# Alternative mirrors (uncomment if primary fails) -# deb [arch=amd64,arm64,ppc64el,s390x signed-by=/usr/share/keyrings/mariadb-keyring.pgp] https://mirrors.gigenet.com/mariadb/repo/10.11/ubuntu {codename} main -# deb [arch=amd64,arm64,ppc64el,s390x signed-by=/usr/share/keyrings/mariadb-keyring.pgp] https://ftp.osuosl.org/pub/mariadb/repo/10.11/ubuntu {codename} main -""" - - WriteToFile = open(RepoPath, 'w') - WriteToFile.write(RepoContent) - WriteToFile.close() - - install_utils.writeToFile("Manual MariaDB repository configuration completed.") - - - - command = 'DEBIAN_FRONTEND=noninteractive apt-get update -y' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - - command = "DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server -y" - elif self.distro == debian12: - # Debian 12 uses similar setup to Ubuntu but with native packages - command = 'DEBIAN_FRONTEND=noninteractive apt-get install software-properties-common apt-transport-https curl -y' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - command = "mkdir -p /etc/apt/keyrings" - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - # Use MariaDB official repository for Debian 12 - command = 'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=10.11' - result = install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, True) - - # If the download fails, use manual repo configuration as fallback - if result != 1: - install_utils.writeToFile("MariaDB repo setup script failed, using manual configuration...") - - # First, ensure directories exist - command = 'mkdir -p /usr/share/keyrings /etc/apt/sources.list.d' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - # Download and add MariaDB signing key - command = 'curl -fsSL https://mariadb.org/mariadb_release_signing_key.pgp | gpg --dearmor -o /usr/share/keyrings/mariadb-keyring.pgp' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - # Use Debian 12 bookworm codename - RepoPath = '/etc/apt/sources.list.d/mariadb.list' - RepoContent = """# MariaDB 10.11 repository list for Debian 12 -# Primary mirror -deb [arch=amd64,arm64,ppc64el,s390x signed-by=/usr/share/keyrings/mariadb-keyring.pgp] https://mirror.mariadb.org/repo/10.11/debian bookworm main - -# Alternative mirrors (uncomment if primary fails) -# deb [arch=amd64,arm64,ppc64el,s390x signed-by=/usr/share/keyrings/mariadb-keyring.pgp] https://mirrors.gigenet.com/mariadb/repo/10.11/debian bookworm main -# deb [arch=amd64,arm64,ppc64el,s390x signed-by=/usr/share/keyrings/mariadb-keyring.pgp] https://ftp.osuosl.org/pub/mariadb/repo/10.11/debian bookworm main -""" - - WriteToFile = open(RepoPath, 'w') - WriteToFile.write(RepoContent) - WriteToFile.close() - - install_utils.writeToFile("Manual MariaDB repository configuration completed.") - - command = 'DEBIAN_FRONTEND=noninteractive apt-get update -y' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - command = "DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server -y" - elif self.distro == centos: - - RepoPath = '/etc/yum.repos.d/mariadb.repo' - RepoContent = f""" -[mariadb] -name = MariaDB -baseurl = http://yum.mariadb.org/10.11/rhel8-amd64 -module_hotfixes=1 -gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB -gpgcheck=1 -""" - WriteToFile = open(RepoPath, 'w') - WriteToFile.write(RepoContent) - WriteToFile.close() - - command = 'dnf install mariadb-server -y' - elif self.distro == cent8 or self.distro == openeuler: - - clAPVersion = FetchCloudLinuxAlmaVersionVersion() - type = clAPVersion.split('-')[0] - version = int(clAPVersion.split('-')[1]) - - - if type == 'cl' and version >= 88: - - command = 'yum remove db-governor db-governor-mysql -y' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - command = 'yum install governor-mysql -y' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - command = '/usr/share/lve/dbgovernor/mysqlgovernor.py --mysql-version=mariadb106' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - command = '/usr/share/lve/dbgovernor/mysqlgovernor.py --install --yes' - - else: - - command = 'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=10.11' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - command = 'yum remove mariadb* -y' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - command = 'sudo dnf -qy module disable mariadb' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - command = 'sudo dnf module reset mariadb -y' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - # Disable problematic mariadb-maxscale repository to avoid 404 errors - command = 'dnf config-manager --disable mariadb-maxscale' - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, True) - - # Clear dnf cache to avoid repository issues - command = 'dnf clean all' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - command = 'dnf install mariadb-server mariadb-devel mariadb-client-utils -y' - - # Apply OS-specific fixes - self.apply_os_specific_fixes() - - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - ############## Start mariadb ###################### - - self.startMariaDB() - - def changeMYSQLRootPassword(self): - if self.remotemysql == 'OFF': - if self.distro == ubuntu: - passwordCMD = "use mysql;DROP DATABASE IF EXISTS test;DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%%';GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY '%s';UPDATE user SET plugin='' WHERE User='root';flush privileges;" % ( - InstallCyberPanel.mysql_Root_password) - else: - passwordCMD = "use mysql;DROP DATABASE IF EXISTS test;DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%%';GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY '%s';flush privileges;" % ( - InstallCyberPanel.mysql_Root_password) - - # Find the correct MySQL/MariaDB client command - mysql_commands = ['mysql', 'mariadb', '/usr/bin/mysql', '/usr/bin/mariadb', '/usr/local/bin/mysql', '/usr/local/bin/mariadb'] - mysql_cmd = None - - for cmd in mysql_commands: - if os.path.exists(cmd) or self.command_exists(cmd): - mysql_cmd = cmd - self.stdOut(f"Found MySQL client: {cmd}", 1) - break - - if not mysql_cmd: - self.stdOut("ERROR: No MySQL/MariaDB client found!", 0) - return False - - # For AlmaLinux 9, try mysql command first, then mariadb - if self.distro == cent8 or self.distro == openeuler: - command = f'{mysql_cmd} -u root -e "{passwordCMD}"' - result = install_utils.call(command, self.distro, command, command, 0, 0, os.EX_OSERR) - if result != 0: - # If mysql command fails, try mariadb - for alt_cmd in ['mariadb', '/usr/bin/mariadb']: - if alt_cmd != mysql_cmd and (os.path.exists(alt_cmd) or self.command_exists(alt_cmd)): - command = f'{alt_cmd} -u root -e "{passwordCMD}"' - result = install_utils.call(command, self.distro, command, command, 0, 0, os.EX_OSERR) - if result == 0: - break - else: - command = f'{mysql_cmd} -u root -e "{passwordCMD}"' - install_utils.call(command, self.distro, command, command, 0, 0, os.EX_OSERR) - - def command_exists(self, command): - """Check if a command exists in PATH""" - try: - result = subprocess.run(['which', command], capture_output=True, text=True) - return result.returncode == 0 - except: - return False - - def startMariaDB(self): - - if self.remotemysql == 'OFF': - ############## Start mariadb ###################### - - # Apply OS-specific fixes - self.apply_os_specific_fixes() - - # Try to start MariaDB service - self.stdOut("Starting MariaDB service...", 1) - start_result = self.manage_service('mariadb', 'start') - - if start_result != 0: - self.stdOut("MariaDB service start failed, trying alternative approaches...", 1) - # Try to start mysqld service as fallback - command = 'systemctl start mysqld' - result = install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - if result == 0: - self.stdOut("Successfully started mysqld service", 1) - else: - self.stdOut("Warning: Could not start MariaDB/MySQL service", 0) - - ############## Enable mariadb at system startup ###################### - - # Clean up any conflicting service files - if os.path.exists('/etc/systemd/system/mysqld.service'): - if os.path.islink('/etc/systemd/system/mysqld.service'): - self.stdOut("Removing symlink: /etc/systemd/system/mysqld.service", 1) - os.remove('/etc/systemd/system/mysqld.service') - else: - self.stdOut("Removing file: /etc/systemd/system/mysqld.service", 1) - os.remove('/etc/systemd/system/mysqld.service') - - if os.path.exists('/etc/systemd/system/mariadb.service'): - if os.path.islink('/etc/systemd/system/mariadb.service'): - self.stdOut("Removing symlink: /etc/systemd/system/mariadb.service", 1) - os.remove('/etc/systemd/system/mariadb.service') - else: - self.stdOut("Removing file: /etc/systemd/system/mariadb.service", 1) - os.remove('/etc/systemd/system/mariadb.service') - - # Reload systemd daemon after removing service files - os.system('systemctl daemon-reload') - - # Try to enable MariaDB service - enable_result = self.manage_service('mariadb', 'enable') - if enable_result != 0: - self.stdOut("MariaDB service enable failed, trying mysqld...", 1) - command = 'systemctl enable mysqld' - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - def fixMariaDB(self): - self.stdOut("Setup MariaDB so it can support Cyberpanel's needs") - - conn = mariadb.connect(user='root', passwd=self.mysql_Root_password) - cursor = conn.cursor() - cursor.execute('set global innodb_file_per_table = on;') - try: - cursor.execute('set global innodb_file_format = Barracuda;') - cursor.execute('set global innodb_large_prefix = on;') - except BaseException as msg: - self.stdOut('%s. [ERROR:335]' % (str(msg))) - cursor.close() - conn.close() - - try: - fileName = '/etc/mysql/mariadb.conf.d/50-server.cnf' - data = open(fileName, 'r').readlines() - - writeDataToFile = open(fileName, 'w') - for line in data: - writeDataToFile.write(line.replace('utf8mb4', 'utf8')) - writeDataToFile.close() - except IOError as err: - self.stdOut("[ERROR] Error in setting: " + fileName + ": " + str(err), 1, 1, os.EX_OSERR) - - # Use the manage_service method for consistent service management - if self.distro == cent8 or self.distro == openeuler: - # Try mariadb first, then mysqld - result = os.system('systemctl restart mariadb') - if result != 0: - os.system('systemctl restart mysqld') - else: - os.system('systemctl restart mariadb') - - self.stdOut("MariaDB is now setup so it can support Cyberpanel's needs") - - def installPureFTPD(self): - if self.distro == ubuntu: - self.install_package('pure-ftpd-mysql') - - if get_Ubuntu_release() == 18.10: - # Special handling for Ubuntu 18.10 - packages = [ - ('pure-ftpd-common_1.0.47-3_all.deb', 'wget https://rep.cyberpanel.net/pure-ftpd-common_1.0.47-3_all.deb'), - ('pure-ftpd-mysql_1.0.47-3_amd64.deb', 'wget https://rep.cyberpanel.net/pure-ftpd-mysql_1.0.47-3_amd64.deb') - ] - - for filename, wget_cmd in packages: - install_utils.call(wget_cmd, self.distro, wget_cmd, wget_cmd, 1, 1, os.EX_OSERR) - command = f'dpkg --install --force-confold {filename}' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - else: - # Apply OS-specific fixes - self.apply_os_specific_fixes() - - # Install Pure-FTPd if not already handled by OS-specific fixes - if not self.is_almalinux9(): - self.install_package('pure-ftpd') - - ####### Install pureftpd to system startup - - command = "systemctl enable " + install.preFlightsChecks.pureFTPDServiceName(self.distro) - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - ###### FTP Groups and user settings settings - - command = 'groupadd -g 2001 ftpgroup' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = 'useradd -u 2001 -s /bin/false -d /bin/null -c "pureftpd user" -g ftpgroup ftpuser' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - def startPureFTPD(self): - ############## Start pureftpd ###################### - serviceName = install.preFlightsChecks.pureFTPDServiceName(self.distro) - - # During fresh installation, don't start Pure-FTPd yet - # It will be started after Django migrations create the required tables - InstallCyberPanel.stdOut("Pure-FTPd enabled for startup.", 1) - InstallCyberPanel.stdOut("Note: Pure-FTPd will start after database setup is complete.", 1) - logging.InstallLog.writeToFile("Pure-FTPd enabled but not started - waiting for Django migrations") - - def installPureFTPDConfigurations(self, mysql): - try: - ## setup ssl for ftp - - InstallCyberPanel.stdOut("Configuring PureFTPD..", 1) - - try: - if not os.path.exists("/etc/ssl/private"): - os.makedirs("/etc/ssl/private", mode=0o755) - except OSError as e: - if e.errno != errno.EEXIST: - logging.InstallLog.writeToFile("[ERROR] Could not create directory for FTP SSL: " + str(e)) - raise - - if (self.distro == centos or self.distro == cent8 or self.distro == openeuler) or ( - self.distro == ubuntu and get_Ubuntu_release() == 18.14): - 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 /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem' - else: - command = 'openssl req -x509 -nodes -days 7300 -newkey rsa:2048 -subj "/C=US/ST=Denial/L=Sprinal-ield/O=Dis/CN=www.example.com" -keyout /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem' - - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - os.chdir(self.cwd) - ftpdPath = "/etc/pure-ftpd" - - self.copy_config_file("pure-ftpd", ftpdPath, mysql) - - if self.distro == ubuntu: - try: - os.mkdir('/etc/pure-ftpd/conf') - os.mkdir('/etc/pure-ftpd/auth') - os.mkdir('/etc/pure-ftpd/db') - except OSError as err: - self.stdOut("[ERROR] Error creating extra pure-ftpd directories: " + str(err), ". Should be ok", 1) - - data = open(ftpdPath + "/pureftpd-mysql.conf", "r").readlines() - - writeDataToFile = open(ftpdPath + "/pureftpd-mysql.conf", "w") - - dataWritten = "MYSQLPassword " + InstallCyberPanel.mysqlPassword + '\n' - for items in data: - if items.find("MYSQLPassword") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - writeDataToFile.close() - - ftpConfPath = '/etc/pure-ftpd/pureftpd-mysql.conf' - - if self.remotemysql == 'ON': - command = "sed -i 's|localhost|%s|g' %s" % (self.mysqlhost, ftpConfPath) - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = "sed -i 's|3306|%s|g' %s" % (self.mysqlport, ftpConfPath) - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = "sed -i 's|MYSQLSocket /var/lib/mysql/mysql.sock||g' %s" % (ftpConfPath) - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - if self.distro == ubuntu: - - if os.path.exists('/etc/pure-ftpd/db/mysql.conf'): - os.remove('/etc/pure-ftpd/db/mysql.conf') - shutil.copy(ftpdPath + "/pureftpd-mysql.conf", '/etc/pure-ftpd/db/mysql.conf') - else: - shutil.copy(ftpdPath + "/pureftpd-mysql.conf", '/etc/pure-ftpd/db/mysql.conf') - - command = 'echo 1 > /etc/pure-ftpd/conf/TLS' - subprocess.call(command, shell=True) - - command = 'echo %s > /etc/pure-ftpd/conf/ForcePassiveIP' % (self.publicip) - subprocess.call(command, shell=True) - - command = 'echo "40110 40210" > /etc/pure-ftpd/conf/PassivePortRange' - subprocess.call(command, shell=True) - - command = 'echo "no" > /etc/pure-ftpd/conf/UnixAuthentication' - subprocess.call(command, shell=True) - - command = 'echo "/etc/pure-ftpd/db/mysql.conf" > /etc/pure-ftpd/conf/MySQLConfigFile' - subprocess.call(command, shell=True) - - command = 'ln -s /etc/pure-ftpd/conf/MySQLConfigFile /etc/pure-ftpd/auth/30mysql' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = 'ln -s /etc/pure-ftpd/conf/UnixAuthentication /etc/pure-ftpd/auth/65unix' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = 'systemctl restart pure-ftpd-mysql.service' - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - - - - if get_Ubuntu_release() > 21.00: - ### change mysql md5 to crypt - - command = "sed -i 's/MYSQLCrypt md5/MYSQLCrypt crypt/g' /etc/pure-ftpd/db/mysql.conf" - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = "systemctl restart pure-ftpd-mysql.service" - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - else: - - try: - clAPVersion = FetchCloudLinuxAlmaVersionVersion() - type = clAPVersion.split('-')[0] - version = int(clAPVersion.split('-')[1]) - - if type == 'al' and version >= 90: - command = "sed -i 's/MYSQLCrypt md5/MYSQLCrypt crypt/g' /etc/pure-ftpd/pureftpd-mysql.conf" - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - except: - pass - - - - InstallCyberPanel.stdOut("PureFTPD configured!", 1) - - except IOError as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installPureFTPDConfigurations]") - return 0 - - def installPowerDNS(self): - try: - if self.distro == ubuntu or self.distro == cent8 or self.distro == openeuler: - # Stop and disable systemd-resolved - self.manage_service('systemd-resolved', 'stop') - self.manage_service('systemd-resolved.service', 'disable') - - try: - os.rename('/etc/resolv.conf', '/etc/resolv.conf.bak') - except OSError as e: - if e.errno != errno.EEXIST and e.errno != errno.ENOENT: - InstallCyberPanel.stdOut("[ERROR] Unable to rename /etc/resolv.conf to install PowerDNS: " + - str(e), 1, 1, os.EX_OSERR) - - # Create a temporary resolv.conf with Google DNS for package installation - try: - with open('/etc/resolv.conf', 'w') as f: - f.write('nameserver 8.8.8.8\n') - f.write('nameserver 8.8.4.4\n') - InstallCyberPanel.stdOut("Created temporary /etc/resolv.conf with Google DNS", 1) - except IOError as e: - InstallCyberPanel.stdOut("[ERROR] Unable to create /etc/resolv.conf: " + str(e), 1, 1, os.EX_OSERR) - - # Install PowerDNS packages - if self.distro == ubuntu: - # Update package list first - command = "DEBIAN_FRONTEND=noninteractive apt-get update" - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - # Install PowerDNS packages - command = "DEBIAN_FRONTEND=noninteractive apt-get -y install pdns-server pdns-backend-mysql" - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - # Ensure service is stopped after installation for configuration - command = 'systemctl stop pdns || true' - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, True) - return 1 - else: - # Apply OS-specific fixes - self.apply_os_specific_fixes() - - # Install PowerDNS if not already handled by OS-specific fixes - if not self.is_almalinux9(): - self.install_package('pdns pdns-backend-mysql') - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [powerDNS]") - - def installPowerDNSConfigurations(self, mysqlPassword, mysql): - try: - - InstallCyberPanel.stdOut("Configuring PowerDNS..", 1) - - os.chdir(self.cwd) - if self.distro == centos or self.distro == cent8 or self.distro == openeuler: - dnsPath = "/etc/pdns/pdns.conf" - else: - dnsPath = "/etc/powerdns/pdns.conf" - # Ensure directory exists for Ubuntu - dnsDir = os.path.dirname(dnsPath) - if not os.path.exists(dnsDir): - try: - os.makedirs(dnsDir, mode=0o755) - except OSError as e: - if e.errno != errno.EEXIST: - raise - - try: - self.copy_config_file("dns", dnsPath, mysql) - except Exception as e: - InstallCyberPanel.stdOut("[ERROR] Failed to copy PowerDNS config: " + str(e), 1) - logging.InstallLog.writeToFile('[ERROR] Failed to copy PowerDNS config: ' + str(e)) - raise - - # Verify the file was copied and has content - if not os.path.exists(dnsPath): - raise IOError(f"PowerDNS config file not found at {dnsPath} after copy") - - # Check if file has content - with open(dnsPath, "r") as f: - content = f.read() - if not content or "launch=gmysql" not in content: - InstallCyberPanel.stdOut("[WARNING] PowerDNS config appears empty or incomplete, attempting to fix...", 1) - - # First try to re-copy - try: - if os.path.exists(dnsPath): - os.remove(dnsPath) - source_file = os.path.join(self.cwd, "dns-one", "pdns.conf") - shutil.copy2(source_file, dnsPath) - except Exception as copy_error: - InstallCyberPanel.stdOut("[WARNING] Failed to re-copy config: " + str(copy_error), 1) - - # Fallback: directly write the essential MySQL configuration - InstallCyberPanel.stdOut("[INFO] Directly writing MySQL backend configuration...", 1) - try: - mysql_config = f"""# PowerDNS MySQL Backend Configuration -launch=gmysql -gmysql-host=localhost -gmysql-port=3306 -gmysql-user=cyberpanel -gmysql-password={mysqlPassword} -gmysql-dbname=cyberpanel - -# Basic PowerDNS settings -daemon=no -guardian=no -setgid=pdns -setuid=pdns -""" - # If file exists and has some content, append our config - if os.path.exists(dnsPath) and content.strip(): - # Check if it's just missing the MySQL part - with open(dnsPath, "a") as f: - f.write("\n\n" + mysql_config) - else: - # Write a complete minimal config - with open(dnsPath, "w") as f: - f.write(mysql_config) - - InstallCyberPanel.stdOut("[SUCCESS] MySQL backend configuration written directly", 1) - except Exception as write_error: - InstallCyberPanel.stdOut("[ERROR] Failed to write MySQL config: " + str(write_error), 1) - raise - - InstallCyberPanel.stdOut("PowerDNS config file prepared at: " + dnsPath, 1) - - data = open(dnsPath, "r").readlines() - - writeDataToFile = open(dnsPath, "w") - - dataWritten = "gmysql-password=" + mysqlPassword + "\n" - - for items in data: - if items.find("gmysql-password") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - # if self.distro == ubuntu: - # os.fchmod(writeDataToFile.fileno(), stat.S_IRUSR | stat.S_IWUSR) - - writeDataToFile.close() - - if self.remotemysql == 'ON': - command = "sed -i 's|gmysql-host=localhost|gmysql-host=%s|g' %s" % (self.mysqlhost, dnsPath) - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = "sed -i 's|gmysql-port=3306|gmysql-port=%s|g' %s" % (self.mysqlport, dnsPath) - install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - # Set proper permissions for PowerDNS config - if self.distro == ubuntu: - # Ensure pdns user/group exists - command = 'id -u pdns &>/dev/null || useradd -r -s /usr/sbin/nologin pdns' - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'chown root:pdns %s' % dnsPath - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'chmod 640 %s' % dnsPath - install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - InstallCyberPanel.stdOut("PowerDNS configured!", 1) - - except IOError as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installPowerDNSConfigurations]") - return 0 - return 1 - - def startPowerDNS(self): - - ############## Start PowerDNS ###################### - - self.manage_service('pdns', 'enable') - - # During fresh installation, don't start PowerDNS yet - # It will be started after Django migrations create the required tables - InstallCyberPanel.stdOut("PowerDNS enabled for startup.", 1) - InstallCyberPanel.stdOut("Note: PowerDNS will start after database setup is complete.", 1) - logging.InstallLog.writeToFile("PowerDNS enabled but not started - waiting for Django migrations") - - # The service will be started later after migrations run - # or manually by the admin after installation completes - - -def Main(cwd, mysql, distro, ent, serial=None, port="8090", ftp=None, dns=None, publicip=None, remotemysql=None, - mysqlhost=None, mysqldb=None, mysqluser=None, mysqlpassword=None, mysqlport=None): - InstallCyberPanel.mysqlPassword = install_utils.generate_pass() - InstallCyberPanel.mysql_Root_password = install_utils.generate_pass() - - file_name = '/etc/cyberpanel/mysqlPassword' - - if remotemysql == 'OFF': - if os.access(file_name, os.F_OK): - password = open(file_name, 'r') - InstallCyberPanel.mysql_Root_password = password.readline() - password.close() - else: - password = open(file_name, "w") - password.writelines(InstallCyberPanel.mysql_Root_password) - password.close() - else: - mysqlData = {'remotemysql': remotemysql, 'mysqlhost': mysqlhost, 'mysqldb': mysqldb, 'mysqluser': mysqluser, - 'mysqlpassword': mysqlpassword, 'mysqlport': mysqlport} - from json import dumps - writeToFile = open(file_name, 'w') - writeToFile.write(dumps(mysqlData)) - writeToFile.close() - - if install.preFlightsChecks.debug: - print(open(file_name, 'r').read()) - time.sleep(10) - - try: - command = 'chmod 640 %s' % (file_name) - install_utils.call(command, distro, '[chmod]', - '', - 1, 0, os.EX_OSERR) - command = 'chown root:cyberpanel %s' % (file_name) - install_utils.call(command, distro, '[chmod]', - '', - 1, 0, os.EX_OSERR) - except: - pass - - if distro == centos: - InstallCyberPanel.mysqlPassword = install_utils.generate_pass() - else: - InstallCyberPanel.mysqlPassword = InstallCyberPanel.mysql_Root_password - - installer = InstallCyberPanel("/usr/local/lsws/", cwd, distro, ent, serial, port, ftp, dns, publicip, remotemysql, - mysqlhost, mysqldb, mysqluser, mysqlpassword, mysqlport) - - logging.InstallLog.writeToFile('Installing LiteSpeed Web server,40') - installer.installLiteSpeed() - if ent == 0: - installer.changePortTo80() - logging.InstallLog.writeToFile('Installing Optimized PHPs..,50') - installer.installAllPHPVersions() - if ent == 0: - installer.fix_ols_configs() - - logging.InstallLog.writeToFile('Installing Sieve for email filtering..,55') - installer.installSieve() - - logging.InstallLog.writeToFile('Installing MySQL,60') - installer.installMySQL(mysql) - installer.changeMYSQLRootPassword() - - installer.startMariaDB() - - if remotemysql == 'OFF': - if distro == ubuntu: - installer.fixMariaDB() - - mysqlUtilities.createDatabase("cyberpanel", "cyberpanel", InstallCyberPanel.mysqlPassword, publicip) - - if ftp is None: - installer.installPureFTPD() - installer.installPureFTPDConfigurations(mysql) - installer.startPureFTPD() - else: - if ftp == 'ON': - installer.installPureFTPD() - installer.installPureFTPDConfigurations(mysql) - installer.startPureFTPD() - - if dns is None: - installer.installPowerDNS() - installer.installPowerDNSConfigurations(InstallCyberPanel.mysqlPassword, mysql) - installer.startPowerDNS() - else: - if dns == 'ON': - installer.installPowerDNS() - installer.installPowerDNSConfigurations(InstallCyberPanel.mysqlPassword, mysql) - installer.startPowerDNS()