mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-01-16 12:32:05 +01:00
- Added a method to detect OS information for supported platforms (Ubuntu, Debian, AlmaLinux, Rocky, RHEL, CloudLinux, CentOS). - Introduced methods to check for specific OS types and apply necessary fixes based on detected OS. - Implemented OS-specific fixes for AlmaLinux 9, including service installations, MySQL GPG key handling, and OpenLiteSpeed setup. - Enhanced the installation process to apply OS-specific fixes before installing packages, ensuring compatibility and reducing conflicts.
1948 lines
86 KiB
Python
1948 lines
86 KiB
Python
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()
|