From 1a82700f511a358d99ab5ffa09c798723f45d134 Mon Sep 17 00:00:00 2001 From: Master3395 Date: Mon, 15 Sep 2025 01:25:52 +0200 Subject: [PATCH 01/11] Update PHP version references and improve AlmaLinux 9 compatibility - Changed PHP symlink from version 8.0 to 8.3 in various scripts to ensure compatibility with the latest PHP version. - Updated documentation links in the FAQ to point to the new community support page. - Added checks and fixes for MariaDB installation issues specific to AlmaLinux 9. - Enhanced the installation script to support additional PHP versions and improve overall installation reliability. --- README.md | 4 +- cyberpanel.sh | 2 +- faq.sh | 14 +- install/filesPermsUtilities.py.bak | 190 -- install/install.py | 2 +- install/install.py.bak | 2383 --------------- install/installCyberPanel.py | 64 +- install/venvsetup.sh.bak | 1290 -------- mailServer/mailserverManager.py.bak | 1713 ----------- .../templates/mailServer/index.html.bak | 121 - plogical/acl.py | 2 +- plogical/filesPermsUtilities.py.bak | 190 -- plogical/upgrade.py | 343 ++- plogical/upgrade.py.bak | 2696 ----------------- 14 files changed, 374 insertions(+), 8640 deletions(-) delete mode 100644 install/filesPermsUtilities.py.bak delete mode 100644 install/install.py.bak delete mode 100644 install/venvsetup.sh.bak delete mode 100644 mailServer/mailserverManager.py.bak delete mode 100644 mailServer/templates/mailServer/index.html.bak delete mode 100644 plogical/filesPermsUtilities.py.bak delete mode 100644 plogical/upgrade.py.bak diff --git a/README.md b/README.md index 9ff9ab30c..2cf210db0 100755 --- a/README.md +++ b/README.md @@ -39,9 +39,9 @@ CyberPanel supports a wide range of PHP versions across different operating syst ### ☑️ **Currently Supported PHP Versions** -- **PHP 8.5** - Latest stable version (EOL: Dec 2028) +- **PHP 8.5** - Latest stable version (EOL: Dec 2028) ⭐ **NEW!** - **PHP 8.4** - Stable version (EOL: Dec 2027) -- **PHP 8.3** - Stable version (EOL: Dec 2027) +- **PHP 8.3** - **Default version** - Stable version (EOL: Dec 2027) 🎯 - **PHP 8.2** - Stable version (EOL: Dec 2026) - **PHP 8.1** - Stable version (EOL: Dec 2025) - **PHP 8.0** - Legacy support (EOL: Nov 2023) diff --git a/cyberpanel.sh b/cyberpanel.sh index 17ce2e110..3271f7818 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -2278,7 +2278,7 @@ echo "echo \$@ > /etc/cyberpanel/adminPass" >> /usr/bin/adminPass chmod 700 /usr/bin/adminPass rm -f /usr/bin/php -ln -s /usr/local/lsws/lsphp80/bin/php /usr/bin/php +ln -s /usr/local/lsws/lsphp83/bin/php /usr/bin/php if [[ "$Server_OS" = "CentOS" ]] ; then #all centos 7/8 post change goes here diff --git a/faq.sh b/faq.sh index 3f44544e3..40f5dd8ec 100644 --- a/faq.sh +++ b/faq.sh @@ -28,7 +28,7 @@ ${BLUE}------------------------------------------------------------${NC} ${PURPLE}3.${NC} How to access LiteSpeed webadmin console ? -Please check this post: ${GREEN}https://forums.cyberpanel.net/discussion/87/tutorial-how-to-setup-and-login-to-openlitespeed-webadmin-console/p1${NC} +Please check this post: ${GREEN}https://community.cyberpanel.net/c/support/55${NC} ${BLUE}------------------------------------------------------------${NC} @@ -52,9 +52,9 @@ ${BLUE}------------------------------------------------------------${NC} ${PURPLE}6.${NC} How to raise upload limit for cyberpanel's phpMyAdmin and File Manager? -edit file ${RED}/usr/local/lsws/lsphp73/etc/php.ini${NC} for CentOS or openEuler +edit file ${RED}/usr/local/lsws/lsphp83/etc/php.ini${NC} for CentOS or openEuler -${RED}/usr/local/lsws/lsphp73/etc/php/7.3/litespeed/php.ini${NC} for Ubbuntu +${RED}/usr/local/lsws/lsphp83/etc/php/8.3/litespeed/php.ini${NC} for Ubuntu find 2 configurations: @@ -66,9 +66,9 @@ ${BLUE}------------------------------------------------------------${NC} ${PURPLE}7.${NC} How to add more IPs to my website(s) ? -For OpenLiteSpeed, please check this post: ${GREEN}https://forums.cyberpanel.net/discussion/126/tutorial-how-to-add-2nd-ip-for-websites/p1${NC} +For OpenLiteSpeed, please check this post: ${GREEN}https://community.cyberpanel.net/c/support/55${NC} -For LiteSpeed Enterprise, please check this post: ${GREEN}https://forums.cyberpanel.net/discussion/3745/tutorial-how-to-add-2nd-ip-for-litespeed-enterprise/p1${NC} +For LiteSpeed Enterprise, please check this post: ${GREEN}https://community.cyberpanel.net/c/support/55${NC} ${BLUE}------------------------------------------------------------${NC} @@ -80,7 +80,7 @@ ${BLUE}------------------------------------------------------------${NC} ${PURPLE}9.${NC} How to enable Auto-Index for my site ? -Please check this post ${GREEN}https://forums.cyberpanel.net/discussion/3850/tutorial-how-to-enable-auto-index-on-openlitespeed-and-litespeed-enterprise${NC} +Please check this post ${GREEN}https://community.cyberpanel.net/c/support/55${NC} ${BLUE}------------------------------------------------------------${NC} @@ -111,5 +111,5 @@ ${BLUE}------------------------------------------------------------${NC} ${PURPLE}13.${NC} How to enable PHP error log ? -Please check this post ${GREEN}https://forums.cyberpanel.net/discussion/3977/tutorial-how-to-enable-php-error-log/p1${NC} +Please check this post ${GREEN}https://community.cyberpanel.net/c/support/55${NC} " \ No newline at end of file diff --git a/install/filesPermsUtilities.py.bak b/install/filesPermsUtilities.py.bak deleted file mode 100644 index 07261326b..000000000 --- a/install/filesPermsUtilities.py.bak +++ /dev/null @@ -1,190 +0,0 @@ -import os -import shutil -import pathlib -import stat - - -def mkdir_p(path, exist_ok=True): - """ - Creates the directory and paths leading up to it like unix mkdir -p . - Defaults to exist_ok so if it exists were not throwing fatal errors - https://docs.python.org/3.7/library/os.html#os.makedirs - """ - if not os.path.exists(path): - print('creating directory: ' + path) - os.makedirs(path, exist_ok) - - -def chmod_digit(file_path, perms): - """ - Helper function to chmod like you would in unix without having to preface 0o or converting to octal yourself. - Credits: https://stackoverflow.com/a/60052847/1621381 - """ - try: - os.chmod(file_path, int(str(perms), base=8)) - except: - print(f'Could not chmod : {file_path} to {perms}') - pass - - -def touch(filepath: str, exist_ok=True): - """ - Touches a file like unix `touch somefile` would. - """ - try: - pathlib.Path(filepath).touch(exist_ok) - except FileExistsError: - print('Could touch : ' + filepath) - pass - - -def symlink(src, dst): - """ - Symlink a path to another if the src exists. - """ - try: - if os.access(src, os.R_OK): - os.symlink(src, dst) - except: - print(f'Could not symlink Source: {src} > Destination: {dst}') - pass - - -def chown(path, user, group=-1): - """ - Chown file/path to user/group provided. Passing -1 to user or group will leave it unchanged. - Useful if just changing user or group vs both. - """ - try: - shutil.chown(path, user, group) - except PermissionError: - print(f'Could not change permissions for: {path} to {user}:{group}') - pass - - -def recursive_chown(path, owner, group=-1): - """ - Recursively chown a path and contents to owner. - https://docs.python.org/3/library/shutil.html - """ - for dirpath, dirnames, filenames in os.walk(path): - try: - shutil.chown(dirpath, owner, group) - except PermissionError: - print('Could not change permissions for: ' + dirpath + ' to: ' + owner) - pass - for filename in filenames: - try: - shutil.chown(os.path.join(dirpath, filename), owner, group) - except PermissionError: - print('Could not change permissions for: ' + os.path.join(dirpath, filename) + ' to: ' + owner) - pass - - -def recursive_permissions(path, dir_mode=755, file_mode=644, topdir=True): - """ - Recursively chmod a path and contents to mode. - Defaults to chmod top level directory but can be optionally - toggled off when you want to chmod only contents of like a user's homedir vs homedir itself - https://docs.python.org/3.6/library/os.html#os.walk - """ - - # Here we are converting the integers to string and then to octal. - # so this function doesn't need to be called with 0o prefixed for the file and dir mode - dir_mode = int(str(dir_mode), base=8) - file_mode = int(str(file_mode), base=8) - - if topdir: - # Set chmod on top level path - try: - os.chmod(path, dir_mode) - except: - print('Could not chmod :' + path + ' to ' + str(dir_mode)) - for root, dirs, files in os.walk(path): - for d in dirs: - try: - os.chmod(os.path.join(root, d), dir_mode) - except: - print('Could not chmod :' + os.path.join(root, d) + ' to ' + str(dir_mode)) - pass - for f in files: - try: - os.chmod(os.path.join(root, f), file_mode) - except: - print('Could not chmod :' + path + ' to ' + str(file_mode)) - pass - - -# Left intentionally here for reference. -# Set recursive chown for a path -# recursive_chown(my_path, 'root', 'root') -# for changing group recursively without affecting user -# recursive_chown('/usr/local/lscp/cyberpanel/rainloop/data', -1, 'lscpd') - -# explicitly set permissions for directories/folders to 0755 and files to 0644 -# recursive_permissions(my_path, 755, 644) - -# Fix permissions and use default values -# recursive_permissions(my_path) -# ========================================================= -# Below is a helper class for getting and working with permissions -# Original credits to : https://github.com/keysemble/perfm - -def perm_octal_digit(rwx): - digit = 0 - if rwx[0] == 'r': - digit += 4 - if rwx[1] == 'w': - digit += 2 - if rwx[2] == 'x': - digit += 1 - return digit - - -class FilePerm: - def __init__(self, filepath): - filemode = stat.filemode(os.stat(filepath).st_mode) - permissions = [filemode[-9:][i:i + 3] for i in range(0, len(filemode[-9:]), 3)] - self.filepath = filepath - self.access_dict = dict(zip(['user', 'group', 'other'], [list(perm) for perm in permissions])) - - def mode(self): - mode = 0 - for shift, digit in enumerate(self.octal()[::-1]): - mode += digit << (shift * 3) - return mode - - def digits(self): - """Get the octal chmod equivalent value 755 in single string""" - return "".join(map(str, self.octal())) - - def octal(self): - """Get the octal value in a list [7, 5, 5]""" - return [perm_octal_digit(p) for p in self.access_dict.values()] - - def access_bits(self, access): - if access in self.access_dict.keys(): - r, w, x = self.access_dict[access] - return [r == 'r', w == 'w', x == 'x'] - - def update_bitwise(self, settings): - def perm_list(read=False, write=False, execute=False): - pl = ['-', '-', '-'] - if read: - pl[0] = 'r' - if write: - pl[1] = 'w' - if execute: - pl[2] = 'x' - return pl - - self.access_dict = dict( - [(access, perm_list(read=r, write=w, execute=x)) for access, [r, w, x] in settings.items()]) - os.chmod(self.filepath, self.mode()) - -# project_directory = os.path.abspath(os.path.dirname(sys.argv[0])) -# home_directory = os.path.expanduser('~') -# print(f'Path: {home_directory} Mode: {FilePerm(home_directory).mode()} Octal: {FilePerm(home_directory).octal()} ' -# f'Digits: {FilePerm(home_directory).digits()}') -# Example: Output -# Path: /home/cooluser Mode: 493 Octal: [7, 5, 5] Digits: 755 diff --git a/install/install.py b/install/install.py index 31267817b..b5c290b1c 100644 --- a/install/install.py +++ b/install/install.py @@ -2969,7 +2969,7 @@ echo $oConfig->Save() ? 'Done' : 'Error'; writeToFile.write(content) writeToFile.close() - command = '/usr/local/lsws/lsphp72/bin/php /usr/local/CyberCP/public/snappymail.php' + command = '/usr/local/lsws/lsphp83/bin/php /usr/local/CyberCP/public/snappymail.php' subprocess.call(shlex.split(command)) command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/snappymail/data" diff --git a/install/install.py.bak b/install/install.py.bak deleted file mode 100644 index 48dea0ae4..000000000 --- a/install/install.py.bak +++ /dev/null @@ -1,2383 +0,0 @@ -import sys -import subprocess -import shutil -import installLog as logging -import argparse -import os -import shlex -from firewallUtilities import FirewallUtilities -import time -import string -import random -import socket -from os.path import * -from stat import * -import stat - -VERSION = '2.3' -BUILD = 1 - -char_set = {'small': 'abcdefghijklmnopqrstuvwxyz', 'nums': '0123456789', 'big': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'} - - -def generate_pass(length=14): - chars = string.ascii_uppercase + string.ascii_lowercase + string.digits - size = length - return ''.join(random.choice(chars) for x in range(size)) - - -# There can not be peace without first a great suffering. - -# distros - -centos = 0 -ubuntu = 1 -cent8 = 2 - - -def get_distro(): - distro = -1 - distro_file = "" - if exists("/etc/lsb-release"): - distro_file = "/etc/lsb-release" - with open(distro_file) as f: - for line in f: - if line == "DISTRIB_ID=Ubuntu\n": - distro = ubuntu - - elif exists("/etc/os-release"): - distro_file = "/etc/os-release" - distro = centos - - data = open('/etc/redhat-release', 'r').read() - - if data.find('CentOS Linux release 8') > -1: - return cent8 - if data.find('AlmaLinux release 8') > -1: - return cent8 - if data.find('Rocky Linux release 8') > -1 or data.find('Rocky Linux 8') > -1 or data.find('rocky:8') > -1: - return cent8 - - else: - logging.InstallLog.writeToFile("Can't find linux release file - fatal error") - preFlightsChecks.stdOut("Can't find linux release file - fatal error") - os._exit(os.EX_UNAVAILABLE) - - if distro == -1: - logging.InstallLog.writeToFile("Can't find distro name in " + distro_file + " - fatal error") - preFlightsChecks.stdOut("Can't find distro name in " + distro_file + " - fatal error") - os._exit(os.EX_UNAVAILABLE) - - return distro - - -def get_Ubuntu_release(): - release = -1 - if exists("/etc/lsb-release"): - distro_file = "/etc/lsb-release" - with open(distro_file) as f: - for line in f: - if line[:16] == "DISTRIB_RELEASE=": - release = float(line[16:]) - - if release == -1: - preFlightsChecks.stdOut("Can't find distro release name in " + distro_file + " - fatal error", 1, 1, - os.EX_UNAVAILABLE) - - else: - logging.InstallLog.writeToFile("Can't find linux release file - fatal error") - preFlightsChecks.stdOut("Can't find linux release file - fatal error") - os._exit(os.EX_UNAVAILABLE) - - return release - - -class preFlightsChecks: - debug = 1 - cyberPanelMirror = "mirror.cyberpanel.net/pip" - cdn = 'cyberpanel.sh' - - def __init__(self, rootPath, ip, path, cwd, cyberPanelPath, distro, remotemysql=None, mysqlhost=None, mysqldb=None, - mysqluser=None, mysqlpassword=None, mysqlport=None): - self.ipAddr = ip - self.path = path - self.cwd = cwd - self.server_root_path = rootPath - self.cyberPanelPath = cyberPanelPath - self.distro = distro - self.remotemysql = remotemysql - self.mysqlhost = mysqlhost - self.mysqluser = mysqluser - self.mysqlpassword = mysqlpassword - self.mysqlport = mysqlport - self.mysqldb = mysqldb - - @staticmethod - def stdOut(message, log=0, do_exit=0, code=os.EX_OK): - print("\n\n") - print(("[" + time.strftime( - "%m.%d.%Y_%H-%M-%S") + "] #########################################################################\n")) - print(("[" + time.strftime("%m.%d.%Y_%H-%M-%S") + "] " + message + "\n")) - print(("[" + time.strftime( - "%m.%d.%Y_%H-%M-%S") + "] #########################################################################\n")) - - if log: - logging.InstallLog.writeToFile(message) - if do_exit: - logging.InstallLog.writeToFile(message) - sys.exit(code) - - def mountTemp(self): - try: - ## On OpenVZ there is an issue using .tempdisk for /tmp as it breaks network on container after reboot. - - if subprocess.check_output('systemd-detect-virt').decode("utf-8").find("openvz") > -1: - - varTmp = "/var/tmp /tmp none bind 0 0\n" - - fstab = "/etc/fstab" - writeToFile = open(fstab, "a") - writeToFile.writelines(varTmp) - writeToFile.close() - - else: - - command = "dd if=/dev/zero of=/usr/.tempdisk bs=100M count=15" - preFlightsChecks.call(command, self.distro, command, - command, - 1, 0, os.EX_OSERR) - - command = "mkfs.ext4 -F /usr/.tempdisk" - preFlightsChecks.call(command, self.distro, command, - command, - 1, 0, os.EX_OSERR) - - command = "mkdir -p /usr/.tmpbak/" - preFlightsChecks.call(command, self.distro, command, - command, - 1, 0, os.EX_OSERR) - - command = "cp -pr /tmp/* /usr/.tmpbak/" - subprocess.call(command, shell=True) - - command = "mount -o loop,rw,nodev,nosuid,noexec,nofail /usr/.tempdisk /tmp" - preFlightsChecks.call(command, self.distro, command, - command, - 1, 0, os.EX_OSERR) - - command = "chmod 1777 /tmp" - preFlightsChecks.call(command, self.distro, command, - command, - 1, 0, os.EX_OSERR) - - command = "cp -pr /usr/.tmpbak/* /tmp/" - subprocess.call(command, shell=True) - - command = "rm -rf /usr/.tmpbak" - preFlightsChecks.call(command, self.distro, command, - command, - 1, 0, os.EX_OSERR) - - command = "mount --bind /tmp /var/tmp" - preFlightsChecks.call(command, self.distro, command, - command, - 1, 0, os.EX_OSERR) - - tmp = "/usr/.tempdisk /tmp ext4 loop,rw,noexec,nosuid,nodev,nofail 0 0\n" - varTmp = "/tmp /var/tmp none bind 0 0\n" - - fstab = "/etc/fstab" - writeToFile = open(fstab, "a") - writeToFile.writelines(tmp) - writeToFile.writelines(varTmp) - writeToFile.close() - - except BaseException as msg: - preFlightsChecks.stdOut('[ERROR] ' + str(msg)) - return 0 - - @staticmethod - def pureFTPDServiceName(distro): - if distro == ubuntu: - return 'pure-ftpd-mysql' - return 'pure-ftpd' - - @staticmethod - def resFailed(distro, res): - if distro == ubuntu and res != 0: - return True - elif distro == centos and res != 0: - return True - return False - - @staticmethod - def call(command, distro, bracket, message, log=0, do_exit=0, code=os.EX_OK): - finalMessage = 'Running: %s' % (message) - preFlightsChecks.stdOut(finalMessage, log) - count = 0 - while True: - res = subprocess.call(shlex.split(command)) - - if preFlightsChecks.resFailed(distro, res): - count = count + 1 - finalMessage = 'Running %s failed. Running again, try number %s' % (message, str(count)) - preFlightsChecks.stdOut(finalMessage) - if count == 3: - fatal_message = '' - if do_exit: - fatal_message = '. Fatal error, see /var/log/installLogs.txt for full details' - - preFlightsChecks.stdOut("[ERROR] We are not able to run " + message + ' return code: ' + str(res) + - fatal_message + ".", 1, do_exit, code) - return False - else: - preFlightsChecks.stdOut('Successfully ran: %s.' % (message), log) - break - - return True - - def checkIfSeLinuxDisabled(self): - try: - command = "sestatus" - output = subprocess.check_output(shlex.split(command)).decode("utf-8") - - if output.find("disabled") > -1 or output.find("permissive") > -1: - logging.InstallLog.writeToFile("SELinux Check OK. [checkIfSeLinuxDisabled]") - preFlightsChecks.stdOut("SELinux Check OK.") - return 1 - else: - logging.InstallLog.writeToFile( - "SELinux is enabled, please disable SELinux and restart the installation!") - preFlightsChecks.stdOut("Installation failed, consult: /var/log/installLogs.txt") - os._exit(0) - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + "[checkIfSeLinuxDisabled]") - logging.InstallLog.writeToFile('[ERROR] ' + "SELinux Check OK. [checkIfSeLinuxDisabled]") - preFlightsChecks.stdOut('[ERROR] ' + "SELinux Check OK.") - return 1 - - def checkPythonVersion(self): - if sys.version_info[0] == 3: - return 1 - else: - preFlightsChecks.stdOut("You are running Unsupported python version, please install python 3.x") - os._exit(0) - - def setup_account_cyberpanel(self): - try: - - if self.distro == centos or self.distro == cent8: - command = "yum install sudo -y" - preFlightsChecks.call(command, self.distro, command, - command, - 1, 0, os.EX_OSERR) - - ## - - if self.distro == ubuntu: - self.stdOut("Add Cyberpanel user") - command = 'adduser --disabled-login --gecos "" cyberpanel' - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - else: - command = "useradd -s /bin/false cyberpanel" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - ############################### - - ### Docker User/group - - if self.distro == ubuntu: - command = 'adduser --disabled-login --gecos "" docker' - else: - command = "adduser docker" - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'groupadd docker' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'usermod -aG docker docker' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'usermod -aG docker cyberpanel' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ### - - command = "mkdir -p /etc/letsencrypt/live/" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - except BaseException as msg: - logging.InstallLog.writeToFile("[ERROR] setup_account_cyberpanel. " + str(msg)) - - def installCyberPanelRepo(self): - self.stdOut("Install Cyberpanel repo") - - if self.distro == ubuntu: - try: - filename = "enable_lst_debain_repo.sh" - command = "wget http://rpms.litespeedtech.com/debian/" + filename - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - os.chmod(filename, S_IRWXU | S_IRWXG) - - command = "./" + filename - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - except: - logging.InstallLog.writeToFile("[ERROR] Exception during CyberPanel install") - preFlightsChecks.stdOut("[ERROR] Exception during CyberPanel install") - os._exit(os.EX_SOFTWARE) - - elif self.distro == centos: - command = 'rpm -ivh http://rpms.litespeedtech.com/centos/litespeed-repo-1.2-1.el7.noarch.rpm' - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - elif self.distro == cent8: - command = 'rpm -Uvh http://rpms.litespeedtech.com/centos/litespeed-repo-1.1-1.el8.noarch.rpm' - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - def fix_selinux_issue(self): - try: - cmd = [] - - cmd.append("setsebool") - cmd.append("-P") - cmd.append("httpd_can_network_connect") - cmd.append("1") - - res = subprocess.call(cmd) - - if preFlightsChecks.resFailed(self.distro, res): - logging.InstallLog.writeToFile("fix_selinux_issue problem") - else: - pass - except: - logging.InstallLog.writeToFile("[ERROR] fix_selinux_issue problem") - - def install_psmisc(self): - self.stdOut("Install psmisc") - - if self.distro == centos or self.distro == cent8: - command = "yum -y install psmisc" - else: - command = "apt-get -y install psmisc" - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - def download_install_CyberPanel(self, mysqlPassword, mysql): - ## - - os.chdir(self.path) - - os.chdir('/usr/local') - - command = "git clone https://github.com/usmannasir/cyberpanel" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - shutil.move('cyberpanel', 'CyberCP') - - ## - - ### update password: - - if self.remotemysql == 'OFF': - passFile = "/etc/cyberpanel/mysqlPassword" - - f = open(passFile) - data = f.read() - password = data.split('\n', 1)[0] - else: - password = self.mysqlpassword - - ### Put correct mysql passwords in settings file! - - # This allows root/sudo users to be able to work with MySQL/MariaDB without hunting down the password like - # all the other control panels allow - # reference: https://oracle-base.com/articles/mysql/mysql-password-less-logins-using-option-files - mysql_my_root_cnf = '/root/.my.cnf' - mysql_root_cnf_content = """ -[client] -user=root -password="%s" -""" % password - - with open(mysql_my_root_cnf, 'w') as f: - f.write(mysql_root_cnf_content) - os.chmod(mysql_my_root_cnf, 0o600) - command = 'chown root:root %s' % mysql_my_root_cnf - subprocess.call(shlex.split(command)) - - logging.InstallLog.writeToFile("Updating /root/.my.cnf!") - - logging.InstallLog.writeToFile("Updating settings.py!") - - path = self.cyberPanelPath + "/CyberCP/settings.py" - - data = open(path, "r").readlines() - - writeDataToFile = open(path, "w") - - counter = 0 - - for items in data: - if items.find('SECRET_KEY') > -1: - SK = "SECRET_KEY = '%s'\n" % (generate_pass(50)) - writeDataToFile.writelines(SK) - continue - - if mysql == 'Two': - if items.find("'PASSWORD':") > -1: - if counter == 0: - writeDataToFile.writelines(" 'PASSWORD': '" + mysqlPassword + "'," + "\n") - counter = counter + 1 - else: - writeDataToFile.writelines(" 'PASSWORD': '" + password + "'," + "\n") - - else: - writeDataToFile.writelines(items) - else: - if items.find("'PASSWORD':") > -1: - if counter == 0: - writeDataToFile.writelines(" 'PASSWORD': '" + mysqlPassword + "'," + "\n") - counter = counter + 1 - else: - writeDataToFile.writelines(" 'PASSWORD': '" + password + "'," + "\n") - elif items.find('127.0.0.1') > -1: - writeDataToFile.writelines(" 'HOST': 'localhost',\n") - elif items.find("'PORT':'3307'") > -1: - writeDataToFile.writelines(" 'PORT': '',\n") - else: - writeDataToFile.writelines(items) - - if self.distro == ubuntu: - os.fchmod(writeDataToFile.fileno(), stat.S_IRUSR | stat.S_IWUSR) - - writeDataToFile.close() - - if self.remotemysql == 'ON': - command = "sed -i 's|localhost|%s|g' %s" % (self.mysqlhost, path) - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - # command = "sed -i 's|'mysql'|'%s'|g' %s" % (self.mysqldb, path) - # preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = "sed -i 's|root|%s|g' %s" % (self.mysqluser, path) - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = "sed -i \"s|'PORT': ''|'PORT':'%s'|g\" %s" % (self.mysqlport, path) - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - logging.InstallLog.writeToFile("settings.py updated!") - - # self.setupVirtualEnv(self.distro) - - ### Applying migrations - - os.chdir("/usr/local/CyberCP") - - command = "/usr/local/CyberPanel/bin/python manage.py makemigrations" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - ## - - command = "/usr/local/CyberPanel/bin/python manage.py migrate" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - if not os.path.exists("/usr/local/CyberCP/public"): - os.mkdir("/usr/local/CyberCP/public") - - ## Moving static content to lscpd location - command = 'mv static /usr/local/CyberCP/public/' - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - try: - path = "/usr/local/CyberCP/version.txt" - writeToFile = open(path, 'w') - writeToFile.writelines('%s\n' % (VERSION)) - writeToFile.writelines(str(BUILD)) - writeToFile.close() - except: - pass - - def fixCyberPanelPermissions(self): - - ###### fix Core CyberPanel permissions - - command = "usermod -G lscpd,lsadm,nobody lscpd" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "usermod -G lscpd,lsadm,nogroup lscpd" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "find /usr/local/CyberCP -type d -exec chmod 0755 {} \;" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "find /usr/local/CyberCP -type f -exec chmod 0644 {} \;" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "chmod -R 755 /usr/local/CyberCP/bin" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## change owner - - command = "chown -R root:root /usr/local/CyberCP" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ########### Fix LSCPD - - command = "find /usr/local/lscp -type d -exec chmod 0755 {} \;" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "find /usr/local/lscp -type f -exec chmod 0644 {} \;" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "chmod -R 755 /usr/local/lscp/bin" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "chmod -R 755 /usr/local/lscp/fcgi-bin" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/tmp" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## change owner - - command = "chown -R root:root /usr/local/lscp" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/rainloop/data" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "chmod 700 /usr/local/CyberCP/cli/cyberPanel.py" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "chmod 700 /usr/local/CyberCP/plogical/upgradeCritical.py" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "chmod 755 /usr/local/CyberCP/postfixSenderPolicy/client.py" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "chmod 640 /usr/local/CyberCP/CyberCP/settings.py" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "chown root:cyberpanel /usr/local/CyberCP/CyberCP/settings.py" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - files = ['/etc/yum.repos.d/MariaDB.repo', '/etc/pdns/pdns.conf', '/etc/systemd/system/lscpd.service', - '/etc/pure-ftpd/pure-ftpd.conf', '/etc/pure-ftpd/pureftpd-pgsql.conf', - '/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/pureftpd-ldap.conf', - '/etc/dovecot/dovecot.conf', '/usr/local/lsws/conf/httpd_config.xml', - '/usr/local/lsws/conf/modsec.conf', '/usr/local/lsws/conf/httpd.conf'] - - for items in files: - command = 'chmod 644 %s' % (items) - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - impFile = ['/etc/pure-ftpd/pure-ftpd.conf', '/etc/pure-ftpd/pureftpd-pgsql.conf', - '/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/pureftpd-ldap.conf', - '/etc/dovecot/dovecot.conf', '/etc/pdns/pdns.conf', '/etc/pure-ftpd/db/mysql.conf', - '/etc/powerdns/pdns.conf'] - - for items in impFile: - command = 'chmod 600 %s' % (items) - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'chmod 640 /etc/postfix/*.cf' - subprocess.call(command, shell=True) - - command = 'chmod 644 /etc/postfix/main.cf' - subprocess.call(command, shell=True) - - command = 'chmod 640 /etc/dovecot/*.conf' - subprocess.call(command, shell=True) - - command = 'chmod 644 /etc/dovecot/dovecot.conf' - subprocess.call(command, shell=True) - - command = 'chmod 640 /etc/dovecot/dovecot-sql.conf.ext' - subprocess.call(command, shell=True) - - command = 'chmod 644 /etc/postfix/dynamicmaps.cf' - subprocess.call(command, shell=True) - - fileM = ['/usr/local/lsws/FileManager/', '/usr/local/CyberCP/install/FileManager', - '/usr/local/CyberCP/serverStatus/litespeed/FileManager', '/usr/local/lsws/Example/html/FileManager'] - - for items in fileM: - try: - shutil.rmtree(items) - except: - pass - - command = 'chmod 755 /etc/pure-ftpd/' - subprocess.call(command, shell=True) - - command = 'chmod +x /usr/local/CyberCP/plogical/renew.py' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'chmod +x /usr/local/CyberCP/CLManager/CLPackages.py' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - clScripts = ['/usr/local/CyberCP/CLScript/panel_info.py', '/usr/local/CyberCP/CLScript/CloudLinuxPackages.py', - '/usr/local/CyberCP/CLScript/CloudLinuxUsers.py', - '/usr/local/CyberCP/CLScript/CloudLinuxDomains.py', - '/usr/local/CyberCP/CLScript/CloudLinuxResellers.py', '/usr/local/CyberCP/CLScript/CloudLinuxAdmins.py', - '/usr/local/CyberCP/CLScript/CloudLinuxDB.py', '/usr/local/CyberCP/CLScript/UserInfo.py'] - - for items in clScripts: - command = 'chmod +x %s' % (items) - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'chmod 600 /usr/local/CyberCP/plogical/adminPass.py' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'chmod 600 /etc/cagefs/exclude/cyberpanelexclude' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "find /usr/local/CyberCP/ -name '*.pyc' -delete" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - if self.distro == cent8 or self.distro == centos: - command = 'chown root:pdns /etc/pdns/pdns.conf' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'chmod 640 /etc/pdns/pdns.conf' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'chmod 640 /usr/local/lscp/cyberpanel/logs/access.log' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'mkdir -p/usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/configs/' - - rainloopinipath = '/usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/configs/application.ini' - - command = 'chmod 600 /usr/local/CyberCP/public/rainloop.php' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ### - - WriteToFile = open('/etc/fstab', 'a') - WriteToFile.write('proc /proc proc defaults,hidepid=2 0 0\n') - WriteToFile.close() - - command = 'mount -o remount,rw,hidepid=2 /proc' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## symlink protection - - writeToFile = open('/usr/lib/sysctl.d/50-default.conf', 'a') - writeToFile.writelines('fs.protected_hardlinks = 1\n') - writeToFile.writelines('fs.protected_symlinks = 1\n') - writeToFile.close() - - command = 'sysctl --system' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'chmod 700 %s' % ('/home/cyberpanel') - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - destPrivKey = "/usr/local/lscp/conf/key.pem" - - command = 'chmod 600 %s' % (destPrivKey) - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ### - - def install_unzip(self): - self.stdOut("Install unzip") - try: - if self.distro == centos or self.distro == cent8: - command = 'yum -y install unzip' - else: - command = 'apt-get -y install unzip' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_unzip]") - - def install_zip(self): - self.stdOut("Install zip") - try: - if self.distro == centos or self.distro == cent8: - command = 'yum -y install zip' - else: - command = 'apt-get -y install zip' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_zip]") - - def download_install_phpmyadmin(self): - try: - - if not os.path.exists("/usr/local/CyberCP/public"): - os.mkdir("/usr/local/CyberCP/public") - - command = 'wget -O /usr/local/CyberCP/public/phpmyadmin.zip https://github.com/usmannasir/cyberpanel/raw/stable/phpmyadmin.zip' - - preFlightsChecks.call(command, self.distro, '[download_install_phpmyadmin]', - command, 1, 0, os.EX_OSERR) - - command = 'unzip /usr/local/CyberCP/public/phpmyadmin.zip -d /usr/local/CyberCP/public/' - preFlightsChecks.call(command, self.distro, '[download_install_phpmyadmin]', - command, 1, 0, os.EX_OSERR) - - command = 'mv /usr/local/CyberCP/public/phpMyAdmin-*-all-languages /usr/local/CyberCP/public/phpmyadmin' - subprocess.call(command, shell=True) - - command = 'rm -f /usr/local/CyberCP/public/phpmyadmin.zip' - preFlightsChecks.call(command, self.distro, '[download_install_phpmyadmin]', - command, 1, 0, os.EX_OSERR) - - ## Write secret phrase - - rString = ''.join([random.choice(string.ascii_letters + string.digits) for n in range(32)]) - - data = open('/usr/local/CyberCP/public/phpmyadmin/config.sample.inc.php', 'r').readlines() - - writeToFile = open('/usr/local/CyberCP/public/phpmyadmin/config.inc.php', 'w') - - writeE = 1 - - phpMyAdminContent = """ -$cfg['Servers'][$i]['AllowNoPassword'] = false; -$cfg['Servers'][$i]['auth_type'] = 'signon'; -$cfg['Servers'][$i]['SignonSession'] = 'SignonSession'; -$cfg['Servers'][$i]['SignonURL'] = 'phpmyadminsignin.php'; -$cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout'; -""" - - for items in data: - if items.find('blowfish_secret') > -1: - writeToFile.writelines( - "$cfg['blowfish_secret'] = '" + rString + "'; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */\n") - elif items.find('/* Authentication type */') > -1: - writeToFile.writelines(items) - writeToFile.write(phpMyAdminContent) - writeE = 0 - elif items.find("$cfg['Servers'][$i]['AllowNoPassword']") > -1: - writeE = 1 - else: - if writeE: - writeToFile.writelines(items) - - writeToFile.writelines("$cfg['TempDir'] = '/usr/local/CyberCP/public/phpmyadmin/tmp';\n") - - writeToFile.close() - - os.mkdir('/usr/local/CyberCP/public/phpmyadmin/tmp') - - command = 'chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin' - preFlightsChecks.call(command, self.distro, '[chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin]', - 'chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin', 1, 0, os.EX_OSERR) - - if self.remotemysql == 'ON': - command = "sed -i 's|'localhost'|'%s'|g' %s" % ( - self.mysqlhost, '/usr/local/CyberCP/public/phpmyadmin/config.inc.php') - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'cp /usr/local/CyberCP/plogical/phpmyadminsignin.php /usr/local/CyberCP/public/phpmyadmin/phpmyadminsignin.php' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - if self.remotemysql == 'ON': - command = "sed -i 's|localhost|%s|g' /usr/local/CyberCP/public/phpmyadmin/phpmyadminsignin.php" % ( - self.mysqlhost) - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [download_install_phpmyadmin]") - return 0 - - ###################################################### Email setup - - def install_postfix_dovecot(self): - self.stdOut("Install dovecot - first remove postfix") - - try: - if self.distro == centos: - command = 'yum remove postfix -y' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - elif self.distro == ubuntu: - command = 'apt-get -y remove postfix' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - self.stdOut("Install dovecot - do the install") - - if self.distro == centos: - command = 'yum install --enablerepo=gf-plus -y postfix3 postfix3-ldap postfix3-mysql postfix3-pcre' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - elif self.distro == cent8: - - command = 'dnf --nogpg install -y https://mirror.ghettoforge.net/distributions/gf/gf-release-latest.gf.el8.noarch.rpm' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'dnf install --enablerepo=gf-plus postfix3 postfix3-mysql -y' - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - else: - command = 'apt-get -y install debconf-utils' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - file_name = self.cwd + '/pf.unattend.text' - pf = open(file_name, 'w') - pf.write('postfix postfix/mailname string ' + str(socket.getfqdn() + '\n')) - pf.write('postfix postfix/main_mailer_type string "Internet Site"\n') - pf.close() - command = 'debconf-set-selections ' + file_name - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'apt-get -y install postfix postfix-mysql' - # os.remove(file_name) - - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - ## - - if self.distro == centos: - command = 'yum --enablerepo=gf-plus -y install dovecot23 dovecot23-mysql' - elif self.distro == cent8: - command = 'dnf install --enablerepo=gf-plus dovecot23 dovecot23-mysql -y' - else: - command = 'apt-get -y install dovecot-mysql dovecot-imapd dovecot-pop3d' - - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_postfix_dovecot]") - return 0 - - return 1 - - def setup_email_Passwords(self, mysqlPassword, mysql): - try: - - logging.InstallLog.writeToFile("Setting up authentication for Postfix and Dovecot...") - - os.chdir(self.cwd) - - mysql_virtual_domains = "email-configs-one/mysql-virtual_domains.cf" - mysql_virtual_forwardings = "email-configs-one/mysql-virtual_forwardings.cf" - mysql_virtual_mailboxes = "email-configs-one/mysql-virtual_mailboxes.cf" - mysql_virtual_email2email = "email-configs-one/mysql-virtual_email2email.cf" - dovecotmysql = "email-configs-one/dovecot-sql.conf.ext" - - ### update password: - - data = open(dovecotmysql, "r").readlines() - - writeDataToFile = open(dovecotmysql, "w") - - if mysql == 'Two': - dataWritten = "connect = host=127.0.0.1 dbname=cyberpanel user=cyberpanel password=" + mysqlPassword + " port=3307\n" - else: - dataWritten = "connect = host=localhost dbname=cyberpanel user=cyberpanel password=" + mysqlPassword + " port=3306\n" - - for items in data: - if items.find("connect") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - writeDataToFile.close() - - ### update password: - - data = open(mysql_virtual_domains, "r").readlines() - - writeDataToFile = open(mysql_virtual_domains, "w") - - dataWritten = "password = " + mysqlPassword + "\n" - - for items in data: - if items.find("password") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - writeDataToFile.close() - - ### update password: - - data = open(mysql_virtual_forwardings, "r").readlines() - - writeDataToFile = open(mysql_virtual_forwardings, "w") - - dataWritten = "password = " + mysqlPassword + "\n" - - for items in data: - if items.find("password") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - writeDataToFile.close() - - ### update password: - - data = open(mysql_virtual_mailboxes, "r").readlines() - - writeDataToFile = open(mysql_virtual_mailboxes, "w") - - dataWritten = "password = " + mysqlPassword + "\n" - - for items in data: - if items.find("password") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - writeDataToFile.close() - - ### update password: - - data = open(mysql_virtual_email2email, "r").readlines() - - writeDataToFile = open(mysql_virtual_email2email, "w") - - dataWritten = "password = " + mysqlPassword + "\n" - - for items in data: - if items.find("password") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - writeDataToFile.close() - - if self.remotemysql == 'ON': - command = "sed -i 's|host=localhost|host=%s|g' %s" % (self.mysqlhost, dovecotmysql) - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = "sed -i 's|port=3306|port=%s|g' %s" % (self.mysqlport, dovecotmysql) - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - ## - - command = "sed -i 's|localhost|%s:%s|g' %s" % (self.mysqlhost, self.mysqlport, mysql_virtual_domains) - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = "sed -i 's|localhost|%s:%s|g' %s" % ( - self.mysqlhost, self.mysqlport, mysql_virtual_forwardings) - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = "sed -i 's|localhost|%s:%s|g' %s" % ( - self.mysqlhost, self.mysqlport, mysql_virtual_mailboxes) - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = "sed -i 's|localhost|%s:%s|g' %s" % ( - self.mysqlhost, self.mysqlport, mysql_virtual_email2email) - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - logging.InstallLog.writeToFile("Authentication for Postfix and Dovecot set.") - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR]' + str(msg) + " [setup_email_Passwords]") - return 0 - - return 1 - - def centos_lib_dir_to_ubuntu(self, filename, old, new): - try: - fd = open(filename, 'r') - lines = fd.readlines() - fd.close() - fd = open(filename, 'w') - centos_prefix = old - ubuntu_prefix = new - for line in lines: - index = line.find(centos_prefix) - if index != -1: - line = line[:index] + ubuntu_prefix + line[index + len(centos_prefix):] - fd.write(line) - fd.close() - except IOError as err: - self.stdOut( - "[ERROR] Error converting: " + filename + " from centos defaults to ubuntu defaults: " + str(err), 1, - 1, os.EX_OSERR) - - def setup_postfix_dovecot_config(self, mysql): - try: - logging.InstallLog.writeToFile("Configuring postfix and dovecot...") - - os.chdir(self.cwd) - - mysql_virtual_domains = "/etc/postfix/mysql-virtual_domains.cf" - mysql_virtual_forwardings = "/etc/postfix/mysql-virtual_forwardings.cf" - mysql_virtual_mailboxes = "/etc/postfix/mysql-virtual_mailboxes.cf" - mysql_virtual_email2email = "/etc/postfix/mysql-virtual_email2email.cf" - main = "/etc/postfix/main.cf" - master = "/etc/postfix/master.cf" - dovecot = "/etc/dovecot/dovecot.conf" - dovecotmysql = "/etc/dovecot/dovecot-sql.conf.ext" - - if os.path.exists(mysql_virtual_domains): - os.remove(mysql_virtual_domains) - - if os.path.exists(mysql_virtual_forwardings): - os.remove(mysql_virtual_forwardings) - - if os.path.exists(mysql_virtual_mailboxes): - os.remove(mysql_virtual_mailboxes) - - if os.path.exists(mysql_virtual_email2email): - os.remove(mysql_virtual_email2email) - - if os.path.exists(main): - os.remove(main) - - if os.path.exists(master): - os.remove(master) - - if os.path.exists(dovecot): - os.remove(dovecot) - - if os.path.exists(dovecotmysql): - os.remove(dovecotmysql) - - ###############Getting SSL - - command = 'openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout /etc/postfix/key.pem -out /etc/postfix/cert.pem' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout /etc/dovecot/key.pem -out /etc/dovecot/cert.pem' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - # Cleanup config files for ubuntu - if self.distro == ubuntu: - preFlightsChecks.stdOut("Cleanup postfix/dovecot config files", 1) - - self.centos_lib_dir_to_ubuntu("email-configs-one/master.cf", "/usr/libexec/", "/usr/lib/") - self.centos_lib_dir_to_ubuntu("email-configs-one/main.cf", "/usr/libexec/postfix", - "/usr/lib/postfix/sbin") - - ########### Copy config files - - shutil.copy("email-configs-one/mysql-virtual_domains.cf", "/etc/postfix/mysql-virtual_domains.cf") - shutil.copy("email-configs-one/mysql-virtual_forwardings.cf", - "/etc/postfix/mysql-virtual_forwardings.cf") - shutil.copy("email-configs-one/mysql-virtual_mailboxes.cf", "/etc/postfix/mysql-virtual_mailboxes.cf") - shutil.copy("email-configs-one/mysql-virtual_email2email.cf", - "/etc/postfix/mysql-virtual_email2email.cf") - shutil.copy("email-configs-one/main.cf", main) - shutil.copy("email-configs-one/master.cf", master) - shutil.copy("email-configs-one/dovecot.conf", dovecot) - shutil.copy("email-configs-one/dovecot-sql.conf.ext", dovecotmysql) - - ########### Set custom settings - - # We are going to leverage postconfig -e to edit the settings for hostname - command = "postconf -e 'myhostname = %s'" % (str(socket.getfqdn())) - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - # We are explicitly going to use sed to set the hostname default from "myhostname = server.example.com" - # to the fqdn from socket if the default is still found - command = "sed -i 's|server.example.com|%s|g' %s" % (str(socket.getfqdn()), main) - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ######################################## Permissions - - command = 'chmod o= /etc/postfix/mysql-virtual_domains.cf' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'chmod o= /etc/postfix/mysql-virtual_forwardings.cf' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'chmod o= /etc/postfix/mysql-virtual_mailboxes.cf' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'chmod o= /etc/postfix/mysql-virtual_email2email.cf' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'chmod o= ' + main - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'chmod o= ' + master - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ####################################### - - command = 'chgrp postfix /etc/postfix/mysql-virtual_domains.cf' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'chgrp postfix /etc/postfix/mysql-virtual_forwardings.cf' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - ## - - command = 'chgrp postfix /etc/postfix/mysql-virtual_mailboxes.cf' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'chgrp postfix /etc/postfix/mysql-virtual_email2email.cf' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'chgrp postfix ' + main - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'chgrp postfix ' + master - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ######################################## users and groups - - command = 'groupadd -g 5000 vmail' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'useradd -g vmail -u 5000 vmail -d /home/vmail -m' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ######################################## Further configurations - - # hostname = socket.gethostname() - - ################################### Restart postix - - command = 'systemctl enable postfix.service' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'systemctl start postfix.service' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ######################################## Permissions - - command = 'chgrp dovecot /etc/dovecot/dovecot-sql.conf.ext' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'chmod o= /etc/dovecot/dovecot-sql.conf.ext' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ################################### Restart dovecot - - command = 'systemctl enable dovecot.service' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'systemctl start dovecot.service' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'systemctl restart postfix.service' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## chaging permissions for main.cf - - command = "chmod 755 " + main - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - if self.distro == ubuntu: - command = "mkdir -p /etc/pki/dovecot/private/" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "mkdir -p /etc/pki/dovecot/certs/" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "mkdir -p /etc/opendkim/keys/" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "sed -i 's/auth_mechanisms = plain/#auth_mechanisms = plain/g' /etc/dovecot/conf.d/10-auth.conf" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## Ubuntu 18.10 ssl_dh for dovecot 2.3.2.1 - - if get_Ubuntu_release() == 18.10: - dovecotConf = '/etc/dovecot/dovecot.conf' - - data = open(dovecotConf, 'r').readlines() - writeToFile = open(dovecotConf, 'w') - for items in data: - if items.find('ssl_key = -1: - writeToFile.writelines(items) - writeToFile.writelines('ssl_dh = -1: - writeToFile.writelines( - " $sCustomDataPath = '/usr/local/lscp/cyberpanel/rainloop/data';\n") - else: - writeToFile.writelines(items) - - writeToFile.close() - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [downoad_and_install_rainloop]") - return 0 - - return 1 - - ###################################################### Email setup ends! - - def reStartLiteSpeed(self): - command = '%sbin/lswsctrl restart' % (self.server_root_path) - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - def removeUfw(self): - try: - preFlightsChecks.stdOut("Checking to see if ufw firewall is installed (will be removed)", 1) - status = subprocess.check_output(shlex.split('ufw status')).decode("utf-8") - preFlightsChecks.stdOut("ufw current status: " + status + "...will be removed") - except BaseException as msg: - preFlightsChecks.stdOut("[ERROR] Expected access to ufw not available, do not need to remove it", 1) - return True - try: - preFlightsChecks.call('apt-get -y remove ufw', self.distro, '[remove_ufw]', 'Remove ufw firewall ' + - '(using firewalld)', 1, 0, os.EX_OSERR) - except: - pass - return True - - def installFirewalld(self): - - if self.distro == ubuntu: - self.removeUfw() - - try: - preFlightsChecks.stdOut("Enabling Firewall!") - - if self.distro == ubuntu: - command = 'apt-get -y install firewalld' - else: - command = 'yum -y install firewalld' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ###### - if self.distro == centos: - # Not available in ubuntu - command = 'systemctl restart dbus' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'systemctl restart systemd-logind' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'systemctl start firewalld' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ########## - - command = 'systemctl enable firewalld' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - FirewallUtilities.addRule("tcp", "8090") - FirewallUtilities.addRule("tcp", "7080") - FirewallUtilities.addRule("tcp", "80") - FirewallUtilities.addRule("tcp", "443") - FirewallUtilities.addRule("tcp", "21") - FirewallUtilities.addRule("tcp", "25") - FirewallUtilities.addRule("tcp", "587") - FirewallUtilities.addRule("tcp", "465") - FirewallUtilities.addRule("tcp", "110") - FirewallUtilities.addRule("tcp", "143") - FirewallUtilities.addRule("tcp", "993") - FirewallUtilities.addRule("tcp", "995") - FirewallUtilities.addRule("udp", "53") - FirewallUtilities.addRule("tcp", "53") - FirewallUtilities.addRule("udp", "443") - FirewallUtilities.addRule("tcp", "40110-40210") - - logging.InstallLog.writeToFile("FirewallD installed and configured!") - preFlightsChecks.stdOut("FirewallD installed and configured!") - - except OSError as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installFirewalld]") - return 0 - except ValueError as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installFirewalld]") - return 0 - - return 1 - - ## from here - - def installLSCPD(self): - try: - - logging.InstallLog.writeToFile("Starting LSCPD installation..") - - os.chdir(self.cwd) - - if self.distro == ubuntu: - command = "apt-get -y install gcc g++ make autoconf rcs" - else: - command = 'yum -y install gcc gcc-c++ make autoconf glibc' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - if self.distro == ubuntu: - command = "apt-get -y install libpcre3 libpcre3-dev openssl libexpat1 libexpat1-dev libgeoip-dev" \ - " zlib1g zlib1g-dev libudns-dev whichman curl" - else: - command = 'yum -y install pcre-devel openssl-devel expat-devel geoip-devel zlib-devel udns-devel' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'tar zxf lscp.tar.gz -C /usr/local/' - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - ### - - lscpdPath = '/usr/local/lscp/bin/lscpd' - - command = 'cp -f /usr/local/CyberCP/lscpd-0.3.1 /usr/local/lscp/bin/lscpd-0.3.1' - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = 'rm -f /usr/local/lscp/bin/lscpd' - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = 'mv /usr/local/lscp/bin/lscpd-0.3.1 /usr/local/lscp/bin/lscpd' - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - command = 'chmod 755 %s' % (lscpdPath) - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) - - ## - - command = 'openssl req -newkey rsa:1024 -new -nodes -x509 -days 3650 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout /usr/local/lscp/conf/key.pem -out /usr/local/lscp/conf/cert.pem' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - try: - os.remove("/usr/local/lscp/fcgi-bin/lsphp") - shutil.copy("/usr/local/lsws/lsphp73/bin/lsphp", "/usr/local/lscp/fcgi-bin/lsphp") - except: - pass - - if self.distro == centos or self.distro == cent8: - command = 'adduser lscpd -M -d /usr/local/lscp' - else: - command = 'useradd lscpd -M -d /usr/local/lscp' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - if self.distro == centos or self.distro == cent8: - command = 'groupadd lscpd' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - # Added group in useradd for Ubuntu - - command = 'usermod -a -G lscpd lscpd' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'usermod -a -G lsadm lscpd' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - try: - os.mkdir('/usr/local/lscp/cyberpanel') - except: - pass - try: - os.mkdir('/usr/local/lscp/cyberpanel/logs') - except: - pass - - # self.setupComodoRules() - - logging.InstallLog.writeToFile("LSCPD successfully installed!") - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installLSCPD]") - - def setupComodoRules(self): - try: - os.chdir(self.cwd) - - extractLocation = "/usr/local/lscp/modsec" - - command = "mkdir -p /usr/local/lscp/modsec" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - try: - if os.path.exists('comodo.tar.gz'): - os.remove('comodo.tar.gz') - except: - pass - - command = "wget https://cyberpanel.net/modsec/comodo.tar.gz" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "tar -zxf comodo.tar.gz -C /usr/local/lscp/modsec" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ### - - modsecConfPath = "/usr/local/lscp/conf/modsec.conf" - - modsecConfig = """ - module mod_security { - ls_enabled 0 - modsecurity on - modsecurity_rules ` - SecDebugLogLevel 0 - SecDebugLog /usr/local/lscp/logs/modsec.log - SecAuditEngine on - SecAuditLogRelevantStatus "^(?:5|4(?!04))" - SecAuditLogParts AFH - SecAuditLogType Serial - SecAuditLog /usr/local/lscp/logs/auditmodsec.log - SecRuleEngine Off - ` - modsecurity_rules_file /usr/local/lscp/modsec/comodo/modsecurity.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/00_Init_Initialization.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/01_Init_AppsInitialization.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/02_Global_Generic.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/03_Global_Agents.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/04_Global_Domains.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/05_Global_Backdoor.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/06_XSS_XSS.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/07_Global_Other.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/08_Bruteforce_Bruteforce.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/09_HTTP_HTTP.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/10_HTTP_HTTPDoS.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/11_HTTP_Protocol.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/12_HTTP_Request.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/13_Outgoing_FilterGen.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/14_Outgoing_FilterASP.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/15_Outgoing_FilterPHP.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/16_Outgoing_FilterSQL.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/17_Outgoing_FilterOther.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/18_Outgoing_FilterInFrame.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/19_Outgoing_FiltersEnd.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/20_PHP_PHPGen.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/21_SQL_SQLi.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/22_Apps_Joomla.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/23_Apps_JComponent.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/24_Apps_WordPress.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/25_Apps_WPPlugin.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/26_Apps_WHMCS.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/27_Apps_Drupal.conf - modsecurity_rules_file /usr/local/lscp/modsec/comodo/28_Apps_OtherApps.conf - } - """ - - writeToFile = open(modsecConfPath, 'w') - writeToFile.write(modsecConfig) - writeToFile.close() - - ### - - command = "chown -R lscpd:lscpd /usr/local/lscp/modsec" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - return 1 - - except BaseException as msg: - logging.InstallLog.writeToFile("[ERROR]" + str(msg)) - return 0 - - def setupPort(self): - try: - ### - bindConfPath = "/usr/local/lscp/conf/bind.conf" - - writeToFile = open(bindConfPath, 'w') - writeToFile.write("*:" + self.port) - writeToFile.close() - - except: - return 0 - - def setupPythonWSGI(self): - try: - - command = "wget http://www.litespeedtech.com/packages/lsapi/wsgi-lsapi-1.6.tgz" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "tar xf wsgi-lsapi-1.6.tgz" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - os.chdir("wsgi-lsapi-1.6") - - command = "/usr/local/CyberPanel/bin/python ./configure.py" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "make" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - if not os.path.exists('/usr/local/CyberCP/bin/'): - os.mkdir('/usr/local/CyberCP/bin/') - - command = "cp lswsgi /usr/local/CyberCP/bin/" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - os.chdir(self.cwd) - - except: - return 0 - - def setupLSCPDDaemon(self): - try: - - preFlightsChecks.stdOut("Trying to setup LSCPD Daemon!") - logging.InstallLog.writeToFile("Trying to setup LSCPD Daemon!") - - os.chdir(self.cwd) - - shutil.copy("lscpd/lscpd.service", "/etc/systemd/system/lscpd.service") - shutil.copy("lscpd/lscpdctrl", "/usr/local/lscp/bin/lscpdctrl") - - ## - - command = 'chmod +x /usr/local/lscp/bin/lscpdctrl' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - path = "/usr/local/lscpd/admin/" - - command = "mkdir -p " + path - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - path = "/usr/local/CyberCP/conf/" - command = "mkdir -p " + path - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - path = "/usr/local/CyberCP/conf/token_env" - writeToFile = open(path, "w") - writeToFile.write("abc\n") - writeToFile.close() - - command = "chmod 600 " + path - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - command = 'systemctl enable lscpd.service' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - count = 0 - - # In Ubuntu, the library that lscpd looks for is libpcre.so.1, but the one it installs is libpcre.so.3... - if self.distro == ubuntu: - command = 'ln -s /lib/x86_64-linux-gnu/libpcre.so.3 /lib/x86_64-linux-gnu/libpcre.so.1' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = 'systemctl start lscpd' - # preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - preFlightsChecks.stdOut("LSCPD Daemon Set!") - - logging.InstallLog.writeToFile("LSCPD Daemon Set!") - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setupLSCPDDaemon]") - return 0 - - return 1 - - def setup_cron(self): - - try: - ## first install crontab - - if self.distro == centos or self.distro == cent8: - command = 'yum install cronie -y' - else: - command = 'apt-get -y install cron' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - if self.distro == centos or self.distro == cent8: - command = 'systemctl enable crond' - else: - command = 'systemctl enable cron' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - if self.distro == centos or self.distro == cent8: - command = 'systemctl start crond' - else: - command = 'systemctl start cron' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - CentOSPath = '/etc/redhat-release' - - if os.path.exists(CentOSPath): - cronPath = '/var/spool/cron/root' - else: - cronPath = '/var/spool/cron/crontabs/root' - - cronFile = open(cronPath, "w") - - content = """ -0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/findBWUsage.py >/dev/null 2>&1 -0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py hourlyCleanup >/dev/null 2>&1 -0 0 1 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py monthlyCleanup >/dev/null 2>&1 -0 2 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/upgradeCritical.py >/dev/null 2>&1 -0 2 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/renew.py >/dev/null 2>&1 -7 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null -0 0 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Daily -0 0 * * 0 /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Weekly -*/3 * * * * if ! find /home/*/public_html/ -maxdepth 2 -type f -newer /usr/local/lsws/cgid -name '.htaccess' -exec false {} +; then /usr/local/lsws/bin/lswsctrl restart; fi -""" - - cronFile.write(content) - cronFile.close() - - ### Check and remove OLS restart if lsws ent detected - - if not os.path.exists('/usr/local/lsws/bin/openlitespeed'): - - data = open(cronPath, 'r').readlines() - - writeToFile = open(cronPath, 'w') - - for items in data: - if items.find('-maxdepth 2 -type f -newer') > -1: - pass - else: - writeToFile.writelines(items) - - writeToFile.close() - - if not os.path.exists(CentOSPath): - command = 'chmod 600 %s' % (cronPath) - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - if self.distro == centos or self.distro == cent8: - command = 'systemctl restart crond.service' - else: - command = 'systemctl restart cron.service' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setup_cron]") - return 0 - - def install_default_keys(self): - try: - path = "/root/.ssh" - - if not os.path.exists(path): - os.mkdir(path) - - command = "ssh-keygen -f /root/.ssh/cyberpanel -t rsa -N ''" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_default_keys]") - return 0 - - def install_rsync(self): - try: - if self.distro == centos or self.distro == cent8: - command = 'yum -y install rsync' - else: - command = 'apt-get -y install rsync' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_rsync]") - return 0 - - def test_Requests(self): - try: - import requests - getVersion = requests.get('https://cyberpanel.net/version.txt') - latest = getVersion.json() - except BaseException as msg: - - command = "pip uninstall --yes urllib3" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "pip uninstall --yes requests" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "pip install http://mirror.cyberpanel.net/urllib3-1.22.tar.gz" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "pip install http://mirror.cyberpanel.net/requests-2.18.4.tar.gz" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - def installation_successfull(self): - print("###################################################################") - print(" CyberPanel Successfully Installed ") - print(" ") - - print(" ") - print(" ") - - print((" Visit: https://" + self.ipAddr + ":8090 ")) - print(" Username: admin ") - print(" Password: 1234567 ") - - print("###################################################################") - - def modSecPreReqs(self): - try: - - pathToRemoveGarbageFile = os.path.join(self.server_root_path, "modules/mod_security.so") - os.remove(pathToRemoveGarbageFile) - - except OSError as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [modSecPreReqs]") - return 0 - - def installOpenDKIM(self): - try: - if self.distro == centos: - command = 'yum -y install opendkim' - elif self.distro == cent8: - command = 'dnf install opendkim -y' - else: - command = 'apt-get -y install opendkim' - - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - if self.distro == ubuntu: - command = 'apt install opendkim-tools -y' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'mkdir -p /etc/opendkim/keys/' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [installOpenDKIM]") - return 0 - - return 1 - - def configureOpenDKIM(self): - try: - - ## Configure OpenDKIM specific settings - - openDKIMConfigurePath = "/etc/opendkim.conf" - - configData = """ -Mode sv -Canonicalization relaxed/simple -KeyTable refile:/etc/opendkim/KeyTable -SigningTable refile:/etc/opendkim/SigningTable -ExternalIgnoreList refile:/etc/opendkim/TrustedHosts -InternalHosts refile:/etc/opendkim/TrustedHosts -""" - - writeToFile = open(openDKIMConfigurePath, 'a') - writeToFile.write(configData) - writeToFile.close() - - ## Configure postfix specific settings - - postfixFilePath = "/etc/postfix/main.cf" - - configData = """ -smtpd_milters = inet:127.0.0.1:8891 -non_smtpd_milters = $smtpd_milters -milter_default_action = accept -""" - - writeToFile = open(postfixFilePath, 'a') - writeToFile.write(configData) - writeToFile.close() - - if self.distro == ubuntu: - data = open(openDKIMConfigurePath, 'r').readlines() - writeToFile = open(openDKIMConfigurePath, 'w') - for items in data: - if items.find('Socket') > -1 and items.find('local:') and items[0] != '#': - writeToFile.writelines('Socket inet:8891@localhost\n') - else: - writeToFile.writelines(items) - writeToFile.close() - - #### Restarting Postfix and OpenDKIM - - command = "systemctl start opendkim" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "systemctl enable opendkim" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## - - command = "systemctl start postfix" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - except BaseException as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [configureOpenDKIM]") - return 0 - - return 1 - - def setupCLI(self): - command = "ln -s /usr/local/CyberCP/cli/cyberPanel.py /usr/bin/cyberpanel" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "chmod +x /usr/local/CyberCP/cli/cyberPanel.py" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - def setupPHPAndComposer(self): - try: - - if self.distro == ubuntu: - if not os.access('/usr/local/lsws/lsphp70/bin/php', os.R_OK): - if os.access('/usr/local/lsws/lsphp70/bin/php7.0', os.R_OK): - os.symlink('/usr/local/lsws/lsphp70/bin/php7.0', '/usr/local/lsws/lsphp70/bin/php') - if not os.access('/usr/local/lsws/lsphp71/bin/php', os.R_OK): - if os.access('/usr/local/lsws/lsphp71/bin/php7.1', os.R_OK): - os.symlink('/usr/local/lsws/lsphp71/bin/php7.1', '/usr/local/lsws/lsphp71/bin/php') - if not os.access('/usr/local/lsws/lsphp72/bin/php', os.R_OK): - if os.access('/usr/local/lsws/lsphp72/bin/php7.2', os.R_OK): - os.symlink('/usr/local/lsws/lsphp72/bin/php7.2', '/usr/local/lsws/lsphp72/bin/php') - - command = "cp /usr/local/lsws/lsphp71/bin/php /usr/bin/" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - os.chdir(self.cwd) - - command = "chmod +x composer.sh" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = "./composer.sh" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - except OSError as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setupPHPAndComposer]") - return 0 - - @staticmethod - def installOne(package): - res = subprocess.call(shlex.split('apt-get -y install ' + package)) - if res != 0: - preFlightsChecks.stdOut("Error #" + str(res) + ' installing:' + package + '. This may not be an issue ' \ - 'but may affect installation of something later', - 1) - - return res # Though probably not used - - @staticmethod - def enableDisableDNS(state): - try: - servicePath = '/home/cyberpanel/powerdns' - - if state == 'off': - - command = 'sudo systemctl stop pdns' - subprocess.call(shlex.split(command)) - - command = 'sudo systemctl disable pdns' - subprocess.call(shlex.split(command)) - - try: - os.remove(servicePath) - except: - pass - - else: - writeToFile = open(servicePath, 'w+') - writeToFile.close() - - except OSError as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [enableDisableDNS]") - return 0 - - @staticmethod - def enableDisableEmail(state): - try: - servicePath = '/home/cyberpanel/postfix' - - if state == 'off': - - command = 'sudo systemctl stop postfix' - subprocess.call(shlex.split(command)) - - command = 'sudo systemctl disable postfix' - subprocess.call(shlex.split(command)) - - try: - os.remove(servicePath) - except: - pass - - else: - writeToFile = open(servicePath, 'w+') - writeToFile.close() - - except OSError as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [enableDisableEmail]") - return 0 - - @staticmethod - def enableDisableFTP(state, distro): - try: - servicePath = '/home/cyberpanel/pureftpd' - - if state == 'off': - - command = 'sudo systemctl stop ' + preFlightsChecks.pureFTPDServiceName(distro) - subprocess.call(shlex.split(command)) - - command = 'sudo systemctl disable ' + preFlightsChecks.pureFTPDServiceName(distro) - subprocess.call(shlex.split(command)) - - try: - os.remove(servicePath) - except: - pass - - else: - writeToFile = open(servicePath, 'w+') - writeToFile.close() - - except OSError as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [enableDisableEmail]") - return 0 - - @staticmethod - def setUpFirstAccount(): - try: - command = 'python /usr/local/CyberCP/plogical/adminPass.py --password 1234567' - subprocess.call(shlex.split(command)) - except: - pass - - def installRestic(self): - try: - - CentOSPath = '/etc/redhat-release' - - if os.path.exists(CentOSPath): - command = 'yum install -y yum-plugin-copr' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - command = 'yum copr enable -y copart/restic' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - command = 'yum install -y restic' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - else: - command = 'apt-get update -y' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'apt-get install restic -y' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - except: - pass - - def installCLScripts(self): - try: - - CentOSPath = '/etc/redhat-release' - - if os.path.exists(CentOSPath): - command = 'mkdir -p /opt/cpvendor/etc/' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - content = """[integration_scripts] - -panel_info = /usr/local/CyberCP/CLScript/panel_info.py -packages = /usr/local/CyberCP/CLScript/CloudLinuxPackages.py -users = /usr/local/CyberCP/CLScript/CloudLinuxUsers.py -domains = /usr/local/CyberCP/CLScript/CloudLinuxDomains.py -resellers = /usr/local/CyberCP/CLScript/CloudLinuxResellers.py -admins = /usr/local/CyberCP/CLScript/CloudLinuxAdmins.py -db_info = /usr/local/CyberCP/CLScript/CloudLinuxDB.py - -[lvemanager_config] -ui_user_info =/usr/local/CyberCP/CLScript/UserInfo.py -base_path = /usr/local/lvemanager -run_service = 1 -service_port = 9000 -""" - - writeToFile = open('/opt/cpvendor/etc/integration.ini', 'w') - writeToFile.write(content) - writeToFile.close() - - command = 'mkdir -p /etc/cagefs/exclude' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - content = """cyberpanel -docker -ftpuser -lscpd -opendkim -pdns -vmail -""" - - writeToFile = open('/etc/cagefs/exclude/cyberpanelexclude', 'w') - writeToFile.write(content) - writeToFile.close() - - except: - pass - - def installAcme(self): - command = 'wget -O - https://get.acme.sh | sh' - subprocess.call(command, shell=True) - - command = '/root/.acme.sh/acme.sh --upgrade --auto-upgrade' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - def installRedis(self): - if self.distro == ubuntu: - command = 'apt install redis-server -y' - elif self.distro == centos: - command = 'yum install redis -y' - else: - command = 'dnf install redis -y' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - ## install redis conf - - redisConf = '/usr/local/lsws/conf/dvhost_redis.conf' - - writeToFile = open(redisConf, 'w') - writeToFile.write('127.0.0.1,6379,\n') - writeToFile.close() - - ## - - os.chdir(self.cwd) - - confPath = '/usr/local/lsws/conf/' - - if os.path.exists('%shttpd.conf' % (confPath)): - os.remove('%shttpd.conf' % (confPath)) - - shutil.copy('litespeed/httpd-redis.conf', '%shttpd.conf' % (confPath)) - - ## start and enable - - command = 'systemctl start redis' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - command = 'systemctl enable redis' - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - def disablePackegeUpdates(self): - if self.distro == centos: - mainConfFile = '/etc/yum.conf' - content = 'exclude=MariaDB-client MariaDB-common MariaDB-devel MariaDB-server MariaDB-shared ' \ - 'pdns pdns-backend-mysql dovecot dovecot-mysql postfix3 postfix3-ldap postfix3-mysql ' \ - 'postfix3-pcre restic opendkim libopendkim pure-ftpd ftp\n' - - writeToFile = open(mainConfFile, 'a') - writeToFile.write(content) - writeToFile.close() - - -def main(): - parser = argparse.ArgumentParser(description='CyberPanel Installer') - parser.add_argument('publicip', help='Please enter public IP for your VPS or dedicated server.') - parser.add_argument('--mysql', help='Specify number of MySQL instances to be used.') - parser.add_argument('--postfix', help='Enable or disable Email Service.') - parser.add_argument('--powerdns', help='Enable or disable DNS Service.') - parser.add_argument('--ftp', help='Enable or disable ftp Service.') - parser.add_argument('--ent', help='Install LS Ent or OpenLiteSpeed') - parser.add_argument('--serial', help='Install LS Ent or OpenLiteSpeed') - parser.add_argument('--port', help='LSCPD Port') - parser.add_argument('--redis', help='vHosts on Redis - Requires LiteSpeed Enterprise') - parser.add_argument('--remotemysql', help='Opt to choose local or remote MySQL') - parser.add_argument('--mysqlhost', help='MySQL host if remote is chosen.') - parser.add_argument('--mysqldb', help='MySQL DB if remote is chosen.') - parser.add_argument('--mysqluser', help='MySQL user if remote is chosen.') - parser.add_argument('--mysqlpassword', help='MySQL password if remote is chosen.') - parser.add_argument('--mysqlport', help='MySQL port if remote is chosen.') - - args = parser.parse_args() - - logging.InstallLog.ServerIP = args.publicip - logging.InstallLog.writeToFile("Starting CyberPanel installation..,10") - preFlightsChecks.stdOut("Starting CyberPanel installation..") - - if args.ent is None: - ent = 0 - preFlightsChecks.stdOut("OpenLiteSpeed web server will be installed.") - else: - if args.ent == 'ols': - ent = 0 - preFlightsChecks.stdOut("OpenLiteSpeed web server will be installed.") - else: - preFlightsChecks.stdOut("LiteSpeed Enterprise web server will be installed.") - ent = 1 - if args.serial is not None: - serial = args.serial - preFlightsChecks.stdOut("LiteSpeed Enterprise Serial detected: " + serial) - else: - preFlightsChecks.stdOut("Installation failed, please specify LiteSpeed Enterprise key using --serial") - os._exit(0) - - ## Writing public IP - - try: - os.mkdir("/etc/cyberpanel") - except: - pass - - machineIP = open("/etc/cyberpanel/machineIP", "w") - machineIP.writelines(args.publicip) - machineIP.close() - - cwd = os.getcwd() - - if args.remotemysql == 'ON': - remotemysql = args.remotemysql - mysqlhost = args.mysqlhost - mysqluser = args.mysqluser - mysqlpassword = args.mysqlpassword - mysqlport = args.mysqlport - mysqldb = args.mysqldb - - if preFlightsChecks.debug: - print('mysqlhost: %s, mysqldb: %s, mysqluser: %s, mysqlpassword: %s, mysqlport: %s' % ( - mysqlhost, mysqldb, mysqluser, mysqlpassword, mysqlport)) - time.sleep(10) - - else: - remotemysql = args.remotemysql - mysqlhost = '' - mysqluser = '' - mysqlpassword = '' - mysqlport = '' - mysqldb = '' - - distro = get_distro() - checks = preFlightsChecks("/usr/local/lsws/", args.publicip, "/usr/local", cwd, "/usr/local/CyberCP", distro, - remotemysql, mysqlhost, mysqldb, mysqluser, mysqlpassword, mysqlport) - checks.mountTemp() - - if args.port is None: - port = "8090" - else: - port = args.port - - if args.mysql is None: - mysql = 'One' - preFlightsChecks.stdOut("Single MySQL instance version will be installed.") - else: - mysql = args.mysql - preFlightsChecks.stdOut("Dobule MySQL instance version will be installed.") - - checks.checkPythonVersion() - checks.setup_account_cyberpanel() - checks.installCyberPanelRepo() - - import installCyberPanel - - if ent == 0: - installCyberPanel.Main(cwd, mysql, distro, ent, None, port, args.ftp, args.powerdns, args.publicip, remotemysql, - mysqlhost, mysqldb, mysqluser, mysqlpassword, mysqlport) - else: - installCyberPanel.Main(cwd, mysql, distro, ent, serial, port, args.ftp, args.powerdns, args.publicip, - remotemysql, mysqlhost, mysqldb, mysqluser, mysqlpassword, mysqlport) - - checks.setupPHPAndComposer() - checks.fix_selinux_issue() - checks.install_psmisc() - - if args.postfix is None: - checks.install_postfix_dovecot() - checks.setup_email_Passwords(installCyberPanel.InstallCyberPanel.mysqlPassword, mysql) - checks.setup_postfix_dovecot_config(mysql) - else: - if args.postfix == 'ON': - checks.install_postfix_dovecot() - checks.setup_email_Passwords(installCyberPanel.InstallCyberPanel.mysqlPassword, mysql) - checks.setup_postfix_dovecot_config(mysql) - - checks.install_unzip() - checks.install_zip() - checks.install_rsync() - - checks.installFirewalld() - checks.install_default_keys() - - checks.download_install_CyberPanel(installCyberPanel.InstallCyberPanel.mysqlPassword, mysql) - checks.downoad_and_install_raindloop() - checks.download_install_phpmyadmin() - checks.setupCLI() - checks.setup_cron() - checks.installRestic() - checks.installAcme() - - ## Install and Configure OpenDKIM. - - if args.postfix is None: - checks.installOpenDKIM() - checks.configureOpenDKIM() - else: - if args.postfix == 'ON': - checks.installOpenDKIM() - checks.configureOpenDKIM() - - checks.modSecPreReqs() - checks.installLSCPD() - checks.setupPort() - checks.setupPythonWSGI() - checks.setupLSCPDDaemon() - - if args.redis is not None: - checks.installRedis() - - if args.postfix is not None: - checks.enableDisableEmail(args.postfix.lower()) - else: - preFlightsChecks.stdOut("Postfix will be installed and enabled.") - checks.enableDisableEmail('on') - - if args.powerdns is not None: - checks.enableDisableDNS(args.powerdns.lower()) - else: - preFlightsChecks.stdOut("PowerDNS will be installed and enabled.") - checks.enableDisableDNS('on') - - if args.ftp is not None: - checks.enableDisableFTP(args.ftp.lower(), distro) - else: - preFlightsChecks.stdOut("Pure-FTPD will be installed and enabled.") - checks.enableDisableFTP('on', distro) - - checks.installCLScripts() - # checks.disablePackegeUpdates() - - try: - # command = 'mkdir -p /usr/local/lscp/cyberpanel/rainloop/data/data/default/configs/' - # subprocess.call(shlex.split(command)) - - writeToFile = open('/usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/configs/application.ini', 'a') - - writeToFile.write(""" -[security] -admin_login = "admin" -admin_password = "12345" -""") - writeToFile.close() - - import randomPassword - - content = """SetPassword('%s'); -echo $oConfig->Save() ? 'Done' : 'Error'; - -?>""" % (randomPassword.generate_pass()) - - writeToFile = open('/usr/local/CyberCP/public/rainloop.php', 'w') - writeToFile.write(content) - writeToFile.close() - - command = '/usr/local/lsws/lsphp72/bin/php /usr/local/CyberCP/public/rainloop.php' - subprocess.call(shlex.split(command)) - - command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/rainloop/data" - subprocess.call(shlex.split(command)) - except: - pass - - checks.fixCyberPanelPermissions() - - logging.InstallLog.writeToFile("CyberPanel installation successfully completed!,80") - - -if __name__ == "__main__": - main() diff --git a/install/installCyberPanel.py b/install/installCyberPanel.py index 6f39a6788..620c641a8 100644 --- a/install/installCyberPanel.py +++ b/install/installCyberPanel.py @@ -44,6 +44,62 @@ FetchCloudLinuxAlmaVersionVersion = install_utils.FetchCloudLinuxAlmaVersionVers class InstallCyberPanel: mysql_Root_password = "" mysqlPassword = "" + + def is_almalinux9(self): + """Check if running on AlmaLinux 9""" + if os.path.exists('/etc/almalinux-release'): + try: + with open('/etc/almalinux-release', 'r') as f: + content = f.read() + return 'release 9' in content + except: + return False + return False + + 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 + 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) + + # Install MariaDB packages + self.stdOut("Installing MariaDB packages...", 1) + mariadb_packages = "MariaDB-server MariaDB-client MariaDB-backup MariaDB-devel" + command = f"dnf install -y {mariadb_packages}" + install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + + self.stdOut("AlmaLinux 9 MariaDB fixes completed", 1) + + except Exception as e: + self.stdOut(f"Error applying AlmaLinux 9 MariaDB fixes: {str(e)}", 0) CloudLinux8 = 0 def install_package(self, package_name, options=""): @@ -335,7 +391,7 @@ class InstallCyberPanel: return self.reStartLiteSpeed() def installAllPHPVersions(self): - php_versions = ['71', '72', '73', '74', '80', '81', '82', '83'] + php_versions = ['71', '72', '73', '74', '80', '81', '82', '83', '84', '85'] if self.distro == ubuntu: # Install base PHP 7.x packages @@ -563,6 +619,12 @@ gpgcheck=1 if self.remotemysql == 'OFF': ############## Start mariadb ###################### + + # Check if AlmaLinux 9 and apply fixes + if self.is_almalinux9(): + self.stdOut("AlmaLinux 9 detected - applying MariaDB fixes", 1) + self.fix_almalinux9_mariadb() + self.manage_service('mariadb', 'start') ############## Enable mariadb at system startup ###################### diff --git a/install/venvsetup.sh.bak b/install/venvsetup.sh.bak deleted file mode 100644 index 85ab9ab41..000000000 --- a/install/venvsetup.sh.bak +++ /dev/null @@ -1,1290 +0,0 @@ -#!/bin/bash - -#CyberPanel installer script for Ubuntu 18.04 and CentOS 7.X -DEV="OFF" -BRANCH="stable" -POSTFIX_VARIABLE="ON" -POWERDNS_VARIABLE="ON" -PUREFTPD_VARIABLE="ON" -PROVIDER="undefined" -SERIAL_NO="" -DIR=$(pwd) -TEMP=$(curl --silent https://cyberpanel.net/version.txt) -CP_VER1=${TEMP:12:3} -CP_VER2=${TEMP:25:1} -SERVER_OS="CentOS" -VERSION="OLS" -LICENSE_KEY="" -KEY_SIZE="" -ADMIN_PASS="1234567" -MEMCACHED="ON" -REDIS="ON" -TOTAL_RAM=$(free -m | awk '/Mem\:/ { print $2 }') - -license_validation() { -CURRENT_DIR=$(pwd) - -if [ -f /root/cyberpanel-tmp ] ; then -rm -rf /root/cyberpanel-tmp -fi - -mkdir /root/cyberpanel-tmp -cd /root/cyberpanel-tmp -wget -q https://$DOWNLOAD_SERVER/litespeed/lsws-$LSWS_STABLE_VER-ent-x86_64-linux.tar.gz -tar xzvf lsws-$LSWS_STABLE_VER-ent-x86_64-linux.tar.gz > /dev/null -cd /root/cyberpanel-tmp/lsws-$LSWS_STABLE_VER/conf -if [[ $LICENSE_KEY == "TRIAL" ]] ; then -wget -q http://license.litespeedtech.com/reseller/trial.key -sed -i "s|writeSerial = open('lsws-5.4.2/serial.no', 'w')|command = 'wget -q --output-document=./lsws-$LSWS_STABLE_VER/trial.key http://license.litespeedtech.com/reseller/trial.key'|g" $CURRENT_DIR/installCyberPanel.py -sed -i 's|writeSerial.writelines(self.serial)|subprocess.call(command, shell=True)|g' $CURRENT_DIR/installCyberPanel.py -sed -i 's|writeSerial.close()||g' $CURRENT_DIR/installCyberPanel.py -else -echo $LICENSE_KEY > serial.no -fi - -cd /root/cyberpanel-tmp/lsws-$LSWS_STABLE_VER/bin - -if [[ $LICENSE_KEY == "TRIAL" ]] ; then - if ./lshttpd -V |& grep "ERROR" ; then - echo -e "\n\nIt apeears to have some issue with license , please check above result..." - exit - fi - LICENSE_KEY="1111-2222-3333-4444" -else - if ./lshttpd -r |& grep "ERROR" ; then - ./lshttpd -r - echo -e "\n\nIt apeears to have some issue with license , please check above result..." - exit - fi -fi -echo -e "License seems valid..." -cd /root/cyberpanel-tmp -rm -rf lsws-$LSWS_STABLE_VER* -cd $CURRENT_DIR -rm -rf /root/cyberpanel-tmp -} - -special_change(){ -sed -i 's|cyberpanel.sh|'$DOWNLOAD_SERVER'|g' install.py -sed -i 's|mirror.cyberpanel.net|'$DOWNLOAD_SERVER'|g' install.py -sed -i 's|git clone https://github.com/usmannasir/cyberpanel|echo downloaded|g' install.py -#change to CDN first, regardless country -sed -i 's|http://|https://|g' install.py - -LATEST_URL="https://update.litespeedtech.com/ws/latest.php" -#LATEST_URL="https://cyberpanel.sh/latest.php" -curl --silent -o /tmp/lsws_latest $LATEST_URL 2>/dev/null -LSWS_STABLE_LINE=`cat /tmp/lsws_latest | grep LSWS_STABLE` -LSWS_STABLE_VER=`expr "$LSWS_STABLE_LINE" : '.*LSWS_STABLE=\(.*\) BUILD .*'` - -if [[ $SERVER_COUNTRY == "CN" ]] ; then -#line1="$(grep -n "github.com/usmannasir/cyberpanel" install.py | head -n 1 | cut -d: -f1)" -#line2=$((line1 - 1)) -#sed -i "${line2}i\ \ \ \ \ \ \ \ subprocess.call(command, shell=True)" install.py -#sed -i "${line2}i\ \ \ \ \ \ \ \ command = 'tar xzvf cyberpanel-git.tar.gz'" install.py -#sed -i "${line2}i\ \ \ \ \ \ \ \ subprocess.call(command, shell=True)" install.py -#sed -i "${line2}i\ \ \ \ \ \ \ \ command = 'wget cyberpanel.sh/cyberpanel-git.tar.gz'" install.py -sed -i 's|wget https://rpms.litespeedtech.com/debian/|wget --no-check-certificate https://rpms.litespeedtech.com/debian/|g' install.py -sed -i 's|https://repo.powerdns.com/repo-files/centos-auth-42.repo|https://'$DOWNLOAD_SERVER'/powerdns/powerdns.repo|g' installCyberPanel.py -sed -i 's|https://www.rainloop.net/repository/webmail/rainloop-community-latest.zip|https://'$DOWNLOAD_SERVER'/misc/rainloop-community-latest.zip|g' install.py - -sed -i 's|rpm -ivh https://rpms.litespeedtech.com/centos/litespeed-repo-1.1-1.el7.noarch.rpm|curl -o /etc/yum.repos.d/litespeed.repo https://'$DOWNLOAD_SERVER'/litespeed/litespeed.repo|g' install.py - - -sed -i 's|https://copr.fedorainfracloud.org/coprs/copart/restic/repo/epel-7/copart-restic-epel-7.repo|https://'$DOWNLOAD_SERVER'/restic/restic.repo|g' install.py - -sed -i 's|yum -y install https://cyberpanel.sh/gf-release-latest.gf.el7.noarch.rpm|wget -O /etc/yum.repos.d/gf.repo https://'$DOWNLOAD_SERVER'/gf-plus/gf.repo|g' install.py -sed -i 's|dovecot-2.3-latest|dovecot-2.3-latest-mirror|g' install.py -sed -i 's|git clone https://github.com/usmannasir/cyberpanel|wget https://cyberpanel.sh/cyberpanel-git.tar.gz \&\& tar xzvf cyberpanel-git.tar.gz|g' install.py -sed -i 's|https://repo.dovecot.org/ce-2.3-latest/centos/$releasever/RPMS/$basearch|https://'$DOWNLOAD_SERVER'/dovecot/|g' install.py -sed -i 's|'$DOWNLOAD_SERVER'|cyberpanel.sh|g' install.py -sed -i 's|https://www.litespeedtech.com/packages/5.0/lsws-5.4.2-ent-x86_64-linux.tar.gz|https://'$DOWNLOAD_SERVER'/litespeed/lsws-'$LSWS_STABLE_VER'-ent-x86_64-linux.tar.gz|g' installCyberPanel.py -# global change for CN , regardless provider and system - - if [[ $SERVER_OS == "CentOS" ]] ; then - DIR=$(pwd) - cd $DIR/mysql - echo "[mariadb-tsinghua] -name = MariaDB -baseurl = https://mirrors.tuna.tsinghua.edu.cn/mariadb/yum/10.1/centos7-amd64 -gpgkey = https://mirrors.tuna.tsinghua.edu.cn/mariadb/yum//RPM-GPG-KEY-MariaDB -gpgcheck = 1" > MariaDB.repo -#above to set mariadb db to Tsinghua repo - cd $DIR - sed -i 's|https://www.litespeedtech.com/packages/5.0/lsws-5.3.5-ent-x86_64-linux.tar.gz|https://cyberpanel.sh/packages/5.0/lsws-5.3.5-ent-x86_64-linux.tar.gz|g' installCyberPanel.py - mkdir /root/.pip - cat << EOF > /root/.pip/pip.conf -[global] -index-url = https://mirrors.aliyun.com/pypi/simple/ -EOF - echo -e "\nSet to Aliyun pip repo..." - cat << EOF > composer.sh -#!/usr/bin/env bash -php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" -php composer-setup.php -php -r "unlink('composer-setup.php');" -mv composer.phar /usr/bin/composer - -if [ ! -d /root/.config ]; then -mkdir /root/.config -fi - -if [ ! -d /root/.config/composer ]; then -mkdir /root/.config/composer -fi - -echo '{ - "bitbucket-oauth": {}, - "github-oauth": {}, - "gitlab-oauth": {}, - "gitlab-token": {}, - "http-basic": {} -} -' > /root/.config/composer/auth.json - -echo '{ - "config": {}, - "repositories": { - "packagist": { - "type": "composer", - "url": "https://mirrors.aliyun.com/composer/" - } - } -} -' > /root/.config/composer/config.json -composer clear-cache -EOF - fi - - - if [[ $SERVER_OS == "Ubuntu" ]] ; then - echo $'\n89.208.248.38 rpms.litespeedtech.com\n' >> /etc/hosts - echo -e "Mirror server set..." - pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ - cat << EOF > /root/.pip/pip.conf -[global] -index-url = https://mirrors.aliyun.com/pypi/simple/ -EOF - echo -e "\nSet to Aliyun pip repo..." - if [[ $PROVIDER == "Tencent Cloud" ]] ; then - #tencent cloud and ubuntu system - echo -e "\n Tencent Cloud detected ... bypass default repository" - cp /etc/apt/sources.list /etc/apt/sources.list-backup - #backup original sources list - cat << 'EOF' > /etc/apt/sources.list -deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse -EOF - DEBIAN_FRONTEND=noninteractive apt update -y - pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ - cat << EOF > composer.sh -#!/usr/bin/env bash -php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" -php composer-setup.php -php -r "unlink('composer-setup.php');" -mv composer.phar /usr/bin/composer - -if [ ! -d /root/.config ]; then -mkdir /root/.config -fi - -if [ ! -d /root/.config/composer ]; then -mkdir /root/.config/composer -fi - -echo '{ - "bitbucket-oauth": {}, - "github-oauth": {}, - "gitlab-oauth": {}, - "gitlab-token": {}, - "http-basic": {} -} -' > /root/.config/composer/auth.json - -echo '{ - "config": {}, - "repositories": { - "packagist": { - "type": "composer", - "url": "https://mirrors.cloud.tencent.com/composer/" - } - } -} -' > /root/.config/composer/config.json -composer clear-cache -EOF - else - cat << EOF > composer.sh -#!/usr/bin/env bash -php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" -php composer-setup.php -php -r "unlink('composer-setup.php');" -mv composer.phar /usr/bin/composer - -if [ ! -d /root/.config ]; then -mkdir /root/.config -fi - -if [ ! -d /root/.config/composer ]; then -mkdir /root/.config/composer -fi - -echo '{ - "bitbucket-oauth": {}, - "github-oauth": {}, - "gitlab-oauth": {}, - "gitlab-token": {}, - "http-basic": {} -} -' > /root/.config/composer/auth.json - -echo '{ - "config": {}, - "repositories": { - "packagist": { - "type": "composer", - "url": "https://packagist.phpcomposer.com" - } - } -} -' > /root/.config/composer/config.json -composer clear-cache -EOF - fi - fi -fi -} - - -system_tweak() { -if [[ $SERVER_OS == "CentOS" ]] ; then - setenforce 0 - sed -i 's/SELINUX=enforcing/SELINUX=permissive/g' /etc/selinux/config -fi - -if ! grep -q "pid_max" /etc/rc.local; then - if [[ $SERVER_OS == "CentOS" ]] ; then - echo "echo 1000000 > /proc/sys/kernel/pid_max -echo 1 > /sys/kernel/mm/ksm/run" >> /etc/rc.d/rc.local - chmod +x /etc/rc.d/rc.local - else - echo "echo 1000000 > /proc/sys/kernel/pid_max -echo 1 > /sys/kernel/mm/ksm/run" >> /etc/rc.local - chmod +x /etc/rc.local - fi - echo "fs.file-max = 65535" >> /etc/sysctl.conf - sysctl -p > /dev/null - echo "* soft nofile 65535 -* hard nofile 65535 -root soft nofile 65535 -root hard nofile 65535 -* soft nproc 65535 -* hard nproc 65535 -root soft nproc 65535 -root hard nproc 65535" >> /etc/security/limits.conf -fi - -#sed -i 's|#DefaultLimitNOFILE=|DefaultLimitNOFILE=65535|g' /etc/systemd/system.conf - - -TOTAL_SWAP=$(free -m | awk '/^Swap:/ { print $2 }') -SET_SWAP=$((TOTAL_RAM - TOTAL_SWAP)) -SWAP_FILE=/cyberpanel.swap - -if [ ! -f $SWAP_FILE ] ; then - if [[ $TOTAL_SWAP -gt $TOTAL_RAM ]] || [[ $TOTAL_SWAP -eq $TOTAL_RAM ]] ; then - echo "SWAP check..." - else - if [[ $SET_SWAP -gt "2049" ]] ; then - SET_SWAP="2048" - else - echo "Checking SWAP..." - fi - fallocate --length ${SET_SWAP}MiB $SWAP_FILE - chmod 600 $SWAP_FILE - mkswap $SWAP_FILE - swapon $SWAP_FILE - echo "${SWAP_FILE} swap swap sw 0 0" | sudo tee -a /etc/fstab - sysctl vm.swappiness=10 - echo "vm.swappiness = 10" >> /etc/sysctl.conf - echo "SWAP set..." - fi -fi -} - - -install_required() { -echo -e "\nInstalling necessary components..." -if [[ $SERVER_OS == "CentOS" ]] ; then - rpm --import https://$DOWNLOAD_SERVER/mariadb/RPM-GPG-KEY-MariaDB - rpm --import https://$DOWNLOAD_SERVER/litespeed/RPM-GPG-KEY-litespeed - rpm --import https://$DOWNLOAD_SERVER/powerdns/FD380FBB-pub.asc - rpm --import http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-7 - rpm --import https://$DOWNLOAD_SERVER/gf-plus/RPM-GPG-KEY-gf.el7 - rpm --import https://repo.dovecot.org/DOVECOT-REPO-GPG - rpm --import https://copr-be.cloud.fedoraproject.org/results/copart/restic/pubkey.gpg - yum autoremove epel-release -y - rm -f /etc/yum.repos.d/epel.repo - rm -f /etc/yum.repos.d/epel.repo.rpmsave - yum clean all - yum update -y - yum install epel-release -y - yum install -y wget strace htop net-tools telnet curl which bc telnet htop libevent-devel gcc python-devel libattr-devel xz-devel gpgme-devel mariadb-devel curl-devel python-pip git - if [[ $DEV == "ON" ]] ; then - yum -y install yum-utils - yum -y groupinstall development - yum -y install https://centos7.iuscommunity.org/ius-release.rpm - yum -y install python36u python36u-pip python36u-devel - fi -fi - -if [[ $SERVER_OS == "Ubuntu" ]] ; then - apt update -y - DEBIAN_FRONTEND=noninteractive apt upgrade -y - DEBIAN_FRONTEND=noninteractive apt install -y htop telnet python-mysqldb python-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libattr1 libattr1-dev liblzma-dev libgpgme-dev libmariadbclient-dev libcurl4-gnutls-dev libssl-dev nghttp2 libnghttp2-dev idn2 libidn2-dev libidn2-0-dev librtmp-dev libpsl-dev nettle-dev libgnutls28-dev libldap2-dev libgssapi-krb5-2 libk5crypto3 libkrb5-dev libcomerr2 libldap2-dev python-gpg python python-minimal python-setuptools virtualenv python-dev python-pip git - if [[ $DEV == "ON" ]] ; then - DEBIAN_FRONTEND=noninteractive apt install -y python3-pip - DEBIAN_FRONTEND=noninteractive apt install -y build-essential libssl-dev libffi-dev python3-dev - DEBIAN_FRONTEND=noninteractive apt install -y python3-venv - fi -fi -} - -memcached_installation() { -if [[ $SERVER_OS == "CentOS" ]] ; then - yum install -y lsphp73-memcached lsphp72-memcached lsphp71-memcached lsphp70-memcached lsphp56-pecl-memcached lsphp55-pecl-memcached lsphp54-pecl-memcached - if [[ $TOTAL_RAM -eq "2048" ]] || [[ $TOTAL_RAM -gt "2048" ]] ; then - yum groupinstall "Development Tools" -y - yum install autoconf automake zlib-devel openssl-devel expat-devel pcre-devel libmemcached-devel cyrus-sasl* -y - wget https://$DOWNLOAD_SERVER/litespeed/lsmcd.tar.gz - tar xzvf lsmcd.tar.gz - DIR=$(pwd) - cd $DIR/lsmcd - ./fixtimestamp.sh - ./configure CFLAGS=" -O3" CXXFLAGS=" -O3" - make - make install - systemctl enable lsmcd - systemctl start lsmcd - cd $DIR - else - yum install -y memcached - sed -i 's|OPTIONS=""|OPTIONS="-l 127.0.0.1 -U 0"|g' /etc/sysconfig/memcached - systemctl enable memcached - systemctl start memcached - fi -fi -if [[ $SERVER_OS == "Ubuntu" ]] ; then - DEBIAN_FRONTEND=noninteractive apt install -y lsphp73-memcached lsphp72-memcached lsphp71-memcached lsphp70-memcached - if [[ $TOTAL_RAM -eq "2048" ]] || [[ $TOTAL_RAM -gt "2048" ]] ; then - DEBIAN_FRONTEND=noninteractive apt install build-essential zlib1g-dev libexpat1-dev openssl libssl-dev libsasl2-dev libpcre3-dev git -y - wget https://$DOWNLOAD/litespeed/lsmcd.tar.gz - tar xzvf lsmcd.tar.gz - DIR=$(pwd) - cd $DIR/lsmcd - ./fixtimestamp.sh - ./configure CFLAGS=" -O3" CXXFLAGS=" -O3" - make - make install - cd $DIR - systemctl enable lsmcd - systemctl start lsmcd - else - DEBIAN_FRONTEND=noninteractive apt install -y memcached - systemctl enable memcached - systemctl start memcached - fi -fi - -if ps -aux | grep "lsmcd" | grep -v grep ; then - echo -e "\n\nLiteSpeed Memcached installed and running..." -fi - -if ps -aux | grep "memcached" | grep -v grep ; then - echo -e "\n\nMemcached installed and running..." -fi - -} - -redis_installation() { -if [[ $SERVER_OS == "CentOS" ]] ; then - yum install -y lsphp73-redis lsphp72-redis lsphp71-redis lsphp70-redis lsphp56-redis lsphp55-redis lsphp54-redis redis -fi -if [[ $SERVER_OS == "Ubuntu" ]] ; then - DEBIAN_FRONTEND=noninteractive apt install -y lsphp73-redis lsphp72-redis lsphp71-redis lsphp70-redis redis -fi - -if ifconfig -a | grep inet6 ; then - echo -e "\n IPv6 detected..." -else - sed -i 's|bind 127.0.0.1 ::1|bind 127.0.0.1|g' /etc/redis/redis.conf - echo -e "\n no IPv6 detected..." -fi - -if [[ $SERVER_OS == "CentOS" ]] ; then - systemctl enable redis - systemctl start redis -fi - -if [[ $SERVER_OS == "Ubuntu" ]] ; then - systemctl enable redis-server - systemctl start redis-server -fi - -if ps -aux | grep "redis" | grep -v grep ; then - echo -e "\n\nRedis installed and running..." -fi -} - -check_provider() { - -if hash dmidecode > /dev/null 2>&1 ; then - if [ "$(dmidecode -s bios-vendor)" = 'Google' ] ; then - PROVIDER='Google Cloud Platform' - elif [ "$(dmidecode -s bios-vendor)" = 'DigitalOcean' ] ; then - PROVIDER='Digital Ocean' - elif [ "$(dmidecode -s system-product-name | cut -c 1-7)" = 'Alibaba' ] ; then - PROVIDER='Alibaba Cloud' - elif [ "$(dmidecode -s system-manufacturer)" = 'Microsoft Corporation' ] ; then - PROVIDER='Microsoft Azure' - elif [ -d /usr/local/qcloud ] ; then - PROVIDER='Tencent Cloud' - else - PROVIDER='undefined' - fi -else - PROVIDER='undefined' -fi - -if [ "$(cat /sys/devices/virtual/dmi/id/product_uuid | cut -c 1-3)" = 'EC2' ] && [ -d /home/ubuntu ]; then - PROVIDER='Amazon Web Service' -fi - -} - - -check_OS() { -echo -e "\nChecking OS..." -OUTPUT=$(cat /etc/*release) -if echo $OUTPUT | grep -q "CentOS Linux 7" ; then - echo -e "\nDetecting CentOS 7.X...\n" - SERVER_OS="CentOS" -elif echo $OUTPUT | grep -q "CloudLinux 7" ; then - echo -e "\nDetecting CloudLinux 7.X...\n" - SERVER_OS="CentOS" -elif echo $OUTPUT | grep -q "Ubuntu 18.04" ; then - echo -e "\nDetecting Ubuntu 18.04...\n" - SERVER_OS="Ubuntu" -else - cat /etc/*release - echo -e "\nUnable to detect your OS...\n" - echo -e "\nCyberPanel is supported on Ubuntu 18.04, CentOS 7.x and CloudLinux 7.x...\n" - exit 1 -fi -} - -check_root() { -echo -e "Checking root privileges...\n" -if [[ $(id -u) != 0 ]] > /dev/null; then - echo -e "You must use root account to do this" - echo -e "or run following command: (do NOT miss the quotes)" - echo -e "\e[31msudo su -c \"sh <(curl https://cyberpanel.sh || wget -O - https://cyberpanel.sh)\"\e[39m" - exit 1 -else - echo -e "You are runing as root...\n" -fi -} - -check_panel() { -if [ -d /usr/local/cpanel ]; then - echo -e "\ncPanel detected...exit...\n" - exit 1 -fi -if [ -d /opt/plesk ]; then - echo -e "\nPlesk detected...exit...\n" - exit 1 -fi -} - -check_process() { -if systemctl is-active --quiet httpd; then - systemctl disable httpd - systemctl stop httpd - echo -e "\nhttpd process detected, disabling...\n" -fi -if systemctl is-active --quiet apache2; then - systemctl disable apache2 - systemctl stop apache2 - echo -e "\napache2 process detected, disabling...\n" -fi -if systemctl is-active --quiet named; then - systemctl stop named - systemctl disable named - echo -e "\nnamed process detected, disabling...\n" -fi -if systemctl is-active --quiet exim; then - systemctl stop exim - systemctl disable exim - echo -e "\nexim process detected, disabling...\n" -fi -} - -show_help() { -echo -e "\nCyberPanel Installer Script Help\n" -echo -e "\nUsage: wget https://cyberpanel.sh/cyberpanel.sh" -echo -e "\nchmod +x cyberpanel.sh" -echo -e "\n./cyberpanel.sh -v ols/SERIAL_NUMBER -c 1 -a 1" -echo -e "\n -v or --version: choose to install CyberPanel OpenLiteSpeed or CyberPanel Enterprise, available options are \e[31mols\e[39m and \e[31mSERIAL_NUMBER\e[39m, default ols" -echo -e "\n Please be aware, this serial number must be obtained from LiteSpeed Store." -echo -e "\n And if this serial number has been used before, it must be released/migrated in Store first, otherwise it will fail to start." -echo -e "\n -a or --addons: install addons: memcached, redis, PHP extension for memcached and redis, 1 for install addons, 0 for not to install, default 0, only applicable for CentOS system." -echo -e "\n -p or --password: set password of new installation, empty for default 1234567, [r] or [random] for randomly generated 16 digital password, any other value besdies [d] and [r(andom)] will be accept as password, default use 1234567." -#echo -e "\n -m: set to minimal mode which will not install PowerDNS, Pure-FTPd and Postfix" -echo -e "\n Example:" -echo -e "\n ./cyberpanel.sh -v ols -p r or ./cyberpanel.sh --version ols --password random" -echo -e "\n This will install CyberPanel OpenLiteSpeed and randomly generate the password." -echo -e "\n ./cyberpanel.sh default" -echo -e "\n This will install everything default , which is OpenLiteSpeed and nothing more.\n" - -} - -license_input() { -VERSION="ENT" -echo -e "\nPlease note that your server has \e[31m$TOTAL_RAM\e[39m RAM" -echo -e "If you are using \e[31mFree Start\e[39m license, It will not start due to \e[31m2GB RAM limit\e[39m.\n" -echo -e "If you do not have any license, you can also use trial license (if server has not used trial license before), type \e[31mTRIAL\e[39m\n" - -printf "%s" "Please input your serial number for LiteSpeed WebServer Enterprise:" -read LICENSE_KEY -if [ -z "$LICENSE_KEY" ] ; then - echo -e "\nPlease provide license key\n" - exit -fi - -echo -e "The serial number you input is: \e[31m$LICENSE_KEY\e[39m" -printf "%s" "Please verify it is correct. [y/N]" -read TMP_YN -if [ -z "$TMP_YN" ] ; then - echo -e "\nPlease type \e[31my\e[39m\n" - exit -fi - -KEY_SIZE=${#LICENSE_KEY} -TMP=$(echo $LICENSE_KEY | cut -c5) -TMP2=$(echo $LICENSE_KEY | cut -c10) -TMP3=$(echo $LICENSE_KEY | cut -c15) - -if [[ $TMP == "-" ]] && [[ $TMP2 == "-" ]] && [[ $TMP3 == "-" ]] && [[ $KEY_SIZE == "19" ]] ; then - echo -e "\nLicense key set..." -elif [[ $LICENSE_KEY == "trial" ]] || [[ $LICENSE_KEY == "TRIAL" ]] || [[ $LICENSE_KEY == "Trial" ]] ; then - echo -e "\nTrial license set..." - LICENSE_KEY="TRIAL" -else - echo -e "\nLicense key seems incorrect, please verify\n" - echo -e "\nIf you are copying/pasting, please make sure you didn't paste blank space...\n" - exit -fi -} - -interactive_mode() { -echo -e " CyberPanel Installer v$CP_VER1$CP_VER2 - - 1. Install CyberPanel. - - 2. Addons and Miscellaneous - - 3. Exit. - - " -read -p " Please enter the number[1-3]: " num -echo "" -case "$num" in - 1) - interactive_install - ;; - 2) - interactive_others - ;; - 3) - exit - ;; - *) - echo -e " Please enter the right number [1-3]\n" - exit - ;; -esac -} - -interactive_others() { -if [ ! -e "/etc/cyberpanel/machineIP" ]; then -echo -e "\nYou don't have CyberPanel installed...\n" -exit -fi - -echo -e " CyberPanel Addons v$CP_VER1$CP_VER2 - - 1. Install Memcached extension and backend - - 2. Install Redis extension and backend - - 3. Return to main page. - - 4. Exit - " - -echo && read -p "Please enter the number[1-4]: " num -case "$num" in - 1) - memcached_installation - exit - ;; - 2) - redis_installation - exit - ;; - 3) - interactive_mode - ;; - 4) - exit - ;; - *) - echo -e "${Error} please enter the right number [1-4]" - ;; -esac -} - -interactive_install() { -RAM=$(free -m | awk 'NR==2{printf "%s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }') -DISK=$(df -h | awk '$NF=="/"{printf "%d/%dGB (%s)\n", $3,$2,$5}') -#clear -echo -e " CyberPanel Installer v$CP_VER1$CP_VER2 - - RAM check : $RAM - - Disk check : $DISK (Minimal \e[31m10GB\e[39m free space) - - 1. Install CyberPanel with \e[31mOpenLiteSpeed\e[39m. - - 2. Install Cyberpanel with \e[31mLiteSpeed Enterprise\e[39m. - - 3. Exit. - - " -read -p " Please enter the number[1-3]: " num -echo "" -case "$num" in - 1) - VERSION="OLS" - ;; - 2) - license_input - ;; - 3) - exit - ;; - *) - echo -e " Please enter the right number [1-3]\n" - exit - ;; -esac - -< /dev/null; then - echo -e "\nCyberPanel installation sucessfully completed..." -else - echo -e "Oops, something went wrong..." - exit -fi - -if [[ $MEMCACHED == "ON" ]] ; then - memcached_installation -fi -if [[ $REDIS == "ON" ]] ; then - redis_installation -fi - after_install -fi -} - -pip_virtualenv() { -if [[ $DEV == "OFF" ]] ; then -if [[ $SERVER_COUNTRY == "CN" ]] ; then - mkdir /root/.pip -cat << EOF > /root/.pip/pip.conf -[global] -index-url = https://mirrors.aliyun.com/pypi/simple/ -EOF -fi - -if [[ $PROVIDER == "Alibaba Cloud" ]] ; then - pip install --upgrade pip - pip install setuptools==40.8.0 -fi - -pip install virtualenv -virtualenv --system-site-packages /usr/local/CyberPanel -source /usr/local/CyberPanel/bin/activate -rm -rf requirements.txt -wget -O requirements.txt https://raw.githubusercontent.com/usmannasir/cyberpanel/1.8.0/requirments.txt -pip install --ignore-installed -r requirements.txt -virtualenv --system-site-packages /usr/local/CyberPanel -fi - -if [[ $DEV == "ON" ]] ; then - #install dev branch - #wget https://raw.githubusercontent.com/usmannasir/cyberpanel/$BRANCH_NAME/requirments.txt - cd /usr/local/ - python3.6 -m venv CyberPanel - source /usr/local/CyberPanel/bin/activate - wget -O requirements.txt https://raw.githubusercontent.com/usmannasir/cyberpanel/$BRANCH_NAME/requirments.txt - pip3.6 install --ignore-installed -r requirements.txt -fi - -if [ -f requirements.txt ] && [ -d cyberpanel ] ; then - rm -rf cyberpanel - rm -f requirements.txt -fi - -if [[ $SERVER_COUNTRY == "CN" ]] ; then - wget https://cyberpanel.sh/cyberpanel-git.tar.gz - tar xzvf cyberpanel-git.tar.gz > /dev/null - cp -r cyberpanel /usr/local/cyberpanel - cd cyberpanel/install -else - if [[ $DEV == "ON" ]] ; then - git clone https://github.com/usmannasir/cyberpanel - cd cyberpanel - git checkout $BRANCH_NAME - cd - - cd cyberpanel/install - else - git clone https://github.com/usmannasir/cyberpanel - cd cyberpanel/install - fi -fi -curl https://cyberpanel.sh/?version -} - -after_install() { -if [ ! -d "/var/lib/php" ]; then - mkdir /var/lib/php -fi - -if [ ! -d "/var/lib/php/session" ]; then - mkdir /var/lib/php/session -fi - -chmod 1733 /var/lib/php/session - -if grep "\[ERROR\] We are not able to run ./install.sh return code: 1. Fatal error, see /var/log/installLogs.txt for full details" /var/log/installLogs.txt > /dev/null; then - cd ${DIR}/cyberpanel/install/lsws-* - ./install.sh - echo -e "\n\n\nIt seems LiteSpeed Enterprise has failed to install, please check your license key is valid" - echo -e "\nIf this license key has been used before, you may need to go to store to release it first." - exit -fi - - -if grep "CyberPanel installation successfully completed" /var/log/installLogs.txt > /dev/null; then - -if [[ $DEV == "ON" ]] ; then -python3.6 -m venv /usr/local/CyberCP -source /usr/local/CyberCP/bin/activate -wget -O requirements.txt https://raw.githubusercontent.com/usmannasir/cyberpanel/$BRANCH_NAME/requirments.txt -pip3.6 install --ignore-installed -r requirements.txt -systemctl restart lscpd -fi - -for version in $(ls /usr/local/lsws | grep lsphp); - do - php_ini=$(find /usr/local/lsws/$version/ -name php.ini) - version2=${version:5:2} - version2=$(awk "BEGIN { print "${version2}/10" }") - if [[ $version2 = "7" ]] ; then - version2="7.0" - fi - if [[ $SERVER_OS == "CentOS" ]] ; then - yum remove -y $version-mysql - yum install -y $version-mysqlnd - yum install -y $version-devel make gcc glibc-devel libmemcached-devel zlib-devel - if [[ ! -d /usr/local/lsws/$version/tmp ]] ; then - mkdir /usr/local/lsws/$version/tmp - fi - /usr/local/lsws/${version}/bin/pecl channel-update pecl.php.net; - /usr/local/lsws/${version}/bin/pear config-set temp_dir /usr/local/lsws/${version}/tmp - /usr/local/lsws/${version}/bin/pecl install timezonedb - echo "extension=timezonedb.so" > /usr/local/lsws/${version}/etc/php.d/20-timezone.ini - sed -i 's|expose_php = On|expose_php = Off|g' $php_ini - sed -i 's|mail.add_x_header = On|mail.add_x_header = Off|g' $php_ini - sed -i 's|;session.save_path = "/tmp"|session.save_path = "/var/lib/php/session"|g' $php_ini - fi - - if [[ $SERVER_OS == "Ubuntu" ]] ; then - if [[ ! -d /usr/local/lsws/cyberpanel-tmp ]] ; then - echo "yes" > /etc/pure-ftpd/conf/ChrootEveryone - systemctl restart pure-ftpd-mysql - DEBIAN_FRONTEND=noninteractive apt install libmagickwand-dev pkg-config build-essential -y - mkdir /usr/local/lsws/cyberpanel-tmp - cd /usr/local/lsws/cyberpanel-tmp - wget https://pecl.php.net/get/timezonedb-2019.3.tgz - tar xzvf timezonedb-2019.3.tgz - cd timezonedb-2019.3 - fi - /usr/local/lsws/${version}/bin/phpize - ./configure --with-php-config=/usr/local/lsws/${version}/bin/php-config${version2} - make - make install - echo "extension=timezonedb.so" > /usr/local/lsws/${version}/etc/php/${version2}/mods-available/20-timezone.ini - make clean - fi -done - -rm -rf /etc/profile.d/cyberpanel* -curl --silent -o /etc/profile.d/cyberpanel.sh https://cyberpanel.sh/?banner 2>/dev/null -chmod +x /etc/profile.d/cyberpanel.sh -RAM2=$(free -m | awk 'NR==2{printf "%s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }') -DISK2=$(df -h | awk '$NF=="/"{printf "%d/%dGB (%s)\n", $3,$2,$5}') -ELAPSED="$(($SECONDS / 3600)) hrs $((($SECONDS / 60) % 60)) min $(($SECONDS % 60)) sec" -MYSQLPASSWD=$(cat /etc/cyberpanel/mysqlPassword) -echo "$ADMIN_PASS" > /etc/cyberpanel/adminPass -/usr/local/CyberPanel/bin/python2 /usr/local/CyberCP/plogical/adminPass.py --password $ADMIN_PASS -systemctl restart lscpd -systemctl restart lsws -echo "/usr/local/CyberPanel/bin/python2 /usr/local/CyberCP/plogical/adminPass.py --password \"\$@\"" > /usr/bin/adminPass -echo "systemctl restart lscpd" >> /usr/bin/adminPass -chmod +x /usr/bin/adminPass -if [[ $VERSION = "OLS" ]] ; then - WORD="OpenLiteSpeed" -# sed -i 's|maxConnections 10000|maxConnections 100000|g' /usr/local/lsws/conf/httpd_config.conf -# OLS_LATEST=$(curl https://openlitespeed.org/packages/release) -# wget https://openlitespeed.org/packages/openlitespeed-$OLS_LATEST.tgz -# tar xzvf openlitespeed-$OLS_LATEST.tgz -# cd openlitespeed -# ./install.sh - /usr/local/lsws/bin/lswsctrl stop - /usr/local/lsws/bin/lswsctrl start -# rm -f openlitespeed-$OLS_LATEST.tgz -# rm -rf openlitespeed -# cd .. -fi -if [[ $VERSION = "ENT" ]] ; then - WORD="LiteSpeed Enterprise" - if [[ $SERVER_COUNTRY != "CN" ]] ; then - /usr/local/lsws/admin/misc/lsup.sh -f -v $LSWS_STABLE_VER - fi -fi - -systemctl status lsws 2>&1>/dev/null -if [[ $? == "0" ]] ; then - echo "LSWS service is running..." -else - systemctl stop lsws - systemctl start lsws -fi - -clear -echo "###################################################################" -echo " CyberPanel Successfully Installed " -echo " " -echo " Current Disk usage : $DISK2 " -echo " " -echo " Current RAM usage : $RAM2 " -echo " " -echo " Installation time : $ELAPSED " -echo " " -echo " Visit: https://$SERVER_IP:8090 " -echo " Panel username: admin " -echo " Panel password: $ADMIN_PASS " -#echo " Mysql username: root " -#echo " Mysql password: $MYSQLPASSWD " -echo " " -echo " Please change your default admin password " -echo " If you need to reset your panel password, please run: " -echo " adminPass YOUR_NEW_PASSWORD " -echo " " -echo " If you change mysql password, please modify file in " -echo -e " \e[31m/etc/cyberpanel/mysqlPassword\e[39m with new password as well " -echo " " -echo " Website : https://www.cyberpanel.net " -echo " Forums : https://forums.cyberpanel.net " -echo " Wikipage: https://cyberpanel.net/KnowledgeBase/ " -echo " " -echo -e " Enjoy your accelerated Internet by " -echo -e " CyberPanel & $WORD " -echo "###################################################################" -if [[ $PROVIDER != "undefined" ]] ; then - echo -e "\033[0;32m$PROVIDER\033[39m detected..." - echo -e "This provider has a \e[31mnetwork-level firewall\033[39m" -else - echo -e "If your provider has a \e[31mnetwork-level firewall\033[39m" -fi - echo -e "Please make sure you have opened following port for both in/out:" - echo -e "\033[0;32mTCP: 8090\033[39m for CyberPanel" - echo -e "\033[0;32mTCP: 80\033[39m, \033[0;32mTCP: 443\033[39m and \033[0;32mUDP: 443\033[39m for webserver" - echo -e "\033[0;32mTCP: 21\033[39m and \033[0;32mTCP: 40110-40210\033[39m for FTP" - echo -e "\033[0;32mTCP: 25\033[39m, \033[0;32mTCP: 587\033[39m, \033[0;32mTCP: 465\033[39m, \033[0;32mTCP: 110\033[39m, \033[0;32mTCP: 143\033[39m and \033[0;32mTCP: 993\033[39m for mail service" - echo -e "\033[0;32mTCP: 53\033[39m and \033[0;32mUDP: 53\033[39m for DNS service" -if [[ $SERVER_COUNTRY = CN ]] ; then - if [[ $PROVIDER == "Tencent Cloud" ]] ; then - if [[ $SERVER_OS == "Ubuntu" ]] ; then - rm -f /etc/apt/sources.list - mv /etc/apt/sources.list-backup /etc/apt/sources.list -echo > "nameserver 127.0.0.53 -options edns0" /run/systemd/resolve/stub-resolv.conf -echo > "nameserver 127.0.0.53 -options edns0" /etc/resolv.conf - apt update -#revert the previous change on tencent cloud repo. - fi - fi - if [[ $VERSION = "ENT" ]] ; then - sed -i 's|https://www.litespeedtech.com/packages/5.0/lsws-5.3.5-ent-x86_64-linux.tar.gz|https://cyberpanel.sh/packages/5.0/lsws-5.3.5-ent-x86_64-linux.tar.gz|g' /usr/local/CyberCP/install/installCyberPanel.py - sed -i 's|https://www.litespeedtech.com/packages/5.0/lsws-5.3.8-ent-x86_64-linux.tar.gz|https://cyberpanel.sh/packages/5.0/lsws-5.3.8-ent-x86_64-linux.tar.gz|g' /usr/local/CyberCP/serverStatus/serverStatusUtil.py - sed -i 's|https://www.litespeedtech.com/packages/5.0/lsws-5.3.8-ent-x86_64-linux.tar.gz|https://'$DOWNLOAD_SERVER'/litespeed/lsws-'$LSWS_STABLE_VER'-ent-x86_64-linux.tar.gz|g' /usr/local/CyberCP/serverStatus/serverStatusUtil.py - echo -e "If you have install LiteSpeed Enterprise, please run \e[31m/usr/local/lsws/admin/misc/lsup.sh\033[39m to update it to latest." - fi -fi - -sed -i 's|lsws-5.3.8|lsws-'$LSWS_STABLE_VER'|g' /usr/local/CyberCP/serverStatus/serverStatusUtil.py -sed -i 's|lsws-5.4.2|lsws-'$LSWS_STABLE_VER'|g' /usr/local/CyberCP/serverStatus/serverStatusUtil.py -sed -i 's|lsws-5.3.5|lsws-'$LSWS_STABLE_VER'|g' /usr/local/CyberCP/serverStatus/serverStatusUtil.py - -if [[ $SILENT != "ON" ]] ; then -printf "%s" "Would you like to restart your server now? [y/N]: " -read TMP_YN - -if [[ "$TMP_YN" = "N" ]] || [[ "$TMP_YN" = "n" ]] || [[ -z "$TMP_YN" ]]; then -: -else -reboot -exit -fi - -exit -fi -#replace URL for CN - - - -else -echo "something went wrong..." -exit -fi -} - -argument_mode() { -KEY_SIZE=${#VERSION} -TMP=$(echo $VERSION | cut -c5) -TMP2=$(echo $VERSION | cut -c10) -TMP3=$(echo $VERSION | cut -c15) -if [[ $VERSION == "OLS" || $VERSION == "ols" ]] ; then - VERSION="OLS" - echo -e "\nSet to OpenLiteSpeed..." -elif [[ $VERSION == "Trial" ]] || [[ $VERSION == "TRIAL" ]] || [[ $VERSION == "trial" ]] ; then - VERSION="ENT" - LICENSE_KEY="TRIAL" - echo -e "\nLiteSpeed Enterprise trial license set..." -elif [[ $TMP == "-" ]] && [[ $TMP2 == "-" ]] && [[ $TMP3 == "-" ]] && [[ $KEY_SIZE == "19" ]] ; then - LICENSE_KEY=$VERSION - VERSION="ENT" - echo -e "\nLiteSpeed Enterprise license key set..." -else - echo -e "\nCan not recognize the input value \e[31m$VERSION\e[39m " - echo -e "\nPlease verify the input value..." - echo -e "\nPlease run with \e[31m-h\e[39m or \e[31m--help\e[39m for more detail." - exit -fi - -if [[ $ADMIN_PASS == "d" ]] ; then - ADMIN_PASS="1234567" - echo -e "\nSet to default password..." - echo -e "\nAdmin password will be set to \e[31m$ADMIN_PASS\e[39m" -elif [[ $ADMIN_PASS == "r" ]] ; then - ADMIN_PASS=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16 ; echo '') - echo -e "\nSet to random-generated password..." - echo -e "\nAdmin password will be set to \e[31m$ADMIN_PASS\e[39m" - echo $ADMIN_PASS -else - echo -e "\nAdmin password will be set to \e[31m$ADMIN_PASS\e[39m" -fi -} - -if [ $# -eq 0 ] ; then - echo -e "\nInitializing...\n" -else - if [[ $1 == "help" ]] ; then - show_help - exit - elif [[ $1 == "dev" ]] ; then - DEV="ON" - DEV_ARG="ON" - SILENT="OFF" - elif [[ $1 == "default" ]] ; then - echo -e "\nThis will start default installation...\n" - SILENT="ON" - POSTFIX_VARIABLE="ON" - POWERDNS_VARIABLE="ON" - PUREFTPD_VARIABLE="ON" - VERSION="OLS" - ADMIN_PASS="1234567" - MEMCACHED="ON" - REDIS="ON" - else - while [ ! -z "${1}" ]; do - case $1 in - -v | --version) shift - if [ "${1}" = '' ]; then - show_help - exit - else - VERSION="${1}" - SILENT="ON" - fi - ;; - -p | --password) shift - if [[ "${1}" == '' ]]; then - ADMIN_PASS="1234567" - elif [[ "${1}" == 'r' ]] || [[ $1 == 'random' ]] ; then - ADMIN_PASS=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16 ; echo '') - else - if [ ${1} -lt 8 ] ; then - echo -e "\nPassword lenth less than 8 digital, please choose a more complicated password.\n" - exit - fi - ADMIN_PASS="${1}" - fi - ;; - -a | --addons) - MEMCACHED="ON" - REDIS="ON" - ;; - -m | --minimal) - echo "minimal installation is still work in progress..." - exit - ;; - -h | --help) - show_help - exit - ;; - *) - echo "unknown argument..." - show_help - exit - ;; - esac - shift - done - fi -fi - - - -SERVER_IP=$(curl --silent --max-time 10 -4 https://cyberpanel.sh/?ip) -if [[ $SERVER_IP =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo -e "Valid IP detected..." -else - echo -e "Can not detect IP, exit..." - exit -fi -SERVER_COUNTRY="unknow" -SERVER_COUNTRY=$(curl --silent --max-time 5 https://cyberpanel.sh/?country) -if [[ ${#SERVER_COUNTRY} == "2" ]] || [[ ${#SERVER_COUNTRY} == "6" ]] ; then - echo -e "\nChecking server..." - else - echo -e "\nChecking server..." - SERVER_COUNTRY="unknow" -fi -#SERVER_COUNTRY="CN" -#test string -if [[ $SERVER_COUNTRY == "CN" ]] ; then -DOWNLOAD_SERVER="cyberpanel.sh" -else -DOWNLOAD_SERVER="cdn.cyberpanel.sh" -fi - -check_OS -check_root -check_panel -check_process -check_provider - - - - - -if [[ $SILENT = "ON" ]] ; then -argument_mode -else -interactive_mode -fi - -SECONDS=0 -install_required - -pip_virtualenv - -system_tweak - -main_install \ No newline at end of file diff --git a/mailServer/mailserverManager.py.bak b/mailServer/mailserverManager.py.bak deleted file mode 100644 index 06c4bddfe..000000000 --- a/mailServer/mailserverManager.py.bak +++ /dev/null @@ -1,1713 +0,0 @@ -#!/usr/local/CyberCP/bin/python -# coding=utf-8 -import os.path -import sys -import django -from plogical.httpProc import httpProc -sys.path.append('/usr/local/CyberCP') -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") -django.setup() -from django.http import HttpResponse -try: - from .models import Domains,EUsers - from loginSystem.views import loadLoginPage -except: - pass -import plogical.CyberCPLogFileWriter as logging -import json -import shlex -import subprocess -try: - from plogical.virtualHostUtilities import virtualHostUtilities - from plogical.mailUtilities import mailUtilities -except: - pass -import _thread -try: - from dns.models import Domains as dnsDomains - from dns.models import Records as dnsRecords - from mailServer.models import Forwardings, Pipeprograms - from plogical.acl import ACLManager - from plogical.dnsUtilities import DNS - from loginSystem.models import Administrator - from websiteFunctions.models import Websites -except: - pass -import os -from plogical.processUtilities import ProcessUtilities -import bcrypt -import threading as multi -import argparse - -class MailServerManager(multi.Thread): - - def __init__(self, request = None, function = None, extraArgs = None): - multi.Thread.__init__(self) - self.request = request - self.function = function - self.extraArgs = extraArgs - - def run(self): - try: - if self.function == 'RunServerLevelEmailChecks': - self.RunServerLevelEmailChecks() - except BaseException as msg: - logging.CyberCPLogFileWriter.writeToFile(str(msg) + ' [MailServerManager.run]') - - def loadEmailHome(self): - proc = httpProc(self.request, 'mailServer/index.html', - None, 'createEmail') - return proc.render() - - def createEmailAccount(self): - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if not os.path.exists('/home/cyberpanel/postfix'): - proc = httpProc(self.request, 'mailServer/createEmailAccount.html', - {"status": 0}, 'createEmail') - return proc.render() - - websitesName = ACLManager.findAllSites(currentACL, userID) - websitesName = websitesName + ACLManager.findChildDomains(websitesName) - - proc = httpProc(self.request, 'mailServer/createEmailAccount.html', - {'websiteList': websitesName, "status": 1}, 'createEmail') - return proc.render() - - def listEmails(self): - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if not os.path.exists('/home/cyberpanel/postfix'): - proc = httpProc(self.request, 'mailServer/listEmails.html', - {"status": 0}, 'listEmails') - return proc.render() - - websitesName = ACLManager.findAllSites(currentACL, userID) - websitesName = websitesName + ACLManager.findChildDomains(websitesName) - - proc = httpProc(self.request, 'mailServer/listEmails.html', - {'websiteList': websitesName, "status": 1}, 'listEmails') - return proc.render() - - def submitEmailCreation(self): - try: - - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if ACLManager.currentContextPermission(currentACL, 'createEmail') == 0: - return ACLManager.loadErrorJson('createEmailStatus', 0) - - data = json.loads(self.request.body) - domainName = data['domain'] - userName = data['username'].lower() - password = data['passwordByPass'] - - - admin = Administrator.objects.get(pk=userID) - if ACLManager.checkOwnership(domainName, admin, currentACL) == 1: - pass - else: - return ACLManager.loadErrorJson() - - - ## Create email entry - - result = mailUtilities.createEmailAccount(domainName, userName.lower(), password) - - if result[0] == 1: - data_ret = {'status': 1, 'createEmailStatus': 1, 'error_message': "None"} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - else: - data_ret = {'status': 0, 'createEmailStatus': 0, 'error_message': result[1]} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - except BaseException as msg: - data_ret = {'status': 0, 'createEmailStatus': 0, 'error_message': str(msg)} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - def deleteEmailAccount(self): - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if not os.path.exists('/home/cyberpanel/postfix'): - proc = httpProc(self.request, 'mailServer/deleteEmailAccount.html', - {"status": 0}, 'deleteEmail') - return proc.render() - - websitesName = ACLManager.findAllSites(currentACL, userID) - websitesName = websitesName + ACLManager.findChildDomains(websitesName) - - proc = httpProc(self.request, 'mailServer/deleteEmailAccount.html', - {'websiteList': websitesName, "status": 1}, 'deleteEmail') - return proc.render() - - def getEmailsForDomain(self): - try: - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if ACLManager.currentContextPermission(currentACL, 'deleteEmail') == 0: - return ACLManager.loadErrorJson('fetchStatus', 0) - - data = json.loads(self.request.body) - domain = data['domain'] - - admin = Administrator.objects.get(pk=userID) - if ACLManager.checkOwnership(domain, admin, currentACL) == 1: - pass - else: - return ACLManager.loadErrorJson() - - try: - domain = Domains.objects.get(domain=domain) - except: - final_dic = {'status': 0, 'fetchStatus': 0, 'error_message': "No email accounts exists!"} - final_json = json.dumps(final_dic) - return HttpResponse(final_json) - - emails = domain.eusers_set.all() - - if emails.count() == 0: - final_dic = {'status': 0, 'fetchStatus': 0, 'error_message': "No email accounts exists!"} - final_json = json.dumps(final_dic) - return HttpResponse(final_json) - - json_data = "[" - checker = 0 - count = 1 - for items in emails: - dic = {'id': count, 'email': items.email} - count = count + 1 - - if checker == 0: - json_data = json_data + json.dumps(dic) - checker = 1 - else: - json_data = json_data + ',' + json.dumps(dic) - - json_data = json_data + ']' - final_dic = {'status': 1, 'fetchStatus': 1, 'error_message': "None", "data": json_data} - final_json = json.dumps(final_dic) - - return HttpResponse(final_json) - - except BaseException as msg: - data_ret = {'status': 0, 'fetchStatus': 0, 'error_message': str(msg)} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - def submitEmailDeletion(self): - try: - - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if ACLManager.currentContextPermission(currentACL, 'deleteEmail') == 0: - return ACLManager.loadErrorJson('deleteEmailStatus', 0) - - data = json.loads(self.request.body) - email = data['email'] - - eUser = EUsers.objects.get(email=email) - - emailOwnerDomain = eUser.emailOwner - - admin = Administrator.objects.get(pk=userID) - if ACLManager.checkOwnership(eUser.emailOwner.domainOwner.domain, admin, currentACL) == 1: - pass - else: - return ACLManager.loadErrorJson() - - mailUtilities.deleteEmailAccount(email) - - if emailOwnerDomain.eusers_set.all().count() == 0: - emailOwnerDomain.delete() - - data_ret = {'status': 1, 'deleteEmailStatus': 1, 'error_message': "None"} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - except BaseException as msg: - data_ret = {'status': 0, 'deleteEmailStatus': 0, 'error_message': str(msg)} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - def fixMailSSL(self, data = None): - try: - - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if data == None: - data = json.loads(self.request.body) - selectedDomain = data['selectedDomain'] - else: - selectedDomain = data['websiteName'] - - - admin = Administrator.objects.get(pk=userID) - - if ACLManager.checkOwnership(selectedDomain, admin, currentACL) == 1: - pass - else: - return ACLManager.loadErrorJson('status', 0) - - website = Websites.objects.get(domain=selectedDomain) - - execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/virtualHostUtilities.py" - execPath = '%s setupAutoDiscover --virtualHostName %s --websiteOwner %s' % (execPath, selectedDomain, website.admin.userName) - - ProcessUtilities.executioner(execPath) - - data_ret = {'status': 1, 'error_message': "None"} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - except BaseException as msg: - data_ret = {'status': 0, 'error_message': str(msg)} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - def emailForwarding(self): - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if not os.path.exists('/home/cyberpanel/postfix'): - proc = httpProc(self.request, 'mailServer/emailForwarding.html', - {"status": 0}, 'emailForwarding') - return proc.render() - - websitesName = ACLManager.findAllSites(currentACL, userID) - websitesName = websitesName + ACLManager.findChildDomains(websitesName) - - proc = httpProc(self.request, 'mailServer/emailForwarding.html', - {'websiteList': websitesName, "status": 1}, 'emailForwarding') - return proc.render() - - def fetchCurrentForwardings(self): - try: - - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0: - return ACLManager.loadErrorJson('fetchStatus', 0) - - data = json.loads(self.request.body) - emailAddress = data['emailAddress'] - forwardingOption = data['forwardingOption'] - - if forwardingOption != "Pipe to program": - eUser = EUsers.objects.get(email=emailAddress) - - admin = Administrator.objects.get(pk=userID) - if ACLManager.checkOwnership(eUser.emailOwner.domainOwner.domain, admin, currentACL) == 1: - pass - else: - return ACLManager.loadErrorJson() - - currentForwardings = Forwardings.objects.filter(source=emailAddress) - - json_data = "[" - checker = 0 - id = 1 - for items in currentForwardings: - if items.source == items.destination: - continue - dic = {'id': id, - 'source': items.source, - 'destination': items.destination} - - id = id + 1 - - if checker == 0: - json_data = json_data + json.dumps(dic) - checker = 1 - else: - json_data = json_data + ',' + json.dumps(dic) - - json_data = json_data + ']' - final_dic = {'status': 1, 'fetchStatus': 1, 'error_message': "None", "data": json_data} - final_json = json.dumps(final_dic) - - return HttpResponse(final_json) - else: - - currentForwardings = Pipeprograms.objects.filter(source=emailAddress) - - json_data = "[" - checker = 0 - id = 1 - for items in currentForwardings: - if items.source == items.destination: - continue - dic = {'id': id, - 'source': items.source, - 'destination': items.destination} - - id = id + 1 - - if checker == 0: - json_data = json_data + json.dumps(dic) - checker = 1 - else: - json_data = json_data + ',' + json.dumps(dic) - - json_data = json_data + ']' - final_dic = {'status': 1, 'fetchStatus': 1, 'error_message': "None", "data": json_data} - final_json = json.dumps(final_dic) - - return HttpResponse(final_json) - - - except BaseException as msg: - data_ret = {'status': 0, 'fetchStatus': 0, 'error_message': str(msg)} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - def submitForwardDeletion(self): - try: - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0: - return ACLManager.loadErrorJson('deleteForwardingStatus', 0) - - data = json.loads(self.request.body) - destination = data['destination'] - source = data['source'] - forwardingOption = data['forwardingOption'] - - eUser = EUsers.objects.get(email=source) - - admin = Administrator.objects.get(pk=userID) - if ACLManager.checkOwnership(eUser.emailOwner.domainOwner.domain, admin, currentACL) == 1: - pass - else: - return ACLManager.loadErrorJson() - - if forwardingOption == 'Forward to email': - for items in Forwardings.objects.filter(destination=destination, source=source): - items.delete() - else: - for items in Pipeprograms.objects.filter(destination=destination, source=source): - items.delete() - - ## Delete Email PIPE - sourceusername = source.split("@")[0] - virtualfilter = '%s FILTER %spipe:dummy' % (source, sourceusername) - command = "sed -i 's/^" + source + ".*//g' /etc/postfix/script_filter" - ProcessUtilities.executioner(command) - command = "sed -i 's/^" + sourceusername + "pipe.*//g' /etc/postfix/master.cf" - ProcessUtilities.executioner(command) - - #### Hashing filter Reloading Postfix - command = "postmap /etc/postfix/script_filter" - ProcessUtilities.executioner(command) - command = "postfix reload" - ProcessUtilities.executioner(command) - ## - - - data_ret = {'status': 1, 'deleteForwardingStatus': 1, 'error_message': "None", - 'successMessage': 'Successfully deleted!'} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - except BaseException as msg: - data_ret = {'status': 0, 'deleteForwardingStatus': 0, 'error_message': str(msg)} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - def submitEmailForwardingCreation(self): - try: - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0: - return ACLManager.loadErrorJson('createStatus', 0) - - data = json.loads(self.request.body) - source = data['source'] - destination = data['destination'] - forwardingOption = data['forwardingOption'] - - eUser = EUsers.objects.get(email=source) - - admin = Administrator.objects.get(pk=userID) - if ACLManager.checkOwnership(eUser.emailOwner.domainOwner.domain, admin, currentACL) == 1: - pass - else: - return ACLManager.loadErrorJson() - - if Forwardings.objects.filter(source=source, destination=destination).count() > 0: - data_ret = {'status': 0, 'createStatus': 0, - 'error_message': "You have already forwarded to this destination."} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - if forwardingOption == 'Forward to email': - if Forwardings.objects.filter(source=source).count() == 0: - forwarding = Forwardings(source=source, destination=source) - forwarding.save() - - forwarding = Forwardings(source=source, destination=destination) - forwarding.save() - else: - forwarding = Pipeprograms(source=source, destination=destination) - forwarding.save() - - ## Create Email PIPE filter - ## example@domain.com FILTER support:dummy - sourceusername = source.split("@")[0] - virtualfilter = '%s FILTER %spipe:dummy' % (source, sourceusername) - command = "echo '" + virtualfilter + "' >> /etc/postfix/script_filter" - ProcessUtilities.executioner(command) - - ## support unix - n n - - pipe flags=Rq user=domain argv=/usr/bin/php -q /home/domain.com/public_html/ticket/api/pipe.php - ## Find Unix file owner of provided pipe - domainName = source.split("@")[1] - website = Websites.objects.get(domain=domainName) - externalApp = website.externalApp - pipeowner = externalApp - ## Add Filter pipe to postfix /etc/postfix/master.cf - filterpipe = '%spipe unix - n n - - pipe flags=Rq user=%s argv=%s -f $(sender) -- $(recipient)' % (sourceusername, pipeowner, destination) - command = "echo '" + filterpipe + "' >> /etc/postfix/master.cf" - ProcessUtilities.executioner(command) - ## Add Check Recipient Hash to postfix /etc/postfix/main.cf - command = "sed -i 's|^smtpd_recipient_restrictions =.*|smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, check_recipient_access hash:/etc/postfix/script_filter, permit|' /etc/postfix/main.cf" - ProcessUtilities.executioner(command) - - #### Hashing filter Reloading Postfix - command = "postmap /etc/postfix/script_filter" - ProcessUtilities.executioner(command) - command = "postfix reload" - ProcessUtilities.executioner(command) - ## - - - - data_ret = {'status': 1, 'createStatus': 1, 'error_message': "None", 'successMessage': 'Successfully Created!'} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - except BaseException as msg: - data_ret = {'status': 0, 'createStatus': 0, 'error_message': str(msg)} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - def fetchEmails(self): - try: - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if ACLManager.currentContextPermission(currentACL, 'listEmails') == 0: - return ACLManager.loadErrorJson('status', 0) - - data = json.loads(self.request.body) - selectedDomain = data['selectedDomain'] - - admin = Administrator.objects.get(pk=userID) - if ACLManager.checkOwnership(selectedDomain, admin, currentACL) == 1: - pass - else: - return ACLManager.loadErrorJson() - try: - - emailDomain = Domains.objects.get(domain=selectedDomain) - except: - raise BaseException('No emails exist for this domain.') - - postfixMapPath = '/etc/postfix/vmail_ssl.map' - - if os.path.exists(postfixMapPath): - - postfixMapData = open(postfixMapPath, 'r').read() - - if postfixMapData.find(selectedDomain) == -1: - mailConfigured = 0 - else: - mailConfigured = 1 - else: - mailConfigured = 0 - - records = emailDomain.eusers_set.all() - - json_data = "[" - checker = 0 - - for items in records: - dic = {'email': items.email, - } - - if checker == 0: - json_data = json_data + json.dumps(dic) - checker = 1 - else: - json_data = json_data + ',' + json.dumps(dic) - - json_data = json_data + ']' - final_json = json.dumps({'status': 1, 'fetchStatus': 1,'serverHostname': 'mail.%s' % (selectedDomain), 'mailConfigured': mailConfigured, 'error_message': "None", "data": json_data}) - return HttpResponse(final_json) - - except BaseException as msg: - final_dic = {'status': 0, 'fetchStatus': 0, 'error_message': str(msg)} - final_json = json.dumps(final_dic) - return HttpResponse(final_json) - - ####### - - def changeEmailAccountPassword(self): - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if not os.path.exists('/home/cyberpanel/postfix'): - proc = httpProc(self.request, 'mailServer/changeEmailPassword.html', - {"status": 0}, 'changeEmailPassword') - return proc.render() - - websitesName = ACLManager.findAllSites(currentACL, userID) - websitesName = websitesName + ACLManager.findChildDomains(websitesName) - - proc = httpProc(self.request, 'mailServer/changeEmailPassword.html', - {'websiteList': websitesName, "status": 1}, 'changeEmailPassword') - return proc.render() - - def submitPasswordChange(self): - try: - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - if ACLManager.currentContextPermission(currentACL, 'changeEmailPassword') == 0: - return ACLManager.loadErrorJson('passChangeStatus', 0) - - data = json.loads(self.request.body) - email = data['email'] - password = data['passwordByPass'] - - emailDB = EUsers.objects.get(email=email) - - admin = Administrator.objects.get(pk=userID) - try: - if ACLManager.checkOwnership(emailDB.emailOwner.domainOwner.domain, admin, currentACL) == 1: - pass - else: - return ACLManager.loadErrorJson() - except: - if ACLManager.checkOwnership(emailDB.emailOwner.childOwner.domain, admin, currentACL) == 1: - pass - else: - return ACLManager.loadErrorJson() - - CentOSPath = '/etc/redhat-release' - if os.path.exists(CentOSPath): - password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) - password = '{CRYPT}%s' % (password.decode()) - emailDB.password = password - else: - password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) - password = '{CRYPT}%s' % (password.decode()) - emailDB.password = password - - emailDB.save() - - - data_ret = {'status': 1, 'passChangeStatus': 1, 'error_message': "None"} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - except BaseException as msg: - data_ret = {'status': 0, 'passChangeStatus': 0, 'error_message': str(msg)} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - ####### - - def dkimManager(self): - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - openDKIMInstalled = 1 - - websitesName = ACLManager.findAllSites(currentACL, userID) - websitesName = websitesName + ACLManager.findChildDomains(websitesName) - - proc = httpProc(self.request, 'mailServer/dkimManager.html', - {'websiteList': websitesName, 'openDKIMInstalled': openDKIMInstalled}, 'dkimManager') - return proc.render() - - def fetchDKIMKeys(self): - try: - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if ACLManager.currentContextPermission(currentACL, 'dkimManager') == 0: - return ACLManager.loadErrorJson('fetchStatus', 0) - - data = json.loads(self.request.body) - domainName = data['domainName'] - - admin = Administrator.objects.get(pk=userID) - if ACLManager.checkOwnership(domainName, admin, currentACL) == 1: - pass - else: - return ACLManager.loadError() - - try: - - command = 'chown cyberpanel:cyberpanel -R /usr/local/CyberCP/lib/python3.6/site-packages/tldextract/.suffix_cache' - ProcessUtilities.executioner(command) - - command = 'chown cyberpanel:cyberpanel -R /usr/local/CyberCP/lib/python3.8/site-packages/tldextract/.suffix_cache' - ProcessUtilities.executioner(command) - - import tldextract - - extractDomain = tldextract.extract(domainName) - domainName = extractDomain.domain + '.' + extractDomain.suffix - - path = "/etc/opendkim/keys/" + domainName + "/default.txt" - command = "sudo cat " + path - output = ProcessUtilities.outputExecutioner(command, 'opendkim') - leftIndex = output.index('(') + 2 - rightIndex = output.rindex(')') - 1 - - path = "/etc/opendkim/keys/" + domainName + "/default.private" - command = "sudo cat " + path - privateKey = ProcessUtilities.outputExecutioner(command, 'opendkim') - - DNS.createDKIMRecords(domainName) - - data_ret = {'status': 1, 'fetchStatus': 1, 'keysAvailable': 1, 'publicKey': output[leftIndex:rightIndex], - 'privateKey': privateKey, 'dkimSuccessMessage': 'Keys successfully fetched!', - 'error_message': "None"} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - except BaseException as msg: - data_ret = {'status': 1, 'fetchStatus': 1, 'keysAvailable': 0, 'error_message': str(msg)} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - except BaseException as msg: - data_ret = {'status': 0, 'fetchStatus': 0, 'error_message': str(msg)} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - def generateDKIMKeys(self): - try: - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - - if ACLManager.currentContextPermission(currentACL, 'dkimManager') == 0: - return ACLManager.loadErrorJson('generateStatus', 0) - - data = json.loads(self.request.body) - domainName = data['domainName'] - - admin = Administrator.objects.get(pk=userID) - if ACLManager.checkOwnership(domainName, admin, currentACL) == 1: - pass - else: - return ACLManager.loadErrorJson() - - execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/mailUtilities.py" - execPath = execPath + " generateKeys --domain " + domainName - output = ProcessUtilities.outputExecutioner(execPath) - - admin = Administrator.objects.get(pk=userID) - DNS.dnsTemplate(domainName, admin) - - if output.find("1,None") > -1: - - command = 'chown cyberpanel:cyberpanel -R /usr/local/CyberCP/lib/python3.6/site-packages/tldextract/.suffix_cache' - ProcessUtilities.executioner(command) - - command = 'chown cyberpanel:cyberpanel -R /usr/local/CyberCP/lib/python3.8/site-packages/tldextract/.suffix_cache' - ProcessUtilities.executioner(command) - - import tldextract - - extractDomain = tldextract.extract(domainName) - topLevelDomain = extractDomain.domain + '.' + extractDomain.suffix - - zone = dnsDomains.objects.get(name=topLevelDomain) - zone.save() - - path = "/etc/opendkim/keys/" + domainName + "/default.txt" - command = "cat " + path - output = ProcessUtilities.outputExecutioner(command) - leftIndex = output.index('(') + 2 - rightIndex = output.rindex(')') - 1 - - DNS.createDKIMRecords(domainName) - - record = dnsRecords(domainOwner=zone, - domain_id=zone.id, - name="default._domainkey." + domainName, - type="TXT", - content=output[leftIndex:rightIndex], - ttl=3600, - prio=0, - disabled=0, - auth=1) - record.save() - - data_ret = {'status': 1, 'generateStatus': 1, 'error_message': "None"} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - else: - data_ret = {'status': 0, 'generateStatus': 0, 'error_message': output} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - except BaseException as msg: - data_ret = {'status': 0, 'generateStatus': 0, 'error_message': str(msg)} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - - def installOpenDKIM(self): - try: - userID = self.request.session['userID'] - currentACL = ACLManager.loadedACL(userID) - if ACLManager.currentContextPermission(currentACL, 'dkimManager') == 0: - return ACLManager.loadErrorJson('installOpenDKIM', 0) - _thread.start_new_thread(mailUtilities.installOpenDKIM, ('Install', 'openDKIM')) - final_json = json.dumps({'installOpenDKIM': 1, 'error_message': "None"}) - return HttpResponse(final_json) - except BaseException as msg: - final_dic = {'installOpenDKIM': 0, 'error_message': str(msg)} - final_json = json.dumps(final_dic) - return HttpResponse(final_json) - - def installStatusOpenDKIM(self): - try: - command = "sudo cat " + mailUtilities.installLogPath - installStatus = subprocess.check_output(shlex.split(command)).decode("utf-8") - - if installStatus.find("[200]") > -1: - - execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/mailUtilities.py" - execPath = execPath + " configureOpenDKIM" - - output = ProcessUtilities.outputExecutioner(execPath) - - if output.find("1,None") > -1: - pass - else: - final_json = json.dumps({ - 'error_message': "Failed to install OpenDKIM configurations.", - 'requestStatus': installStatus, - 'abort': 1, - 'installed': 0, - }) - return HttpResponse(final_json) - - final_json = json.dumps({ - 'error_message': "None", - 'requestStatus': installStatus, - 'abort': 1, - 'installed': 1, - }) - return HttpResponse(final_json) - elif installStatus.find("[404]") > -1: - - final_json = json.dumps({ - 'abort': 1, - 'installed': 0, - 'error_message': "None", - 'requestStatus': installStatus, - }) - return HttpResponse(final_json) - else: - final_json = json.dumps({ - 'abort': 0, - 'error_message': "None", - 'requestStatus': installStatus, - }) - return HttpResponse(final_json) - - except BaseException as msg: - final_dic = {'abort': 1, 'installed': 0, 'error_message': str(msg)} - final_json = json.dumps(final_dic) - return HttpResponse(final_json) - - ####### - - def checkIfMailServerSSLIssued(self): - postfixPath = '/etc/postfix/main.cf' - - postFixData = ProcessUtilities.outputExecutioner('cat %s' % (postfixPath)) - - if postFixData.find('myhostname = server.example.com') > -1: - return 0 - else: - try: - - postFixLines = ProcessUtilities.outputExecutioner('cat %s' % (postfixPath)).splitlines() - - for items in postFixLines: - if items.find('myhostname') > -1 and items[0] != '#': - self.mailHostName = items.split('=')[1].strip(' ') - self.MailSSL = 1 - except BaseException as msg: - logging.CyberCPLogFileWriter.writeToFile('%s. [checkIfMailServerSSLIssued:864]' % (str(msg))) - - ipFile = "/etc/cyberpanel/machineIP" - f = open(ipFile) - ipData = f.read() - ipAddress = ipData.split('\n', 1)[0] - - command = 'openssl s_client -connect %s:465' % (ipAddress) - result = ProcessUtilities.outputExecutioner(command) - - if result.find('18 (self signed certificate)') > -1: - return 0 - else: - return 1 - - def RunServerLevelEmailChecks(self): - try: - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Checking if MailServer SSL issued..,10') - - reportFile = self.extraArgs['reportFile'] - - report = {} - report['MailSSL'] = self.checkIfMailServerSSLIssued() - - writeToFile = open(reportFile, 'w') - writeToFile.write(json.dumps(report)) - writeToFile.close() - - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Completed [200].') - - except BaseException as msg: - final_dic = {'installOpenDKIM': 0, 'error_message': str(msg)} - final_json = json.dumps(final_dic) - return HttpResponse(final_json) - - def install_postfix_dovecot(self): - try: - if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8: - command = 'yum remove postfix -y' - ProcessUtilities.executioner(command) - elif ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu: - command = 'apt-get -y remove postfix' - ProcessUtilities.executioner(command) - - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Re-installing postfix..,10') - - if ProcessUtilities.decideDistro() == ProcessUtilities.centos: - command = 'yum install --enablerepo=gf-plus -y postfix3 postfix3-ldap postfix3-mysql postfix3-pcre' - elif ProcessUtilities.decideDistro() == ProcessUtilities.cent8: - - command = 'dnf --nogpg install -y https://mirror.ghettoforge.net/distributions/gf/el/8/gf/x86_64/gf-release-8-11.gf.el8.noarch.rpm' - ProcessUtilities.executioner(command) - - command = 'dnf install --enablerepo=gf-plus postfix3 postfix3-mysql -y' - ProcessUtilities.executioner(command) - else: - command = 'apt-get install -y debconf-utils' - ProcessUtilities.executioner(command) - file_name = 'pf.unattend.text' - pf = open(file_name, 'w') - pf.write('postfix postfix/mailname string ' + str(socket.getfqdn() + '\n')) - pf.write('postfix postfix/main_mailer_type string "Internet Site"\n') - pf.close() - command = 'debconf-set-selections ' + file_name - ProcessUtilities.executioner(command) - - command = 'apt-get -y install postfix' - # os.remove(file_name) - - ProcessUtilities.executioner(command) - - import socket - # We are going to leverage postconfig -e to edit the settings for hostname - command = '"postconf -e "myhostname = %s"' % (str(socket.getfqdn())) - ProcessUtilities.executioner(command) - command = '"postconf -e "myhostname = %s"' % (str(socket.getfqdn())) - ProcessUtilities.executioner(command) - - # We are explicitly going to use sed to set the hostname default from "myhostname = server.example.com" - # to the fqdn from socket if the default is still found - postfix_main = '/etc/postfix/main.cf' - command = "sed -i 's|server.example.com|%s|g' %s" % (str(socket.getfqdn()), postfix_main) - ProcessUtilities.executioner(command) - - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Re-installing Dovecot..,15') - - if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8: - pass - else: - command = 'apt-get -y install dovecot-imapd dovecot-pop3d postfix-mysql' - - ProcessUtilities.executioner(command) - - ## - - if ProcessUtilities.decideDistro() == ProcessUtilities.centos: - command = 'yum --enablerepo=gf-plus -y install dovecot23 dovecot23-mysql' - elif ProcessUtilities.decideDistro() == ProcessUtilities.cent8: - command = 'dnf install --enablerepo=gf-plus dovecot23 dovecot23-mysql -y' - else: - command = 'apt-get -y install dovecot-mysql' - - ProcessUtilities.executioner(command) - - if ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu: - - command = 'curl https://repo.dovecot.org/DOVECOT-REPO-GPG | gpg --import' - subprocess.call(command, shell=True) - - command = 'gpg --export ED409DA1 > /etc/apt/trusted.gpg.d/dovecot.gpg' - subprocess.call(command, shell=True) - - debPath = '/etc/apt/sources.list.d/dovecot.list' - writeToFile = open(debPath, 'w') - writeToFile.write('deb https://repo.dovecot.org/ce-2.3-latest/ubuntu/bionic bionic main\n') - writeToFile.close() - - try: - command = 'apt update -y' - subprocess.call(command, shell=True) - except: - pass - - try: - command = 'DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical sudo apt-get -q -y -o "Dpkg::Options::=--force-confdef" -o "Dpkg::Options::=--force-confold" --only-upgrade install dovecot-mysql -y' - subprocess.call(command, shell=True) - - command = 'dpkg --configure -a' - subprocess.call(command, shell=True) - - command = 'apt --fix-broken install -y' - subprocess.call(command, shell=True) - - command = 'DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical sudo apt-get -q -y -o "Dpkg::Options::=--force-confdef" -o "Dpkg::Options::=--force-confold" --only-upgrade install dovecot-mysql -y' - subprocess.call(command, shell=True) - except: - pass - - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Postfix/dovecot reinstalled.,40') - - except BaseException as msg: - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], '%s [install_postfix_dovecot][404]' % (str(msg)), 10) - return 0 - - return 1 - - def setup_email_Passwords(self, mysqlPassword): - try: - - - mysql_virtual_domains = "/usr/local/CyberCP/install/email-configs-one/mysql-virtual_domains.cf" - mysql_virtual_forwardings = "/usr/local/CyberCP/install/email-configs-one/mysql-virtual_forwardings.cf" - mysql_virtual_mailboxes = "/usr/local/CyberCP/install/email-configs-one/mysql-virtual_mailboxes.cf" - mysql_virtual_email2email = "/usr/local/CyberCP/install/email-configs-one/mysql-virtual_email2email.cf" - dovecotmysql = "/usr/local/CyberCP/install/email-configs-one/dovecot-sql.conf.ext" - - ### update password: - - data = open(dovecotmysql, "r").readlines() - - writeDataToFile = open(dovecotmysql, "w") - - dataWritten = "connect = host=localhost dbname=cyberpanel user=cyberpanel password=" + mysqlPassword + " port=3306\n" - - for items in data: - if items.find("connect") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - # if self.distro == ubuntu: - # os.fchmod(writeDataToFile.fileno(), stat.S_IRUSR | stat.S_IWUSR) - - writeDataToFile.close() - - ### update password: - - data = open(mysql_virtual_domains, "r").readlines() - - writeDataToFile = open(mysql_virtual_domains, "w") - - dataWritten = "password = " + mysqlPassword + "\n" - - for items in data: - if items.find("password") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - # if self.distro == ubuntu: - # os.fchmod(writeDataToFile.fileno(), stat.S_IRUSR | stat.S_IWUSR) - - writeDataToFile.close() - - ### update password: - - data = open(mysql_virtual_forwardings, "r").readlines() - - writeDataToFile = open(mysql_virtual_forwardings, "w") - - dataWritten = "password = " + mysqlPassword + "\n" - - for items in data: - if items.find("password") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - # if self.distro == ubuntu: - # os.fchmod(writeDataToFile.fileno(), stat.S_IRUSR | stat.S_IWUSR) - - writeDataToFile.close() - - ### update password: - - data = open(mysql_virtual_mailboxes, "r").readlines() - - writeDataToFile = open(mysql_virtual_mailboxes, "w") - - dataWritten = "password = " + mysqlPassword + "\n" - - for items in data: - if items.find("password") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - # if self.distro == ubuntu: - # os.fchmod(writeDataToFile.fileno(), stat.S_IRUSR | stat.S_IWUSR) - - writeDataToFile.close() - - ### update password: - - data = open(mysql_virtual_email2email, "r").readlines() - - writeDataToFile = open(mysql_virtual_email2email, "w") - - dataWritten = "password = " + mysqlPassword + "\n" - - for items in data: - if items.find("password") > -1: - writeDataToFile.writelines(dataWritten) - else: - writeDataToFile.writelines(items) - - # if self.distro == ubuntu: - # os.fchmod(writeDataToFile.fileno(), stat.S_IRUSR | stat.S_IWUSR) - - writeDataToFile.close() - - if self.remotemysql == 'ON': - command = "sed -i 's|host=localhost|host=%s|g' %s" % (self.mysqlhost, dovecotmysql) - ProcessUtilities.executioner(command) - - command = "sed -i 's|port=3306|port=%s|g' %s" % (self.mysqlport, dovecotmysql) - ProcessUtilities.executioner(command) - - ## - - command = "sed -i 's|localhost|%s:%s|g' %s" % (self.mysqlhost, self.mysqlport, mysql_virtual_domains) - ProcessUtilities.executioner(command) - - command = "sed -i 's|localhost|%s:%s|g' %s" % ( - self.mysqlhost, self.mysqlport, mysql_virtual_forwardings) - ProcessUtilities.executioner(command) - - command = "sed -i 's|localhost|%s:%s|g' %s" % ( - self.mysqlhost, self.mysqlport, mysql_virtual_mailboxes) - ProcessUtilities.executioner(command) - - command = "sed -i 's|localhost|%s:%s|g' %s" % ( - self.mysqlhost, self.mysqlport, mysql_virtual_email2email) - ProcessUtilities.executioner(command) - except BaseException as msg: - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], - '%s [setup_email_Passwords][404]' % (str(msg)), 10) - return 0 - - return 1 - - def centos_lib_dir_to_ubuntu(self, filename, old, new): - try: - fd = open(filename, 'r') - lines = fd.readlines() - fd.close() - fd = open(filename, 'w') - centos_prefix = old - ubuntu_prefix = new - for line in lines: - index = line.find(centos_prefix) - if index != -1: - line = line[:index] + ubuntu_prefix + line[index + len(centos_prefix):] - fd.write(line) - fd.close() - except BaseException as msg: - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], - '%s [centos_lib_dir_to_ubuntu][404]' % (str(msg)), 10) - - def setup_postfix_dovecot_config(self): - try: - - mysql_virtual_domains = "/etc/postfix/mysql-virtual_domains.cf" - mysql_virtual_forwardings = "/etc/postfix/mysql-virtual_forwardings.cf" - mysql_virtual_mailboxes = "/etc/postfix/mysql-virtual_mailboxes.cf" - mysql_virtual_email2email = "/etc/postfix/mysql-virtual_email2email.cf" - main = "/etc/postfix/main.cf" - master = "/etc/postfix/master.cf" - dovecot = "/etc/dovecot/dovecot.conf" - dovecotmysql = "/etc/dovecot/dovecot-sql.conf.ext" - - if os.path.exists(mysql_virtual_domains): - os.remove(mysql_virtual_domains) - - if os.path.exists(mysql_virtual_forwardings): - os.remove(mysql_virtual_forwardings) - - if os.path.exists(mysql_virtual_mailboxes): - os.remove(mysql_virtual_mailboxes) - - if os.path.exists(mysql_virtual_email2email): - os.remove(mysql_virtual_email2email) - - if os.path.exists(main): - os.remove(main) - - if os.path.exists(master): - os.remove(master) - - if os.path.exists(dovecot): - os.remove(dovecot) - - if os.path.exists(dovecotmysql): - os.remove(dovecotmysql) - - ###############Getting SSL - - command = 'openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout /etc/postfix/key.pem -out /etc/postfix/cert.pem' - ProcessUtilities.executioner(command) - - ## - - command = 'openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout /etc/dovecot/key.pem -out /etc/dovecot/cert.pem' - ProcessUtilities.executioner(command) - - # Cleanup config files for ubuntu - if ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu: - self.centos_lib_dir_to_ubuntu("/usr/local/CyberCP/install/email-configs-one/master.cf", "/usr/libexec/", "/usr/lib/") - self.centos_lib_dir_to_ubuntu("/usr/local/CyberCP/install/email-configs-one/main.cf", "/usr/libexec/postfix", - "/usr/lib/postfix/sbin") - - - ########### Copy config files - import shutil - - shutil.copy("/usr/local/CyberCP/install/email-configs-one/mysql-virtual_domains.cf", "/etc/postfix/mysql-virtual_domains.cf") - shutil.copy("/usr/local/CyberCP/install/email-configs-one/mysql-virtual_forwardings.cf", - "/etc/postfix/mysql-virtual_forwardings.cf") - shutil.copy("/usr/local/CyberCP/install/email-configs-one/mysql-virtual_mailboxes.cf", "/etc/postfix/mysql-virtual_mailboxes.cf") - shutil.copy("/usr/local/CyberCP/install/email-configs-one/mysql-virtual_email2email.cf", - "/etc/postfix/mysql-virtual_email2email.cf") - shutil.copy("/usr/local/CyberCP/install/email-configs-one/main.cf", main) - shutil.copy("/usr/local/CyberCP/install/email-configs-one/master.cf", master) - shutil.copy("/usr/local/CyberCP/install/email-configs-one/dovecot.conf", dovecot) - shutil.copy("/usr/local/CyberCP/install/email-configs-one/dovecot-sql.conf.ext", dovecotmysql) - - - ######################################## Permissions - - command = 'chmod o= /etc/postfix/mysql-virtual_domains.cf' - ProcessUtilities.executioner(command) - - ## - - command = 'chmod o= /etc/postfix/mysql-virtual_forwardings.cf' - ProcessUtilities.executioner(command) - - ## - - command = 'chmod o= /etc/postfix/mysql-virtual_mailboxes.cf' - ProcessUtilities.executioner(command) - - ## - - command = 'chmod o= /etc/postfix/mysql-virtual_email2email.cf' - ProcessUtilities.executioner(command) - - ## - - command = 'chmod o= ' + main - ProcessUtilities.executioner(command) - - ## - - command = 'chmod o= ' + master - ProcessUtilities.executioner(command) - - ####################################### - - command = 'chgrp postfix /etc/postfix/mysql-virtual_domains.cf' - ProcessUtilities.executioner(command) - - ## - - command = 'chgrp postfix /etc/postfix/mysql-virtual_forwardings.cf' - ProcessUtilities.executioner(command) - ## - - command = 'chgrp postfix /etc/postfix/mysql-virtual_mailboxes.cf' - ProcessUtilities.executioner(command) - - ## - - command = 'chgrp postfix /etc/postfix/mysql-virtual_email2email.cf' - ProcessUtilities.executioner(command) - - ## - - command = 'chgrp postfix ' + main - ProcessUtilities.executioner(command) - - ## - - command = 'chgrp postfix ' + master - ProcessUtilities.executioner(command) - - ######################################## users and groups - - command = 'groupadd -g 5000 vmail' - ProcessUtilities.executioner(command) - - ## - - command = 'useradd -g vmail -u 5000 vmail -d /home/vmail -m' - ProcessUtilities.executioner(command) - - ######################################## Further configurations - - # hostname = socket.gethostname() - - ################################### Restart postix - - command = 'systemctl enable postfix.service' - ProcessUtilities.executioner(command) - - ## - - command = 'systemctl start postfix.service' - ProcessUtilities.executioner(command) - - ######################################## Permissions - - command = 'chgrp dovecot /etc/dovecot/dovecot-sql.conf.ext' - ProcessUtilities.executioner(command) - - ## - - command = 'chmod o= /etc/dovecot/dovecot-sql.conf.ext' - ProcessUtilities.executioner(command) - - ################################### Restart dovecot - - command = 'systemctl enable dovecot.service' - ProcessUtilities.executioner(command) - - ## - - command = 'systemctl start dovecot.service' - ProcessUtilities.executioner(command) - - ## - - command = 'systemctl restart postfix.service' - ProcessUtilities.executioner(command) - - ## changing permissions for main.cf - - command = "chmod 755 " + main - ProcessUtilities.executioner(command) - - if ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu: - command = "mkdir -p /etc/pki/dovecot/private/" - ProcessUtilities.executioner(command) - - command = "mkdir -p /etc/pki/dovecot/certs/" - ProcessUtilities.executioner(command) - - command = "mkdir -p /etc/opendkim/keys/" - ProcessUtilities.executioner(command) - - command = "sed -i 's/auth_mechanisms = plain/#auth_mechanisms = plain/g' /etc/dovecot/conf.d/10-auth.conf" - ProcessUtilities.executioner(command) - - ## Ubuntu 18.10 ssl_dh for dovecot 2.3.2.1 - - if ProcessUtilities.ubuntu: - dovecotConf = '/etc/dovecot/dovecot.conf' - - data = open(dovecotConf, 'r').readlines() - writeToFile = open(dovecotConf, 'w') - for items in data: - if items.find('ssl_key = -1: - writeToFile.writelines(items) - writeToFile.writelines('ssl_dh = -1 and items.find('local:') and items[0] != '#': - writeToFile.writelines('Socket inet:8891@localhost\n') - else: - writeToFile.writelines(items) - writeToFile.close() - - #### Restarting Postfix and OpenDKIM - - command = "systemctl start opendkim" - ProcessUtilities.executioner(command) - - command = "systemctl enable opendkim" - ProcessUtilities.executioner(command) - - ## - - command = "systemctl restart postfix" - ProcessUtilities.executioner(command) - - return 1 - - except BaseException as msg: - return 0 - - def fixCyberPanelPermissions(self): - - ###### fix Core CyberPanel permissions - command = "find /usr/local/CyberCP -type d -exec chmod 0755 {} \;" - ProcessUtilities.executioner(command) - - command = "find /usr/local/CyberCP -type f -exec chmod 0644 {} \;" - ProcessUtilities.executioner(command) - - command = "chmod -R 755 /usr/local/CyberCP/bin" - ProcessUtilities.executioner(command) - - ## change owner - - command = "chown -R root:root /usr/local/CyberCP" - ProcessUtilities.executioner(command) - - ########### Fix LSCPD - - command = "find /usr/local/lscp -type d -exec chmod 0755 {} \;" - ProcessUtilities.executioner(command) - - command = "find /usr/local/lscp -type f -exec chmod 0644 {} \;" - ProcessUtilities.executioner(command) - - command = "chmod -R 755 /usr/local/lscp/bin" - ProcessUtilities.executioner(command) - - command = "chmod -R 755 /usr/local/lscp/fcgi-bin" - ProcessUtilities.executioner(command) - - command = "chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/tmp" - ProcessUtilities.executioner(command) - - ## change owner - - command = "chown -R root:root /usr/local/lscp" - ProcessUtilities.executioner(command) - - command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/rainloop/data" - ProcessUtilities.executioner(command) - - command = "chmod 700 /usr/local/CyberCP/cli/cyberPanel.py" - ProcessUtilities.executioner(command) - - command = "chmod 700 /usr/local/CyberCP/plogical/upgradeCritical.py" - ProcessUtilities.executioner(command) - - command = "chmod 755 /usr/local/CyberCP/postfixSenderPolicy/client.py" - ProcessUtilities.executioner(command) - - command = "chmod 640 /usr/local/CyberCP/CyberCP/settings.py" - ProcessUtilities.executioner(command) - - command = "chown root:cyberpanel /usr/local/CyberCP/CyberCP/settings.py" - ProcessUtilities.executioner(command) - - files = ['/etc/yum.repos.d/MariaDB.repo', '/etc/pdns/pdns.conf', '/etc/systemd/system/lscpd.service', - '/etc/pure-ftpd/pure-ftpd.conf', '/etc/pure-ftpd/pureftpd-pgsql.conf', - '/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/pureftpd-ldap.conf', - '/etc/dovecot/dovecot.conf', '/usr/local/lsws/conf/httpd_config.xml', - '/usr/local/lsws/conf/modsec.conf', '/usr/local/lsws/conf/httpd.conf'] - - for items in files: - command = 'chmod 644 %s' % (items) - ProcessUtilities.executioner(command) - - impFile = ['/etc/pure-ftpd/pure-ftpd.conf', '/etc/pure-ftpd/pureftpd-pgsql.conf', - '/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/pureftpd-ldap.conf', - '/etc/dovecot/dovecot.conf', '/etc/pdns/pdns.conf', '/etc/pure-ftpd/db/mysql.conf', - '/etc/powerdns/pdns.conf'] - - for items in impFile: - command = 'chmod 600 %s' % (items) - ProcessUtilities.executioner(command) - - command = 'chmod 640 /etc/postfix/*.cf' - subprocess.call(command, shell=True) - - command = 'chmod 644 /etc/postfix/main.cf' - subprocess.call(command, shell=True) - - command = 'chmod 640 /etc/dovecot/*.conf' - subprocess.call(command, shell=True) - - command = 'chmod 644 /etc/dovecot/dovecot.conf' - subprocess.call(command, shell=True) - - command = 'chmod 640 /etc/dovecot/dovecot-sql.conf.ext' - subprocess.call(command, shell=True) - - command = 'chmod 644 /etc/postfix/dynamicmaps.cf' - subprocess.call(command, shell=True) - - fileM = ['/usr/local/lsws/FileManager/', '/usr/local/CyberCP/install/FileManager', - '/usr/local/CyberCP/serverStatus/litespeed/FileManager', '/usr/local/lsws/Example/html/FileManager'] - - for items in fileM: - try: - import shutil - shutil.rmtree(items) - except: - pass - - command = 'chmod 755 /etc/pure-ftpd/' - subprocess.call(command, shell=True) - - command = 'chmod +x /usr/local/CyberCP/plogical/renew.py' - ProcessUtilities.executioner(command) - - command = 'chmod +x /usr/local/CyberCP/CLManager/CLPackages.py' - ProcessUtilities.executioner(command) - - clScripts = ['/usr/local/CyberCP/CLScript/panel_info.py', '/usr/local/CyberCP/CLScript/CloudLinuxPackages.py', - '/usr/local/CyberCP/CLScript/CloudLinuxUsers.py', - '/usr/local/CyberCP/CLScript/CloudLinuxDomains.py' - , '/usr/local/CyberCP/CLScript/CloudLinuxResellers.py', '/usr/local/CyberCP/CLScript/CloudLinuxAdmins.py', - '/usr/local/CyberCP/CLScript/CloudLinuxDB.py', '/usr/local/CyberCP/CLScript/UserInfo.py'] - - for items in clScripts: - command = 'chmod +x %s' % (items) - ProcessUtilities.executioner(command) - - command = 'chmod 600 /usr/local/CyberCP/plogical/adminPass.py' - ProcessUtilities.executioner(command) - - command = 'chmod 600 /etc/cagefs/exclude/cyberpanelexclude' - ProcessUtilities.executioner(command) - - command = "find /usr/local/CyberCP/ -name '*.pyc' -delete" - ProcessUtilities.executioner(command) - - if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.cent8: - - command = 'chown root:pdns /etc/pdns/pdns.conf' - ProcessUtilities.executioner(command) - - command = 'chmod 640 /etc/pdns/pdns.conf' - ProcessUtilities.executioner(command) - - command = 'chmod 640 /usr/local/lscp/cyberpanel/logs/access.log' - ProcessUtilities.executioner(command) - - ### - - def ResetEmailConfigurations(self): - try: - ### Check if remote or local mysql - - passFile = "/etc/cyberpanel/mysqlPassword" - - try: - jsonData = json.loads(ProcessUtilities.outputExecutioner('cat %s' % (passFile))) - - self.mysqluser = jsonData['mysqluser'] - self.mysqlpassword = jsonData['mysqlpassword'] - self.mysqlport = jsonData['mysqlport'] - self.mysqlhost = jsonData['mysqlhost'] - self.remotemysql = 'ON' - - if self.mysqlhost.find('rds.amazon') > -1: - self.RDS = 1 - - ## Also set localhost to this server - - ipFile = "/etc/cyberpanel/machineIP" - f = open(ipFile) - ipData = f.read() - ipAddressLocal = ipData.split('\n', 1)[0] - - self.LOCALHOST = ipAddressLocal - except BaseException as msg: - self.remotemysql = 'OFF' - - if os.path.exists(ProcessUtilities.debugPath): - logging.CyberCPLogFileWriter.writeToFile('%s. [setupConnection:75]' % (str(msg))) - - ### - - self.checkIfMailServerSSLIssued() - - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Removing and re-installing postfix/dovecot..,5') - - if self.install_postfix_dovecot() == 0: - return 0 - - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Resetting configurations..,40') - - import sys - sys.path.append('/usr/local/CyberCP') - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") - from CyberCP import settings - - if self.setup_email_Passwords(settings.DATABASES['default']['PASSWORD']) == 0: - return 0 - - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Configurations reset..,70') - - if self.setup_postfix_dovecot_config() == 0: - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'setup_postfix_dovecot_config failed. [404].') - return 0 - - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Restoreing OpenDKIM configurations..,70') - - if self.configureOpenDKIM() == 0: - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'configureOpenDKIM failed. [404].') - return 0 - - - if self.MailSSL: - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Setting up Mail Server SSL if any..,75') - from plogical.virtualHostUtilities import virtualHostUtilities - virtualHostUtilities.issueSSLForMailServer(self.mailHostName, '/home/%s/public_html' % (self.mailHostName)) - - from websiteFunctions.models import ChildDomains - from plogical.virtualHostUtilities import virtualHostUtilities - for websites in Websites.objects.all(): - try: - child = ChildDomains.objects.get(domain='mail.%s' % (websites.domain)) - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], - 'Creating mail domain for %s..,80' % (websites.domain)) - virtualHostUtilities.setupAutoDiscover(1, '/dev/null', websites.domain, websites.admin) - except: - pass - - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Fixing permissions..,90') - - self.fixCyberPanelPermissions() - - logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Completed [200].') - - except BaseException as msg: - final_dic = {'installOpenDKIM': 0, 'error_message': str(msg)} - final_json = json.dumps(final_dic) - return HttpResponse(final_json) - - def debugEmailForSite(self, websiteName): - - ipFile = "/etc/cyberpanel/machineIP" - f = open(ipFile) - ipData = f.read() - ipAddress = ipData.split('\n', 1)[0] - - try: - import socket - siteIPAddr = socket.gethostbyname('mail.%s' % (websiteName)) - - if siteIPAddr != ipAddress: - return 0, 'mail.%s does not point to %s.' % (websiteName, ipAddress) - except: - return 0, 'mail.%s does not point to %s.' % (websiteName, ipAddress) - - command = 'openssl s_client -connect mail.%s:993' % (websiteName) - result = ProcessUtilities.outputExecutioner(command) - - if result.find('18 (self signed certificate)') > -1: - return 0, 'No valid SSL on port 993.' - else: - return 1, 'All checks are OK.' - -def main(): - - parser = argparse.ArgumentParser(description='CyberPanel') - parser.add_argument('function', help='Specifiy a function to call!') - parser.add_argument('--tempStatusPath', help='Path of temporary status file.') - - args = parser.parse_args() - - if args.function == "ResetEmailConfigurations": - extraArgs = {'tempStatusPath': args.tempStatusPath} - background = MailServerManager(None, 'ResetEmailConfigurations', extraArgs) - background.ResetEmailConfigurations() - -if __name__ == "__main__": - main() diff --git a/mailServer/templates/mailServer/index.html.bak b/mailServer/templates/mailServer/index.html.bak deleted file mode 100644 index e0f619981..000000000 --- a/mailServer/templates/mailServer/index.html.bak +++ /dev/null @@ -1,121 +0,0 @@ -{% extends "baseTemplate/index.html" %} -{% load i18n %} -{% block title %}{% trans "Mail Functions - CyberPanel" %}{% endblock %} -{% block content %} - - {% load static %} - {% get_current_language as LANGUAGE_CODE %} - - -
-
-

{% trans "Mail Functions" %}

-

{% trans "Manage email accounts on this page." %}

-
- - - -
- - -{% endblock %} diff --git a/plogical/acl.py b/plogical/acl.py index 5fe1d1d16..071fc110c 100644 --- a/plogical/acl.py +++ b/plogical/acl.py @@ -1372,7 +1372,7 @@ echo $oConfig->Save() ? 'Done' : 'Error'; command = 'chmod 640 /usr/local/lscp/cyberpanel/logs/access.log' ProcessUtilities.executioner(command, 'root', True) - command = '/usr/local/lsws/lsphp72/bin/php /usr/local/CyberCP/public/snappymail.php' + command = '/usr/local/lsws/lsphp83/bin/php /usr/local/CyberCP/public/snappymail.php' ProcessUtilities.executioner(command, 'root', True) command = 'chmod 600 /usr/local/CyberCP/public/snappymail.php' diff --git a/plogical/filesPermsUtilities.py.bak b/plogical/filesPermsUtilities.py.bak deleted file mode 100644 index 8e71af9aa..000000000 --- a/plogical/filesPermsUtilities.py.bak +++ /dev/null @@ -1,190 +0,0 @@ -import os -import shutil -import pathlib -import stat - - -def mkdir_p(path, exist_ok=True): - """ - Creates the directory and paths leading up to it like unix mkdir -p . - Defaults to exist_ok so if it exists were not throwing fatal errors - https://docs.python.org/3.7/library/os.html#os.makedirs - """ - if not os.path.exists(path): - print('creating directory: ' + path) - os.makedirs(path, exist_ok) - - -def chmod_digit(file_path, perms): - """ - Helper function to chmod like you would in unix without having to preface 0o or converting to octal yourself. - Credits: https://stackoverflow.com/a/60052847/1621381 - """ - try: - os.chmod(file_path, int(str(perms), base=8)) - except: - print(f'Could not chmod : {file_path} to {perms}') - pass - - -def touch(filepath: str, exist_ok=True): - """ - Touches a file like unix `touch somefile` would. - """ - try: - pathlib.Path(filepath).touch(exist_ok) - except FileExistsError: - print('Could touch : ' + filepath) - pass - - -def symlink(src, dst): - """ - Symlink a path to another if the src exists. - """ - try: - if os.access(src, os.R_OK): - os.symlink(src, dst) - except: - print(f'Could not symlink Source: {src} > Destination: {dst}') - pass - - -def chown(path, user, group=-1): - """ - Chown file/path to user/group provided. Passing -1 to user or group will leave it unchanged. - Useful if just changing user or group vs both. - """ - try: - shutil.chown(path, user, group) - except PermissionError: - print(f'Could not change permissions for: {path} to {user}:{group}') - pass - - -def recursive_chown(path, owner, group=-1): - """ - Recursively chown a path and contents to owner. - https://docs.python.org/3/library/shutil.html - """ - for dirpath, dirnames, filenames in os.walk(path): - try: - shutil.chown(dirpath, owner, group) - except PermissionError: - print('Could not change permissions for: ' + dirpath + ' to: ' + owner) - pass - for filename in filenames: - try: - shutil.chown(os.path.join(dirpath, filename), owner, group) - except PermissionError: - print('Could not change permissions for: ' + os.path.join(dirpath, filename) + ' to: ' + owner) - pass - - -def recursive_permissions(path, dir_mode=755, file_mode=644, topdir=True): - """ - Recursively chmod a path and contents to mode. - Defaults to chmod top level directory but can be optionally - toggled off when you want to chmod only contents of like a user's homedir vs homedir itself - https://docs.python.org/3.6/library/os.html#os.walk - """ - - # Here we are converting the integers to string and then to octal. - # so this function doesn't need to be called with 0o prefixed for the file and dir mode - dir_mode = int(str(dir_mode), base=8) - file_mode = int(str(file_mode), base=8) - - if topdir: - # Set chmod on top level path - try: - os.chmod(path, dir_mode) - except: - print('Could not chmod :' + path + ' to ' + str(dir_mode)) - for root, dirs, files in os.walk(path): - for d in dirs: - try: - os.chmod(os.path.join(root, d), dir_mode) - except: - print('Could not chmod :' + os.path.join(root, d) + ' to ' + str(dir_mode)) - pass - for f in files: - try: - os.chmod(os.path.join(root, f), file_mode) - except: - print('Could not chmod :' + path + ' to ' + str(file_mode)) - pass - - -# Left intentionally here for reference. -# Set recursive chown for a path -# recursive_chown(my_path, 'root', 'root') -# for changing group recursively without affecting user -# recursive_chown('/usr/local/lscp/cyberpanel/rainloop/data', -1, 'lscpd') - -# explicitly set permissions for directories/folders to 0755 and files to 0644 -# recursive_permissions(my_path, 755, 644) - -# Fix permissions and use default values -# recursive_permissions(my_path) -# ========================================================= -# Below is a helper class for getting and working with permissions -# Original credits to : https://github.com/keysemble/perfm - -def perm_octal_digit(rwx): - digit = 0 - if rwx[0] == 'r': - digit += 4 - if rwx[1] == 'w': - digit += 2 - if rwx[2] == 'x': - digit += 1 - return digit - - -class FilePerm: - def __init__(self, filepath): - filemode = stat.filemode(os.stat(filepath).st_mode) - permissions = [filemode[-9:][i:i + 3] for i in range(0, len(filemode[-9:]), 3)] - self.filepath = filepath - self.access_dict = dict(zip(['user', 'group', 'other'], [list(perm) for perm in permissions])) - - def mode(self): - mode = 0 - for shift, digit in enumerate(self.octal()[::-1]): - mode += digit << (shift * 3) - return mode - - def digits(self): - """Get the octal chmod equivalent value 755 in single string""" - return "".join(map(str, self.octal())) - - def octal(self): - """Get the octal value in a list [7, 5, 5]""" - return [perm_octal_digit(p) for p in self.access_dict.values()] - - def access_bits(self, access): - if access in self.access_dict.keys(): - r, w, x = self.access_dict[access] - return [r == 'r', w == 'w', x == 'x'] - - def update_bitwise(self, settings): - def perm_list(read=False, write=False, execute=False): - pl = ['-', '-', '-'] - if read: - pl[0] = 'r' - if write: - pl[1] = 'w' - if execute: - pl[2] = 'x' - return pl - - self.access_dict = dict( - [(access, perm_list(read=r, write=w, execute=x)) for access, [r, w, x] in settings.items()]) - os.chmod(self.filepath, self.mode()) - -# project_directory = os.path.abspath(os.path.dirname(sys.argv[0])) -# home_directory = os.path.expanduser('~') -# print(f'Path: {home_directory} Mode: {FilePerm(home_directory).mode()} Octal: {FilePerm(home_directory).octal()} ' -# f'Digits: {FilePerm(home_directory).digits()}') -# Example: Output -# Path: /home/cooluser Mode: 493 Octal: [7, 5, 5] Digits: 755 \ No newline at end of file diff --git a/plogical/upgrade.py b/plogical/upgrade.py index 49221f4c9..851af6eb2 100644 --- a/plogical/upgrade.py +++ b/plogical/upgrade.py @@ -861,7 +861,7 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout'; command = f'wget -q -O /usr/local/CyberCP/snappymail_cyberpanel.php https://raw.githubusercontent.com/the-djmaze/snappymail/master/integrations/cyberpanel/install.php' Upgrade.executioner_silent(command, 'verify certificate', 0) - command = f'/usr/local/lsws/lsphp80/bin/php /usr/local/CyberCP/snappymail_cyberpanel.php' + command = f'/usr/local/lsws/lsphp83/bin/php /usr/local/CyberCP/snappymail_cyberpanel.php' Upgrade.executioner_silent(command, 'verify certificate', 0) # labsPath = '/usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/configs/application.ini' @@ -3121,7 +3121,7 @@ echo $oConfig->Save() ? 'Done' : 'Error'; command = 'chmod 640 /usr/local/lscp/cyberpanel/logs/access.log' Upgrade.executioner(command, 0) - command = '/usr/local/lsws/lsphp72/bin/php /usr/local/CyberCP/public/snappymail.php' + command = '/usr/local/lsws/lsphp83/bin/php /usr/local/CyberCP/public/snappymail.php' Upgrade.executioner_silent(command, 'Configure SnappyMail') command = 'chmod 600 /usr/local/CyberCP/public/snappymail.php' @@ -3182,44 +3182,221 @@ echo $oConfig->Save() ? 'Done' : 'Error'; command = '/root/.acme.sh/acme.sh --set-default-ca --server letsencrypt' Upgrade.executioner(command, command, 0) + @staticmethod + def check_package_availability(package_name): + """Check if a package is available in the repositories""" + try: + # Try to search for the package without installing + if os.path.exists('/etc/yum.repos.d/') or os.path.exists('/etc/dnf/dnf.conf'): + # RHEL-based systems + command = f"dnf search --quiet {package_name} 2>/dev/null | grep -q '^Last metadata expiration' || yum search --quiet {package_name} 2>/dev/null | head -1" + result = subprocess.run(command, shell=True, capture_output=True, text=True) + return result.returncode == 0 + else: + # Ubuntu/Debian systems + command = f"apt-cache search {package_name} 2>/dev/null | head -1" + result = subprocess.run(command, shell=True, capture_output=True, text=True) + return result.returncode == 0 and result.stdout.strip() != "" + except Exception as e: + Upgrade.stdOut(f"Error checking package availability for {package_name}: {str(e)}", 0) + return False + + @staticmethod + def is_almalinux9(): + """Check if running on AlmaLinux 9""" + if os.path.exists('/etc/almalinux-release'): + try: + with open('/etc/almalinux-release', 'r') as f: + content = f.read() + return 'release 9' in content + except: + return False + return False + + @staticmethod + def fix_almalinux9_mariadb(): + """Fix AlmaLinux 9 MariaDB installation issues""" + if not Upgrade.is_almalinux9(): + return + + Upgrade.stdOut("Applying AlmaLinux 9 MariaDB fixes...", 1) + + try: + # Disable problematic MariaDB MaxScale repository + Upgrade.stdOut("Disabling problematic MariaDB MaxScale repository...", 1) + command = "dnf config-manager --disable mariadb-maxscale 2>/dev/null || true" + subprocess.run(command, shell=True, capture_output=True) + + # Remove problematic repository files + Upgrade.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) + Upgrade.stdOut(f"Removed {repo_file}", 1) + + # Clean DNF cache + Upgrade.stdOut("Cleaning DNF cache...", 1) + command = "dnf clean all" + subprocess.run(command, shell=True, capture_output=True) + + # Install MariaDB from official repository + Upgrade.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'" + result = subprocess.run(command, shell=True, capture_output=True, text=True) + if result.returncode != 0: + Upgrade.stdOut(f"Warning: MariaDB repo setup failed: {result.stderr}", 0) + + # Install MariaDB packages + Upgrade.stdOut("Installing MariaDB packages...", 1) + mariadb_packages = "MariaDB-server MariaDB-client MariaDB-backup MariaDB-devel" + command = f"dnf install -y {mariadb_packages}" + result = subprocess.run(command, shell=True, capture_output=True, text=True) + if result.returncode != 0: + Upgrade.stdOut(f"Warning: MariaDB installation issues: {result.stderr}", 0) + + # Start and enable MariaDB service + Upgrade.stdOut("Starting MariaDB service...", 1) + services = ['mariadb', 'mysql', 'mysqld'] + for service in services: + try: + command = f"systemctl start {service}" + result = subprocess.run(command, shell=True, capture_output=True) + if result.returncode == 0: + command = f"systemctl enable {service}" + subprocess.run(command, shell=True, capture_output=True) + Upgrade.stdOut(f"MariaDB service started as {service}", 1) + break + except: + continue + + Upgrade.stdOut("AlmaLinux 9 MariaDB fixes completed", 1) + + except Exception as e: + Upgrade.stdOut(f"Error applying AlmaLinux 9 MariaDB fixes: {str(e)}", 0) + + @staticmethod + def get_available_php_versions(): + """Get list of available PHP versions based on OS""" + # Check for AlmaLinux 9+ first + if os.path.exists('/etc/almalinux-release'): + try: + with open('/etc/almalinux-release', 'r') as f: + content = f.read() + if 'release 9' in content or 'release 10' in content: + Upgrade.stdOut("AlmaLinux 9+ detected - checking available PHP versions", 1) + # AlmaLinux 9+ doesn't have PHP 7.1, 7.2, 7.3 + php_versions = ['74', '80', '81', '82', '83', '84', '85'] + else: + php_versions = ['71', '72', '73', '74', '80', '81', '82', '83', '84', '85'] + except: + php_versions = ['71', '72', '73', '74', '80', '81', '82', '83', '84', '85'] + else: + # Check other OS versions + os_info = Upgrade.findOperatingSytem() + if os_info in [Ubuntu24, CENTOS8]: + php_versions = ['74', '80', '81', '82', '83', '84', '85'] + else: + php_versions = ['71', '72', '73', '74', '80', '81', '82', '83', '84', '85'] + + # Check availability of each version + available_versions = [] + for version in php_versions: + if Upgrade.check_package_availability(f'lsphp{version}'): + available_versions.append(version) + else: + Upgrade.stdOut(f"PHP {version} not available on this OS", 0) + + return available_versions + + @staticmethod + def fixLiteSpeedConfig(): + """Fix LiteSpeed configuration issues by creating missing files""" + try: + Upgrade.stdOut("Checking and fixing LiteSpeed configuration...", 1) + + # Check if LiteSpeed is installed + if not os.path.exists('/usr/local/lsws'): + Upgrade.stdOut("LiteSpeed not found at /usr/local/lsws", 0) + return + + # Create missing configuration files + config_files = [ + "/usr/local/lsws/conf/httpd_config.xml", + "/usr/local/lsws/conf/httpd.conf", + "/usr/local/lsws/conf/modsec.conf" + ] + + for config_file in config_files: + if not os.path.exists(config_file): + Upgrade.stdOut(f"Missing LiteSpeed config: {config_file}", 0) + + # Create directory if it doesn't exist + os.makedirs(os.path.dirname(config_file), exist_ok=True) + + # Create minimal config file + if config_file.endswith('httpd_config.xml'): + with open(config_file, 'w') as f: + f.write('\n') + f.write('\n') + f.write(' \n') + f.write(' \n') + f.write(' Default\n') + f.write('
*:8088
\n') + f.write('
\n') + f.write('
\n') + elif config_file.endswith('httpd.conf'): + with open(config_file, 'w') as f: + f.write('# Minimal LiteSpeed HTTP configuration\n') + f.write('# This file will be updated by CyberPanel\n') + elif config_file.endswith('modsec.conf'): + with open(config_file, 'w') as f: + f.write('# ModSecurity configuration\n') + f.write('# This file will be updated by CyberPanel\n') + + Upgrade.stdOut(f"Created minimal config: {config_file}", 1) + else: + Upgrade.stdOut(f"LiteSpeed config exists: {config_file}", 1) + + except Exception as e: + Upgrade.stdOut(f"Error fixing LiteSpeed config: {str(e)}", 0) + @staticmethod def installPHP73(): try: - if Upgrade.installedOutput.find('lsphp73') == -1: - command = 'yum install -y lsphp73 lsphp73-json lsphp73-xmlrpc lsphp73-xml lsphp73-tidy lsphp73-soap lsphp73-snmp ' \ - 'lsphp73-recode lsphp73-pspell lsphp73-process lsphp73-pgsql lsphp73-pear lsphp73-pdo lsphp73-opcache ' \ - 'lsphp73-odbc lsphp73-mysqlnd lsphp73-mcrypt lsphp73-mbstring lsphp73-ldap lsphp73-intl lsphp73-imap ' \ - 'lsphp73-gmp lsphp73-gd lsphp73-enchant lsphp73-dba lsphp73-common lsphp73-bcmath' - Upgrade.executioner(command, 'Install PHP 73, 0') - - if Upgrade.installedOutput.find('lsphp74') == -1: - command = 'yum install -y lsphp74 lsphp74-json lsphp74-xmlrpc lsphp74-xml lsphp74-tidy lsphp74-soap lsphp74-snmp ' \ - 'lsphp74-recode lsphp74-pspell lsphp74-process lsphp74-pgsql lsphp74-pear lsphp74-pdo lsphp74-opcache ' \ - 'lsphp74-odbc lsphp74-mysqlnd lsphp74-mcrypt lsphp74-mbstring lsphp74-ldap lsphp74-intl lsphp74-imap ' \ - 'lsphp74-gmp lsphp74-gd lsphp74-enchant lsphp74-dba lsphp74-common lsphp74-bcmath' - - Upgrade.executioner(command, 'Install PHP 74, 0') - - if Upgrade.installedOutput.find('lsphp80') == -1: - command = 'yum install lsphp80* -y' - subprocess.call(command, shell=True) - - if Upgrade.installedOutput.find('lsphp81') == -1: - command = 'yum install lsphp81* -y' - subprocess.call(command, shell=True) - - if Upgrade.installedOutput.find('lsphp82') == -1: - command = 'yum install lsphp82* -y' - subprocess.call(command, shell=True) - - command = 'yum install lsphp83* -y' - subprocess.call(command, shell=True) - - command = 'yum install lsphp84* -y' - subprocess.call(command, shell=True) - - command = 'yum install lsphp85* -y' + Upgrade.stdOut("Installing PHP versions based on OS compatibility...", 1) + + # Get available PHP versions + available_versions = Upgrade.get_available_php_versions() + + if not available_versions: + Upgrade.stdOut("No PHP versions available for installation", 0) + return + + Upgrade.stdOut(f"Installing available PHP versions: {', '.join(available_versions)}", 1) + + for version in available_versions: + try: + if version in ['71', '72', '73', '74']: + # PHP 7.x versions with specific extensions + if Upgrade.installedOutput.find(f'lsphp{version}') == -1: + extensions = ['json', 'xmlrpc', 'xml', 'tidy', 'soap', 'snmp', 'recode', 'pspell', 'process', 'pgsql', 'pear', 'pdo', 'opcache', 'odbc', 'mysqlnd', 'mcrypt', 'mbstring', 'ldap', 'intl', 'imap', 'gmp', 'gd', 'enchant', 'dba', 'common', 'bcmath'] + package_list = f"lsphp{version} " + " ".join([f"lsphp{version}-{ext}" for ext in extensions]) + command = f"yum install -y {package_list}" + Upgrade.executioner(command, f'Install PHP {version}', 0) + else: + # PHP 8.x versions + if Upgrade.installedOutput.find(f'lsphp{version}') == -1: + command = f"yum install lsphp{version}* -y" subprocess.call(command, shell=True) + Upgrade.stdOut(f"Installed PHP {version}", 1) + + except Exception as e: + Upgrade.stdOut(f"Error installing PHP {version}: {str(e)}", 0) + continue except: command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install ' \ @@ -3997,9 +4174,20 @@ pm.max_spare_servers = 3 @staticmethod def setupPHPSymlink(): try: - # Check if PHP 8.3 exists - if not os.path.exists('/usr/local/lsws/lsphp83/bin/php'): - Upgrade.stdOut("PHP 8.3 not found, installing it first...") + # Try to find available PHP version (prioritize modern stable versions) + # Priority: 8.3 (recommended), 8.2, 8.4, 8.5, 8.1, 8.0, then older versions + php_versions = ['83', '82', '84', '85', '81', '80', '74', '73', '72', '71'] + selected_php = None + + for version in php_versions: + if os.path.exists(f'/usr/local/lsws/lsphp{version}/bin/php'): + selected_php = version + Upgrade.stdOut(f"Found PHP {version}, using as default", 1) + break + + if not selected_php: + # Try to install PHP 8.3 as fallback (modern stable version) + Upgrade.stdOut("No PHP found, installing PHP 8.3 as fallback...") # Install PHP 8.3 based on OS if os.path.exists(Upgrade.CentOSPath) or os.path.exists(Upgrade.openEulerPath): @@ -4013,16 +4201,17 @@ pm.max_spare_servers = 3 if not os.path.exists('/usr/local/lsws/lsphp83/bin/php'): Upgrade.stdOut('[ERROR] Failed to install PHP 8.3') return 0 + selected_php = '83' # Remove existing PHP symlink if it exists if os.path.exists('/usr/bin/php'): os.remove('/usr/bin/php') - # Create symlink to PHP 8.3 - command = 'ln -s /usr/local/lsws/lsphp83/bin/php /usr/bin/php' - Upgrade.executioner(command, 'Setup PHP Symlink to 8.3', 0) + # Create symlink to selected PHP version + command = f'ln -s /usr/local/lsws/lsphp{selected_php}/bin/php /usr/bin/php' + Upgrade.executioner(command, f'Setup PHP Symlink to {selected_php}', 0) - Upgrade.stdOut("PHP symlink updated to PHP 8.3 successfully.") + Upgrade.stdOut(f"PHP symlink updated to PHP {selected_php} successfully.") except BaseException as msg: Upgrade.stdOut('[ERROR] ' + str(msg) + " [setupPHPSymlink]") @@ -4150,6 +4339,9 @@ pm.max_spare_servers = 3 Upgrade.manageServiceMigrations() Upgrade.enableServices() + # Apply AlmaLinux 9 fixes before other installations + Upgrade.fix_almalinux9_mariadb() + Upgrade.installPHP73() Upgrade.setupCLI() Upgrade.someDirectories() @@ -4158,6 +4350,9 @@ pm.max_spare_servers = 3 ## Fix Apache configuration issues after upgrade Upgrade.fixApacheConfiguration() + + # Fix LiteSpeed configuration files if missing + Upgrade.fixLiteSpeedConfig() ### General migrations are not needed any more @@ -4191,8 +4386,32 @@ pm.max_spare_servers = 3 except: pass - command = 'cp /usr/local/lsws/lsphp80/bin/lsphp %s' % (phpPath) + # Try to find available PHP binary in order of preference (modern stable first) + php_versions = ['83', '82', '84', '85', '81', '80', '74', '73', '72', '71'] + php_binary_found = False + + for version in php_versions: + php_binary = f'/usr/local/lsws/lsphp{version}/bin/lsphp' + if os.path.exists(php_binary): + command = f'cp {php_binary} {phpPath}' Upgrade.executioner(command, 0) + Upgrade.stdOut(f"Using PHP {version} for LSCPD", 1) + php_binary_found = True + break + + if not php_binary_found: + Upgrade.stdOut("Warning: No PHP binary found for LSCPD", 0) + # Try to create a symlink to any available PHP + try: + command = 'find /usr/local/lsws -name "lsphp" -type f 2>/dev/null | head -1' + result = subprocess.run(command, shell=True, capture_output=True, text=True) + if result.stdout.strip(): + php_binary = result.stdout.strip() + command = f'cp {php_binary} {phpPath}' + Upgrade.executioner(command, 0) + Upgrade.stdOut(f"Using found PHP binary: {php_binary}", 1) + except: + pass if Upgrade.SoftUpgrade == 0: try: @@ -4200,6 +4419,42 @@ pm.max_spare_servers = 3 Upgrade.executioner(command, 'Start LSCPD', 0) except: pass + + # Try to start other services if they exist + # Enhanced service startup with AlmaLinux 9 support + services_to_start = ['fastapi_ssh_server', 'cyberpanel'] + + # Special handling for AlmaLinux 9 MariaDB service + if Upgrade.is_almalinux9(): + Upgrade.stdOut("AlmaLinux 9 detected - applying enhanced service management", 1) + mariadb_services = ['mariadb', 'mysql', 'mysqld'] + for service in mariadb_services: + try: + check_command = f"systemctl list-unit-files | grep -q {service}" + result = subprocess.run(check_command, shell=True, capture_output=True) + if result.returncode == 0: + command = f"systemctl restart {service}" + Upgrade.executioner(command, f'Restart {service} for AlmaLinux 9', 0) + command = f"systemctl enable {service}" + Upgrade.executioner(command, f'Enable {service} for AlmaLinux 9', 0) + Upgrade.stdOut(f"MariaDB service managed as {service} on AlmaLinux 9", 1) + break + except Exception as e: + Upgrade.stdOut(f"Could not manage MariaDB service {service}: {str(e)}", 0) + continue + + for service in services_to_start: + try: + # Check if service exists + check_command = f"systemctl list-unit-files | grep -q {service}" + result = subprocess.run(check_command, shell=True, capture_output=True) + if result.returncode == 0: + command = f"systemctl start {service}" + Upgrade.executioner(command, f'Start {service}', 0) + else: + Upgrade.stdOut(f"Service {service} not found, skipping", 0) + except Exception as e: + Upgrade.stdOut(f"Could not start {service}: {str(e)}", 0) # Remove CSF if installed and restore firewalld (CSF is being discontinued on August 31, 2025) if os.path.exists('/etc/csf'): diff --git a/plogical/upgrade.py.bak b/plogical/upgrade.py.bak deleted file mode 100644 index 869740ef6..000000000 --- a/plogical/upgrade.py.bak +++ /dev/null @@ -1,2696 +0,0 @@ -import os -import os.path -import sys -import argparse -import pwd -import grp - -sys.path.append('/usr/local/CyberCP') -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") -import shlex -import subprocess -import shutil -import time -import MySQLdb as mysql -from CyberCP import settings -import random -import string - -VERSION = '2.3' -BUILD = 1 - -CENTOS7 = 0 -CENTOS8 = 1 -Ubuntu18 = 2 -Ubuntu20 = 3 -CloudLinux7 = 4 -CloudLinux8 = 5 - - -class Upgrade: - logPath = "/usr/local/lscp/logs/upgradeLog" - cdn = 'cdn.cyberpanel.sh' - installedOutput = '' - CentOSPath = '/etc/redhat-release' - UbuntuPath = '/etc/lsb-release' - FromCloud = 0 - - AdminACL = '{"adminStatus":1, "versionManagement": 1, "createNewUser": 1, "listUsers": 1, "deleteUser":1 , "resellerCenter": 1, ' \ - '"changeUserACL": 1, "createWebsite": 1, "modifyWebsite": 1, "suspendWebsite": 1, "deleteWebsite": 1, ' \ - '"createPackage": 1, "listPackages": 1, "deletePackage": 1, "modifyPackage": 1, "createDatabase": 1, "deleteDatabase": 1, ' \ - '"listDatabases": 1, "createNameServer": 1, "createDNSZone": 1, "deleteZone": 1, "addDeleteRecords": 1, ' \ - '"createEmail": 1, "listEmails": 1, "deleteEmail": 1, "emailForwarding": 1, "changeEmailPassword": 1, ' \ - '"dkimManager": 1, "createFTPAccount": 1, "deleteFTPAccount": 1, "listFTPAccounts": 1, "createBackup": 1,' \ - ' "restoreBackup": 1, "addDeleteDestinations": 1, "scheduleBackups": 1, "remoteBackups": 1, "googleDriveBackups": 1, "manageSSL": 1, ' \ - '"hostnameSSL": 1, "mailServerSSL": 1 }' - - ResellerACL = '{"adminStatus":0, "versionManagement": 1, "createNewUser": 1, "listUsers": 1, "deleteUser": 1 , "resellerCenter": 1, ' \ - '"changeUserACL": 0, "createWebsite": 1, "modifyWebsite": 1, "suspendWebsite": 1, "deleteWebsite": 1, ' \ - '"createPackage": 1, "listPackages": 1, "deletePackage": 1, "modifyPackage": 1, "createDatabase": 1, "deleteDatabase": 1, ' \ - '"listDatabases": 1, "createNameServer": 1, "createDNSZone": 1, "deleteZone": 1, "addDeleteRecords": 1, ' \ - '"createEmail": 1, "listEmails": 1, "deleteEmail": 1, "emailForwarding": 1, "changeEmailPassword": 1, ' \ - '"dkimManager": 1, "createFTPAccount": 1, "deleteFTPAccount": 1, "listFTPAccounts": 1, "createBackup": 1,' \ - ' "restoreBackup": 1, "addDeleteDestinations": 0, "scheduleBackups": 0, "remoteBackups": 0, "googleDriveBackups": 1, "manageSSL": 1, ' \ - '"hostnameSSL": 0, "mailServerSSL": 0 }' - - UserACL = '{"adminStatus":0, "versionManagement": 1, "createNewUser": 0, "listUsers": 0, "deleteUser": 0 , "resellerCenter": 0, ' \ - '"changeUserACL": 0, "createWebsite": 0, "modifyWebsite": 0, "suspendWebsite": 0, "deleteWebsite": 0, ' \ - '"createPackage": 0, "listPackages": 0, "deletePackage": 0, "modifyPackage": 0, "createDatabase": 1, "deleteDatabase": 1, ' \ - '"listDatabases": 1, "createNameServer": 0, "createDNSZone": 1, "deleteZone": 1, "addDeleteRecords": 1, ' \ - '"createEmail": 1, "listEmails": 1, "deleteEmail": 1, "emailForwarding": 1, "changeEmailPassword": 1, ' \ - '"dkimManager": 1, "createFTPAccount": 1, "deleteFTPAccount": 1, "listFTPAccounts": 1, "createBackup": 1,' \ - ' "restoreBackup": 0, "addDeleteDestinations": 0, "scheduleBackups": 0, "remoteBackups": 0, "googleDriveBackups": 1, "manageSSL": 1, ' \ - '"hostnameSSL": 0, "mailServerSSL": 0 }' - - @staticmethod - def decideCentosVersion(): - - if open(Upgrade.CentOSPath, 'r').read().find('CentOS Linux release 8') > -1: - return CENTOS8 - else: - return CENTOS7 - - @staticmethod - def FindOperatingSytem(): - - if os.path.exists(Upgrade.CentOSPath): - result = open(Upgrade.CentOSPath, 'r').read() - - if result.find('CentOS Linux release 8') > -1 or result.find('CloudLinux release 8') > -1: - return CENTOS8 - else: - return CENTOS7 - else: - result = open(Upgrade.UbuntuPath, 'r').read() - - if result.find('20.04') > -1: - return Ubuntu20 - else: - return Ubuntu18 - - - @staticmethod - def stdOut(message, do_exit=0): - print("\n\n") - print(("[" + time.strftime( - "%m.%d.%Y_%H-%M-%S") + "] #########################################################################\n")) - print(("[" + time.strftime("%m.%d.%Y_%H-%M-%S") + "] " + message + "\n")) - print(("[" + time.strftime( - "%m.%d.%Y_%H-%M-%S") + "] #########################################################################\n")) - - if do_exit: - if Upgrade.FromCloud == 0: - os._exit(0) - - @staticmethod - def executioner(command, component, do_exit=0): - try: - FNULL = open(os.devnull, 'w') - count = 0 - while True: - res = subprocess.call(shlex.split(command), stderr=subprocess.STDOUT) - if res != 0: - count = count + 1 - Upgrade.stdOut(component + ' failed, trying again, try number: ' + str(count), 0) - if count == 3: - Upgrade.stdOut(component + ' failed.', do_exit) - return False - else: - Upgrade.stdOut(component + ' successful.', 0) - break - return True - except: - return False - - @staticmethod - def updateRepoURL(): - command = "sed -i 's|sgp.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/MariaDB.repo" - Upgrade.executioner(command, command, 0) - - command = "sed -i 's|lax.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/MariaDB.repo" - Upgrade.executioner(command, command, 0) - - command = "sed -i 's|fra.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/MariaDB.repo" - Upgrade.executioner(command, command, 0) - - command = "sed -i 's|mirror.cyberpanel.net|cdn.cyberpanel.sh|g' /etc/yum.repos.d/MariaDB.repo" - Upgrade.executioner(command, command, 0) - - command = "sed -i 's|sgp.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/litespeed.repo" - Upgrade.executioner(command, command, 0) - - command = "sed -i 's|lax.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/litespeed.repo" - Upgrade.executioner(command, command, 0) - - command = "sed -i 's|fra.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/litespeed.repo" - Upgrade.executioner(command, command, 0) - - command = "sed -i 's|mirror.cyberpanel.net|cdn.cyberpanel.sh|g' /etc/yum.repos.d/litespeed.repo" - Upgrade.executioner(command, command, 0) - - @staticmethod - def mountTemp(): - try: - - if os.path.exists("/usr/.tempdisk"): - return 0 - - command = "dd if=/dev/zero of=/usr/.tempdisk bs=100M count=15" - Upgrade.executioner(command, 'mountTemp', 0) - - command = "mkfs.ext4 -F /usr/.tempdisk" - Upgrade.executioner(command, 'mountTemp', 0) - - command = "mkdir -p /usr/.tmpbak/" - Upgrade.executioner(command, 'mountTemp', 0) - - command = "cp -pr /tmp/* /usr/.tmpbak/" - subprocess.call(command, shell=True) - - command = "mount -o loop,rw,nodev,nosuid,noexec,nofail /usr/.tempdisk /tmp" - Upgrade.executioner(command, 'mountTemp', 0) - - command = "chmod 1777 /tmp" - Upgrade.executioner(command, 'mountTemp', 0) - - command = "cp -pr /usr/.tmpbak/* /tmp/" - subprocess.call(command, shell=True) - - command = "rm -rf /usr/.tmpbak" - Upgrade.executioner(command, 'mountTemp', 0) - - command = "mount --bind /tmp /var/tmp" - Upgrade.executioner(command, 'mountTemp', 0) - - tmp = "/usr/.tempdisk /tmp ext4 loop,rw,noexec,nosuid,nodev,nofail 0 0\n" - varTmp = "/tmp /var/tmp none bind 0 0\n" - - fstab = "/etc/fstab" - writeToFile = open(fstab, "a") - writeToFile.writelines(tmp) - writeToFile.writelines(varTmp) - writeToFile.close() - - except BaseException as msg: - Upgrade.stdOut(str(msg) + " [mountTemp]", 0) - - @staticmethod - def dockerUsers(): - ### Docker User/group - try: - pwd.getpwnam('docker') - except KeyError: - command = "adduser docker" - Upgrade.executioner(command, 'adduser docker', 0) - - try: - grp.getgrnam('docker') - except KeyError: - command = 'groupadd docker' - Upgrade.executioner(command, 'adduser docker', 0) - - command = 'usermod -aG docker docker' - Upgrade.executioner(command, 'adduser docker', 0) - - command = 'usermod -aG docker cyberpanel' - Upgrade.executioner(command, 'adduser docker', 0) - - ### - - @staticmethod - def fixSudoers(): - try: - distroPath = '/etc/lsb-release' - - if os.path.exists(distroPath): - fileName = '/etc/sudoers' - data = open(fileName, 'r').readlines() - - writeDataToFile = open(fileName, 'w') - for line in data: - if line.find("%sudo ALL=(ALL:ALL)") > -1: - continue - else: - writeDataToFile.write(line) - writeDataToFile.close() - - else: - try: - path = "/etc/sudoers" - - data = open(path, 'r').readlines() - - writeToFile = open(path, 'w') - - for items in data: - if items.find("wheel") > -1 and items.find("ALL=(ALL)"): - continue - else: - writeToFile.writelines(items) - - writeToFile.close() - except: - pass - - command = "chsh -s /bin/false cyberpanel" - Upgrade.executioner(command, 0) - except IOError as err: - pass - - @staticmethod - def download_install_phpmyadmin(): - try: - cwd = os.getcwd() - - if not os.path.exists("/usr/local/CyberCP/public"): - os.mkdir("/usr/local/CyberCP/public") - - try: - shutil.rmtree("/usr/local/CyberCP/public/phpmyadmin") - except: - pass - - command = 'wget -O /usr/local/CyberCP/public/phpmyadmin.zip https://github.com/usmannasir/cyberpanel/raw/stable/phpmyadmin.zip' - Upgrade.executioner(command, 0) - - command = 'unzip /usr/local/CyberCP/public/phpmyadmin.zip -d /usr/local/CyberCP/public/' - Upgrade.executioner(command, 0) - - command = 'mv /usr/local/CyberCP/public/phpMyAdmin-*-all-languages /usr/local/CyberCP/public/phpmyadmin' - subprocess.call(command, shell=True) - - command = 'rm -f /usr/local/CyberCP/public/phpmyadmin.zip' - Upgrade.executioner(command, 0) - - ## Write secret phrase - - rString = ''.join([random.choice(string.ascii_letters + string.digits) for n in range(32)]) - - data = open('/usr/local/CyberCP/public/phpmyadmin/config.sample.inc.php', 'r').readlines() - - writeToFile = open('/usr/local/CyberCP/public/phpmyadmin/config.inc.php', 'w') - - writeE = 1 - - phpMyAdminContent = """ -$cfg['Servers'][$i]['AllowNoPassword'] = false; -$cfg['Servers'][$i]['auth_type'] = 'signon'; -$cfg['Servers'][$i]['SignonSession'] = 'SignonSession'; -$cfg['Servers'][$i]['SignonURL'] = 'phpmyadminsignin.php'; -$cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout'; -""" - - for items in data: - if items.find('blowfish_secret') > -1: - writeToFile.writelines( - "$cfg['blowfish_secret'] = '" + rString + "'; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */\n") - elif items.find('/* Authentication type */') > -1: - writeToFile.writelines(items) - writeToFile.write(phpMyAdminContent) - writeE = 0 - elif items.find("$cfg['Servers'][$i]['AllowNoPassword']") > -1: - writeE = 1 - else: - if writeE: - writeToFile.writelines(items) - - writeToFile.writelines("$cfg['TempDir'] = '/usr/local/CyberCP/public/phpmyadmin/tmp';\n") - - writeToFile.close() - - os.mkdir('/usr/local/CyberCP/public/phpmyadmin/tmp') - - command = 'cp /usr/local/CyberCP/plogical/phpmyadminsignin.php /usr/local/CyberCP/public/phpmyadmin/phpmyadminsignin.php' - Upgrade.executioner(command, 0) - - passFile = "/etc/cyberpanel/mysqlPassword" - - try: - import json - jsonData = json.loads(open(passFile, 'r').read()) - - mysqluser = jsonData['mysqluser'] - mysqlpassword = jsonData['mysqlpassword'] - mysqlport = jsonData['mysqlport'] - mysqlhost = jsonData['mysqlhost'] - - command = "sed -i 's|localhost|%s|g' /usr/local/CyberCP/public/phpmyadmin/phpmyadminsignin.php" % ( - mysqlhost) - Upgrade.executioner(command, 0) - - except: - pass - - os.chdir(cwd) - - except BaseException as msg: - Upgrade.stdOut(str(msg) + " [download_install_phpmyadmin]", 0) - - @staticmethod - def setupComposer(): - - if os.path.exists('composer.sh'): - os.remove('composer.sh') - - command = "wget https://cyberpanel.sh/composer.sh" - Upgrade.executioner(command, 0) - - command = "chmod +x composer.sh" - Upgrade.executioner(command, 0) - - command = "./composer.sh" - Upgrade.executioner(command, 0) - - @staticmethod - def downoad_and_install_raindloop(): - try: - ####### - - if os.path.exists("/usr/local/CyberCP/public/snappymail"): - - if os.path.exists("/usr/local/lscp/cyberpanel/snappymail/data"): - pass - else: - command = "mv /usr/local/CyberCP/public/snappymail/data /usr/local/lscp/cyberpanel/snappymail/data" - Upgrade.executioner(command, 0) - - command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/snappymail/data" - Upgrade.executioner(command, 0) - - iPath = os.listdir('/usr/local/CyberCP/public/snappymail/snappymail/v/') - - path = "/usr/local/CyberCP/public/snappymail/snappymail/v/%s/include.php" % (iPath[0]) - - data = open(path, 'r').readlines() - writeToFile = open(path, 'w') - - for items in data: - if items.find("$sCustomDataPath = '';") > -1: - writeToFile.writelines( - " $sCustomDataPath = '/usr/local/lscp/cyberpanel/snappymail/data';\n") - else: - writeToFile.writelines(items) - - writeToFile.close() - return 0 - - cwd = os.getcwd() - - if not os.path.exists("/usr/local/CyberCP/public"): - os.mkdir("/usr/local/CyberCP/public") - - os.chdir("/usr/local/CyberCP/public") - - count = 1 - - while (1): - command = 'wget https://www.snappymail.net/repository/webmail/snappymail-community-latest.zip' - cmd = shlex.split(command) - res = subprocess.call(cmd) - if res != 0: - count = count + 1 - if count == 3: - break - else: - break - - ############# - - count = 0 - - while (1): - command = 'unzip snappymail-community-latest.zip -d /usr/local/CyberCP/public/snappymail' - - cmd = shlex.split(command) - res = subprocess.call(cmd) - if res != 0: - count = count + 1 - if count == 3: - break - else: - break - - os.remove("snappymail-community-latest.zip") - - ####### - - os.chdir("/usr/local/CyberCP/public/snappymail") - - count = 0 - - while (1): - command = 'find . -type d -exec chmod 755 {} \;' - cmd = shlex.split(command) - res = subprocess.call(cmd) - if res != 0: - count = count + 1 - if count == 3: - break - else: - break - - ############# - - count = 0 - - while (1): - command = 'find . -type f -exec chmod 644 {} \;' - cmd = shlex.split(command) - res = subprocess.call(cmd) - if res != 0: - count = count + 1 - if count == 3: - break - else: - break - ###### - - iPath = os.listdir('/usr/local/CyberCP/public/snappymail/snappymail/v/') - - path = "/usr/local/CyberCP/public/snappymail/snappymail/v/%s/include.php" % (iPath[0]) - - data = open(path, 'r').readlines() - writeToFile = open(path, 'w') - - for items in data: - if items.find("$sCustomDataPath = '';") > -1: - writeToFile.writelines( - " $sCustomDataPath = '/usr/local/lscp/cyberpanel/snappymail/data';\n") - else: - writeToFile.writelines(items) - - writeToFile.close() - - command = "mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/configs/" - Upgrade.executioner(command, 'mkdir snappymail configs', 0) - - labsPath = '/usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/configs/application.ini' - - labsData = """[labs] -imap_folder_list_limit = 0 -""" - - writeToFile = open(labsPath, 'w') - writeToFile.write(labsData) - writeToFile.close() - - os.chdir(cwd) - - except BaseException as msg: - Upgrade.stdOut(str(msg) + " [downoad_and_install_raindloop]", 0) - - return 1 - - @staticmethod - def downloadLink(): - try: - version_number = VERSION - version_build = str(BUILD) - - try: - path = "/usr/local/CyberCP/version.txt" - writeToFile = open(path, 'w') - writeToFile.writelines(version_number + '\n') - writeToFile.writelines(version_build) - writeToFile.close() - except: - pass - - return (version_number + "." + version_build + ".tar.gz") - except BaseException as msg: - Upgrade.stdOut(str(msg) + ' [downloadLink]') - os._exit(0) - - @staticmethod - def setupCLI(): - try: - - command = "ln -s /usr/local/CyberCP/cli/cyberPanel.py /usr/bin/cyberpanel" - Upgrade.executioner(command, 'CLI Symlink', 0) - - command = "chmod +x /usr/local/CyberCP/cli/cyberPanel.py" - Upgrade.executioner(command, 'CLI Permissions', 0) - - except OSError as msg: - Upgrade.stdOut(str(msg) + " [setupCLI]") - return 0 - - @staticmethod - def staticContent(): - - command = "rm -rf /usr/local/CyberCP/public/static" - Upgrade.executioner(command, 'Remove old static content', 0) - - ## - - if not os.path.exists("/usr/local/CyberCP/public"): - os.mkdir("/usr/local/CyberCP/public") - - shutil.move("/usr/local/CyberCP/static", "/usr/local/CyberCP/public/") - - @staticmethod - def upgradeVersion(): - try: - - import django - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") - django.setup() - from baseTemplate.models import version - - vers = version.objects.get(pk=1) - vers.currentVersion = VERSION - vers.build = str(BUILD) - vers.save() - except: - pass - - @staticmethod - def setupConnection(db=None): - try: - passFile = "/etc/cyberpanel/mysqlPassword" - - f = open(passFile) - data = f.read() - password = data.split('\n', 1)[0] - - if db == None: - conn = mysql.connect(user='root', passwd=password) - else: - try: - conn = mysql.connect(db=db, user='root', passwd=password) - except: - try: - conn = mysql.connect(host='127.0.0.1', port=3307, db=db, user='root', passwd=password) - except: - dbUser = settings.DATABASES['default']['USER'] - password = settings.DATABASES['default']['PASSWORD'] - host = settings.DATABASES['default']['HOST'] - port = settings.DATABASES['default']['PORT'] - - if port == '': - conn = mysql.connect(host=host, port=3306, db=db, user=dbUser, passwd=password) - else: - conn = mysql.connect(host=host, port=int(port), db=db, user=dbUser, passwd=password) - - cursor = conn.cursor() - return conn, cursor - - except BaseException as msg: - Upgrade.stdOut(str(msg)) - return 0, 0 - - @staticmethod - def applyLoginSystemMigrations(): - try: - - connection, cursor = Upgrade.setupConnection('cyberpanel') - - try: - cursor.execute('CREATE TABLE `baseTemplate_cyberpanelcosmetic` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `MainDashboardCSS` longtext NOT NULL)') - except: - pass - - try: - cursor.execute( - 'CREATE TABLE `loginSystem_acl` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(50) NOT NULL UNIQUE, `adminStatus` integer NOT NULL DEFAULT 0, `versionManagement` integer NOT NULL DEFAULT 0, `createNewUser` integer NOT NULL DEFAULT 0, `deleteUser` integer NOT NULL DEFAULT 0, `resellerCenter` integer NOT NULL DEFAULT 0, `changeUserACL` integer NOT NULL DEFAULT 0, `createWebsite` integer NOT NULL DEFAULT 0, `modifyWebsite` integer NOT NULL DEFAULT 0, `suspendWebsite` integer NOT NULL DEFAULT 0, `deleteWebsite` integer NOT NULL DEFAULT 0, `createPackage` integer NOT NULL DEFAULT 0, `deletePackage` integer NOT NULL DEFAULT 0, `modifyPackage` integer NOT NULL DEFAULT 0, `createDatabase` integer NOT NULL DEFAULT 0, `deleteDatabase` integer NOT NULL DEFAULT 0, `listDatabases` integer NOT NULL DEFAULT 0, `createNameServer` integer NOT NULL DEFAULT 0, `createDNSZone` integer NOT NULL DEFAULT 0, `deleteZone` integer NOT NULL DEFAULT 0, `addDeleteRecords` integer NOT NULL DEFAULT 0, `createEmail` integer NOT NULL DEFAULT 0, `deleteEmail` integer NOT NULL DEFAULT 0, `emailForwarding` integer NOT NULL DEFAULT 0, `changeEmailPassword` integer NOT NULL DEFAULT 0, `dkimManager` integer NOT NULL DEFAULT 0, `createFTPAccount` integer NOT NULL DEFAULT 0, `deleteFTPAccount` integer NOT NULL DEFAULT 0, `listFTPAccounts` integer NOT NULL DEFAULT 0, `createBackup` integer NOT NULL DEFAULT 0, `restoreBackup` integer NOT NULL DEFAULT 0, `addDeleteDestinations` integer NOT NULL DEFAULT 0, `scheduleBackups` integer NOT NULL DEFAULT 0, `remoteBackups` integer NOT NULL DEFAULT 0, `manageSSL` integer NOT NULL DEFAULT 0, `hostnameSSL` integer NOT NULL DEFAULT 0, `mailServerSSL` integer NOT NULL DEFAULT 0)') - except: - pass - try: - cursor.execute('ALTER TABLE loginSystem_administrator ADD token varchar(500)') - except: - pass - - try: - cursor.execute("ALTER TABLE loginSystem_administrator ADD secretKey varchar(50) DEFAULT 'None'") - except: - pass - - try: - cursor.execute('alter table databases_databases drop index dbUser;') - except: - pass - - try: - cursor.execute("ALTER TABLE loginSystem_administrator ADD state varchar(15) DEFAULT 'ACTIVE'") - except: - pass - - try: - cursor.execute('ALTER TABLE loginSystem_administrator ADD securityLevel integer DEFAULT 1') - except: - pass - - try: - cursor.execute('ALTER TABLE loginSystem_administrator ADD twoFA integer DEFAULT 0') - except: - pass - - try: - cursor.execute('ALTER TABLE loginSystem_administrator ADD api integer') - except: - pass - - try: - cursor.execute('ALTER TABLE loginSystem_administrator ADD acl_id integer') - except: - pass - try: - cursor.execute( - 'ALTER TABLE loginSystem_administrator ADD FOREIGN KEY (acl_id) REFERENCES loginSystem_acl(id)') - except: - pass - - try: - cursor.execute("insert into loginSystem_acl (id, name, adminStatus) values (1,'admin',1)") - except: - pass - - try: - cursor.execute( - "insert into loginSystem_acl (id, name, adminStatus, createNewUser, deleteUser, createWebsite, resellerCenter, modifyWebsite, suspendWebsite, deleteWebsite, createPackage, deletePackage, modifyPackage, createNameServer, restoreBackup) values (2,'reseller',0,1,1,1,1,1,1,1,1,1,1,1,1)") - except: - pass - try: - cursor.execute( - "insert into loginSystem_acl (id, name, createDatabase, deleteDatabase, listDatabases, createDNSZone, deleteZone, addDeleteRecords, createEmail, deleteEmail, emailForwarding, changeEmailPassword, dkimManager, createFTPAccount, deleteFTPAccount, listFTPAccounts, createBackup, manageSSL) values (3,'user', 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)") - except: - pass - - try: - cursor.execute("UPDATE loginSystem_administrator SET acl_id = 1 where userName = 'admin'") - except: - pass - - try: - cursor.execute('ALTER TABLE loginSystem_acl ADD config longtext') - except: - pass - - try: - cursor.execute("UPDATE loginSystem_acl SET config = '%s' where name = 'admin'" % (Upgrade.AdminACL)) - except BaseException as msg: - print(str(msg)) - import sleep - sleep(10) - - try: - cursor.execute("UPDATE loginSystem_acl SET config = '%s' where name = 'reseller'" % (Upgrade.ResellerACL)) - except: - pass - - try: - cursor.execute("UPDATE loginSystem_acl SET config = '%s' where name = 'user'" % (Upgrade.UserACL)) - except: - pass - - try: - cursor.execute("alter table loginSystem_administrator drop initUserAccountsLimit") - except: - pass - - try: - cursor.execute( - "CREATE TABLE `websiteFunctions_aliasdomains` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `aliasDomain` varchar(75) NOT NULL)") - except: - pass - try: - cursor.execute("ALTER TABLE `websiteFunctions_aliasdomains` ADD COLUMN `master_id` integer NOT NULL") - except: - pass - try: - cursor.execute( - "ALTER TABLE `websiteFunctions_aliasdomains` ADD CONSTRAINT `websiteFunctions_ali_master_id_726c433d_fk_websiteFu` FOREIGN KEY (`master_id`) REFERENCES `websiteFunctions_websites` (`id`)") - except: - pass - - try: - cursor.execute('ALTER TABLE websiteFunctions_websites ADD config longtext') - except: - pass - - try: - cursor.execute("ALTER TABLE websiteFunctions_websites MODIFY externalApp varchar(30)") - except: - pass - - try: - cursor.execute("ALTER TABLE emailMarketing_smtphosts MODIFY userName varchar(200)") - except: - pass - - try: - cursor.execute("ALTER TABLE emailMarketing_smtphosts MODIFY password varchar(200)") - except: - pass - - try: - cursor.execute("ALTER TABLE websiteFunctions_backups MODIFY fileName varchar(200)") - except: - pass - - try: - cursor.execute("ALTER TABLE loginSystem_acl ADD COLUMN listUsers INT DEFAULT 0;") - except: - pass - - try: - cursor.execute("ALTER TABLE loginSystem_acl ADD COLUMN listEmails INT DEFAULT 1;") - except: - pass - - try: - cursor.execute("ALTER TABLE loginSystem_acl ADD COLUMN listPackages INT DEFAULT 0;") - except: - pass - - query = """CREATE TABLE `websiteFunctions_normalbackupdests` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(25) NOT NULL, - `config` longtext NOT NULL, - PRIMARY KEY (`id`) -)""" - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `cloudAPI_wpdeployments` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `config` longtext NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `cloudAPI_wpdeploymen_owner_id_506ddf01_fk_websiteFu` (`owner_id`), - CONSTRAINT `cloudAPI_wpdeploymen_owner_id_506ddf01_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_websites` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `websiteFunctions_normalbackupjobs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(25) NOT NULL, - `config` longtext NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `websiteFunctions_nor_owner_id_3a7a13db_fk_websiteFu` (`owner_id`), - CONSTRAINT `websiteFunctions_nor_owner_id_3a7a13db_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_normalbackupdests` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `websiteFunctions_normalbackupsites` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `domain_id` int(11) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `websiteFunctions_nor_domain_id_c03362bc_fk_websiteFu` (`domain_id`), - KEY `websiteFunctions_nor_owner_id_c6ece6cc_fk_websiteFu` (`owner_id`), - CONSTRAINT `websiteFunctions_nor_domain_id_c03362bc_fk_websiteFu` FOREIGN KEY (`domain_id`) REFERENCES `websiteFunctions_websites` (`id`), - CONSTRAINT `websiteFunctions_nor_owner_id_c6ece6cc_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_normalbackupjobs` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `websiteFunctions_normalbackupjoblogs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `status` int(11) NOT NULL, - `message` longtext NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `websiteFunctions_nor_owner_id_69403e73_fk_websiteFu` (`owner_id`), - CONSTRAINT `websiteFunctions_nor_owner_id_69403e73_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_normalbackupjobs` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - try: - connection.close() - except: - pass - - except OSError as msg: - Upgrade.stdOut(str(msg) + " [applyLoginSystemMigrations]") - - @staticmethod - def s3BackupMigrations(): - try: - - connection, cursor = Upgrade.setupConnection('cyberpanel') - - query = """CREATE TABLE `s3Backups_backupplan` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL, - `bucket` varchar(50) NOT NULL, - `freq` varchar(50) NOT NULL, - `retention` int(11) NOT NULL, - `type` varchar(5) NOT NULL, - `lastRun` varchar(50) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`), - KEY `s3Backups_backupplan_owner_id_7d058ced_fk_loginSyst` (`owner_id`), - CONSTRAINT `s3Backups_backupplan_owner_id_7d058ced_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - try: - cursor.execute('ALTER TABLE s3Backups_backupplan ADD config longtext') - except: - pass - - query = """CREATE TABLE `s3Backups_websitesinplan` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `domain` varchar(100) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `s3Backups_websitesin_owner_id_0e9a4fe3_fk_s3Backups` (`owner_id`), - CONSTRAINT `s3Backups_websitesin_owner_id_0e9a4fe3_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplan` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `s3Backups_backuplogs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `timeStamp` varchar(200) NOT NULL, - `level` varchar(5) NOT NULL, - `msg` varchar(500) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `s3Backups_backuplogs_owner_id_7b4653af_fk_s3Backups` (`owner_id`), - CONSTRAINT `s3Backups_backuplogs_owner_id_7b4653af_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplan` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `s3Backups_backupplando` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL, - `bucket` varchar(50) NOT NULL, - `freq` varchar(50) NOT NULL, - `retention` int(11) NOT NULL, - `type` varchar(5) NOT NULL, - `region` varchar(5) NOT NULL, - `lastRun` varchar(50) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`), - KEY `s3Backups_backupplan_owner_id_1a3ec86d_fk_loginSyst` (`owner_id`), - CONSTRAINT `s3Backups_backupplan_owner_id_1a3ec86d_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `s3Backups_websitesinplando` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `domain` varchar(100) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `s3Backups_websitesin_owner_id_cef3ea04_fk_s3Backups` (`owner_id`), - CONSTRAINT `s3Backups_websitesin_owner_id_cef3ea04_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplando` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `s3Backups_backuplogsdo` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `timeStamp` varchar(200) NOT NULL, - `level` varchar(5) NOT NULL, - `msg` varchar(500) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `s3Backups_backuplogs_owner_id_c7cb5872_fk_s3Backups` (`owner_id`), - CONSTRAINT `s3Backups_backuplogs_owner_id_c7cb5872_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplando` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - ## - - query = """CREATE TABLE `s3Backups_minionodes` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `endPointURL` varchar(200) NOT NULL, - `accessKey` varchar(200) NOT NULL, - `secretKey` varchar(200) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `endPointURL` (`endPointURL`), - UNIQUE KEY `accessKey` (`accessKey`), - KEY `s3Backups_minionodes_owner_id_e50993d9_fk_loginSyst` (`owner_id`), - CONSTRAINT `s3Backups_minionodes_owner_id_e50993d9_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `s3Backups_backupplanminio` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL, - `freq` varchar(50) NOT NULL, - `retention` int(11) NOT NULL, - `lastRun` varchar(50) NOT NULL, - `minioNode_id` int(11) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`), - KEY `s3Backups_backupplan_minioNode_id_a4eaf917_fk_s3Backups` (`minioNode_id`), - KEY `s3Backups_backupplan_owner_id_d6830e67_fk_loginSyst` (`owner_id`), - CONSTRAINT `s3Backups_backupplan_minioNode_id_a4eaf917_fk_s3Backups` FOREIGN KEY (`minioNode_id`) REFERENCES `s3Backups_minionodes` (`id`), - CONSTRAINT `s3Backups_backupplan_owner_id_d6830e67_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `s3Backups_websitesinplanminio` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `domain` varchar(100) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `s3Backups_websitesin_owner_id_224ce049_fk_s3Backups` (`owner_id`), - CONSTRAINT `s3Backups_websitesin_owner_id_224ce049_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplanminio` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `s3Backups_backuplogsminio` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `timeStamp` varchar(200) NOT NULL, - `level` varchar(5) NOT NULL, - `msg` varchar(500) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `s3Backups_backuplogs_owner_id_f19e1736_fk_s3Backups` (`owner_id`), - CONSTRAINT `s3Backups_backuplogs_owner_id_f19e1736_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplanminio` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - try: - connection.close() - except: - pass - - except OSError as msg: - Upgrade.stdOut(str(msg) + " [applyLoginSystemMigrations]") - - @staticmethod - def mailServerMigrations(): - try: - connection, cursor = Upgrade.setupConnection('cyberpanel') - - try: - cursor.execute( - 'ALTER TABLE `e_domains` ADD COLUMN `childOwner_id` integer') - except: - pass - - try: - cursor.execute( - 'ALTER TABLE e_users ADD mail varchar(200)') - except: - pass - - try: - cursor.execute( - 'ALTER TABLE e_users MODIFY password varchar(200)') - except: - pass - - try: - cursor.execute( - 'ALTER TABLE e_forwardings DROP PRIMARY KEY;ALTER TABLE e_forwardings ADD id INT AUTO_INCREMENT PRIMARY KEY') - except: - pass - - query = """CREATE TABLE `emailPremium_domainlimits` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `limitStatus` int(11) NOT NULL, - `monthlyLimit` int(11) NOT NULL, - `monthlyUsed` int(11) NOT NULL, - `domain_id` varchar(50) NOT NULL, - PRIMARY KEY (`id`), - KEY `emailPremium_domainlimits_domain_id_303ab297_fk_e_domains_domain` (`domain_id`), - CONSTRAINT `emailPremium_domainlimits_domain_id_303ab297_fk_e_domains_domain` FOREIGN KEY (`domain_id`) REFERENCES `e_domains` (`domain`) -)""" - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `emailPremium_emaillimits` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `limitStatus` int(11) NOT NULL, - `monthlyLimits` int(11) NOT NULL, - `monthlyUsed` int(11) NOT NULL, - `hourlyLimit` int(11) NOT NULL, - `hourlyUsed` int(11) NOT NULL, - `emailLogs` int(11) NOT NULL, - `email_id` varchar(80) NOT NULL, - PRIMARY KEY (`id`), - KEY `emailPremium_emaillimits_email_id_1c111df5_fk_e_users_email` (`email_id`), - CONSTRAINT `emailPremium_emaillimits_email_id_1c111df5_fk_e_users_email` FOREIGN KEY (`email_id`) REFERENCES `e_users` (`email`) -)""" - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `emailPremium_emaillogs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `destination` varchar(200) NOT NULL, - `timeStamp` varchar(200) NOT NULL, - `email_id` varchar(80) NOT NULL, - PRIMARY KEY (`id`), - KEY `emailPremium_emaillogs_email_id_9ef49552_fk_e_users_email` (`email_id`), - CONSTRAINT `emailPremium_emaillogs_email_id_9ef49552_fk_e_users_email` FOREIGN KEY (`email_id`) REFERENCES `e_users` (`email`) -)""" - try: - cursor.execute(query) - except: - pass - - try: - connection.close() - except: - pass - except: - pass - - @staticmethod - def emailMarketingMigrationsa(): - try: - connection, cursor = Upgrade.setupConnection('cyberpanel') - - query = """CREATE TABLE `emailMarketing_emailmarketing` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `userName` varchar(50) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `userName` (`userName`) -)""" - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `emailMarketing_emaillists` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `listName` varchar(50) NOT NULL, - `dateCreated` varchar(200) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `listName` (`listName`), - KEY `emailMarketing_email_owner_id_bf1b4530_fk_websiteFu` (`owner_id`), - CONSTRAINT `emailMarketing_email_owner_id_bf1b4530_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_websites` (`id`) -)""" - try: - cursor.execute(query) - except: - pass - - query = 'ALTER TABLE emailMarketing_emaillists ADD COLUMN verified INT DEFAULT 0' - - try: - cursor.execute(query) - except: - pass - - query = 'ALTER TABLE emailMarketing_emaillists ADD COLUMN notVerified INT DEFAULT 0' - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `emailMarketing_emailsinlist` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `email` varchar(50) NOT NULL, - `firstName` varchar(20) NOT NULL, - `lastName` varchar(20) NOT NULL, - `verificationStatus` varchar(100) NOT NULL, - `dateCreated` varchar(200) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `emailMarketing_email_owner_id_c5c27005_fk_emailMark` (`owner_id`), - CONSTRAINT `emailMarketing_email_owner_id_c5c27005_fk_emailMark` FOREIGN KEY (`owner_id`) REFERENCES `emailMarketing_emaillists` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `emailMarketing_smtphosts` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `host` varchar(150) NOT NULL, - `port` varchar(10) NOT NULL, - `userName` varchar(50) NOT NULL, - `password` varchar(50) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `host` (`host`), - KEY `emailMarketing_smtph_owner_id_8b2d4ac7_fk_loginSyst` (`owner_id`), - CONSTRAINT `emailMarketing_smtph_owner_id_8b2d4ac7_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `emailMarketing_emailtemplate` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(100) NOT NULL, - `subject` varchar(1000) NOT NULL, - `fromName` varchar(100) NOT NULL, - `fromEmail` varchar(150) NOT NULL, - `replyTo` varchar(150) NOT NULL, - `emailMessage` varchar(30000) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`), - KEY `emailMarketing_email_owner_id_d27e1d00_fk_loginSyst` (`owner_id`), - CONSTRAINT `emailMarketing_email_owner_id_d27e1d00_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `emailMarketing_emailjobs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `date` varchar(200) NOT NULL, - `host` varchar(1000) NOT NULL, - `totalEmails` int(11) NOT NULL, - `sent` int(11) NOT NULL, - `failed` int(11) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `emailMarketing_email_owner_id_73ee4827_fk_emailMark` (`owner_id`), - CONSTRAINT `emailMarketing_email_owner_id_73ee4827_fk_emailMark` FOREIGN KEY (`owner_id`) REFERENCES `emailMarketing_emailtemplate` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `mailServer_pipeprograms` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `source` varchar(80) NOT NULL, - `destination` longtext NOT NULL, - PRIMARY KEY (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `emailMarketing_validationlog` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `status` int(11) NOT NULL, - `message` longtext NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `emailMarketing_valid_owner_id_240ad36e_fk_emailMark` (`owner_id`), - CONSTRAINT `emailMarketing_valid_owner_id_240ad36e_fk_emailMark` FOREIGN KEY (`owner_id`) REFERENCES `emailMarketing_emaillists` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - try: - connection.close() - except: - pass - except: - pass - - @staticmethod - def dockerMigrations(): - try: - connection, cursor = Upgrade.setupConnection('cyberpanel') - - query = """CREATE TABLE `dockerManager_containers` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL, - `cid` varchar(64) NOT NULL, - `image` varchar(50) NOT NULL, - `tag` varchar(50) NOT NULL, - `memory` int(11) NOT NULL, - `ports` longtext NOT NULL, - `env` longtext NOT NULL, - `startOnReboot` int(11) NOT NULL, - `admin_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`), - KEY `dockerManager_contai_admin_id_58fb62b7_fk_loginSyst` (`admin_id`), - CONSTRAINT `dockerManager_contai_admin_id_58fb62b7_fk_loginSyst` FOREIGN KEY (`admin_id`) REFERENCES `loginSystem_administrator` (`id`) -)""" - try: - cursor.execute(query) - except: - pass - - try: - cursor.execute('ALTER TABLE loginSystem_administrator ADD config longtext') - except: - pass - - try: - cursor.execute('ALTER TABLE loginSystem_acl ADD config longtext') - except: - pass - - try: - cursor.execute('ALTER TABLE dockerManager_containers ADD volumes longtext') - except: - pass - - try: - connection.close() - except: - pass - except: - pass - - @staticmethod - def containerMigrations(): - try: - connection, cursor = Upgrade.setupConnection('cyberpanel') - - query = """CREATE TABLE `containerization_containerlimits` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `cpuPers` varchar(10) NOT NULL, - `IO` varchar(10) NOT NULL, - `IOPS` varchar(10) NOT NULL, - `memory` varchar(10) NOT NULL, - `networkSpeed` varchar(10) NOT NULL, - `networkHexValue` varchar(10) NOT NULL, - `enforce` int(11) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `containerization_con_owner_id_494eb637_fk_websiteFu` (`owner_id`), - CONSTRAINT `containerization_con_owner_id_494eb637_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_websites` (`id`) -)""" - try: - cursor.execute(query) - except: - pass - - try: - connection.close() - except: - pass - except: - pass - - @staticmethod - def CLMigrations(): - try: - connection, cursor = Upgrade.setupConnection('cyberpanel') - - query = """CREATE TABLE `CLManager_clpackages` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL, - `speed` varchar(50) NOT NULL, - `vmem` varchar(50) NOT NULL, - `pmem` varchar(50) NOT NULL, - `io` varchar(50) NOT NULL, - `iops` varchar(50) NOT NULL, - `ep` varchar(50) NOT NULL, - `nproc` varchar(50) NOT NULL, - `inodessoft` varchar(50) NOT NULL, - `inodeshard` varchar(50) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`), - KEY `CLManager_clpackages_owner_id_9898c1e8_fk_packages_package_id` (`owner_id`), - CONSTRAINT `CLManager_clpackages_owner_id_9898c1e8_fk_packages_package_id` FOREIGN KEY (`owner_id`) REFERENCES `packages_package` (`id`) -)""" - try: - cursor.execute(query) - except: - pass - - query = "ALTER TABLE packages_package ADD COLUMN allowFullDomain INT DEFAULT 1;" - try: - cursor.execute(query) - except: - pass - - query = "ALTER TABLE packages_package ADD COLUMN enforceDiskLimits INT DEFAULT 0;" - try: - cursor.execute(query) - except: - pass - - try: - connection.close() - except: - pass - except: - pass - - @staticmethod - def manageServiceMigrations(): - try: - connection, cursor = Upgrade.setupConnection('cyberpanel') - - query = """CREATE TABLE `manageServices_pdnsstatus` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `serverStatus` int(11) NOT NULL, - `type` varchar(6) NOT NULL, - PRIMARY KEY (`id`) -)""" - try: - cursor.execute(query) - except: - pass - - try: - cursor.execute('alter table manageServices_pdnsstatus add masterServer varchar(200)') - except: - pass - - try: - cursor.execute('alter table manageServices_pdnsstatus add masterIP varchar(200)') - except: - pass - - try: - cursor.execute('ALTER TABLE `manageServices_pdnsstatus` CHANGE `type` `type` VARCHAR(10) NULL;') - except: - pass - - query = '''CREATE TABLE `databases_dbmeta` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `key` varchar(200) NOT NULL, - `value` longtext NOT NULL, - `database_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `databases_dbmeta_database_id_777997bc_fk_databases_databases_id` (`database_id`), - CONSTRAINT `databases_dbmeta_database_id_777997bc_fk_databases_databases_id` FOREIGN KEY (`database_id`) REFERENCES `databases_databases` (`id`) -)''' - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `filemanager_trash` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `originalPath` varchar(500) NOT NULL, - `fileName` varchar(200) NOT NULL, - `website_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `filemanager_trash_website_id_e2762f3c_fk_websiteFu` (`website_id`), - CONSTRAINT `filemanager_trash_website_id_e2762f3c_fk_websiteFu` FOREIGN KEY (`website_id`) REFERENCES `websiteFunctions_websites` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `databases_globaluserdb` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `username` varchar(200) NOT NULL, - `password` varchar(500) NOT NULL, - `token` varchar(20) NOT NULL, - PRIMARY KEY (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = "CREATE TABLE `databases_databasesusers` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `username` varchar(50) NOT NULL UNIQUE, `owner_id` integer NOT NULL)" - - try: - cursor.execute(query) - except: - pass - - query = "ALTER TABLE `databases_databasesusers` ADD CONSTRAINT `databases_databasesu_owner_id_908fc638_fk_databases` FOREIGN KEY (`owner_id`) REFERENCES `databases_databases` (`id`);" - - try: - cursor.execute(query) - except: - pass - - try: - connection.close() - except: - pass - except: - pass - - @staticmethod - def GeneralMigrations(): - try: - - cwd = os.getcwd() - os.chdir('/usr/local/CyberCP') - - command = '/usr/local/CyberPanel/bin/python manage.py makemigrations' - Upgrade.executioner(command, 'python manage.py makemigrations', 0) - - command = '/usr/local/CyberPanel/bin/python manage.py makemigrations' - Upgrade.executioner(command, '/usr/local/CyberPanel/bin/python manage.py migrate', 0) - - os.chdir(cwd) - - except: - pass - - @staticmethod - def IncBackupMigrations(): - try: - connection, cursor = Upgrade.setupConnection('cyberpanel') - - query = """CREATE TABLE `IncBackups_backupjob` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `destination` varchar(300) NOT NULL, - `frequency` varchar(50) NOT NULL, - `websiteData` int(11) NOT NULL, - `websiteDatabases` int(11) NOT NULL, - `websiteDataEmails` int(11) NOT NULL, - PRIMARY KEY (`id`) -)""" - try: - cursor.execute(query) - except: - pass - - - query = 'ALTER TABLE IncBackups_backupjob ADD retention integer DEFAULT 0' - - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `IncBackups_incjob` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `date` datetime(6) NOT NULL, - `website_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `IncBackups_incjob_website_id_aad31bf6_fk_websiteFu` (`website_id`), - CONSTRAINT `IncBackups_incjob_website_id_aad31bf6_fk_websiteFu` FOREIGN KEY (`website_id`) REFERENCES `websiteFunctions_websites` (`id`) -)""" - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `IncBackups_jobsites` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `website` varchar(300) NOT NULL, - `job_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `IncBackups_jobsites_job_id_494a1f69_fk_IncBackups_backupjob_id` (`job_id`), - CONSTRAINT `IncBackups_jobsites_job_id_494a1f69_fk_IncBackups_backupjob_id` FOREIGN KEY (`job_id`) REFERENCES `IncBackups_backupjob` (`id`) -)""" - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `IncBackups_jobsnapshots` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `type` varchar(300) NOT NULL, - `snapshotid` varchar(50) NOT NULL, - `job_id` int(11) NOT NULL, - `destination` varchar(200) NOT NULL, - PRIMARY KEY (`id`), - KEY `IncBackups_jobsnapshots_job_id_a8237ca8_fk_IncBackups_incjob_id` (`job_id`), - CONSTRAINT `IncBackups_jobsnapshots_job_id_a8237ca8_fk_IncBackups_incjob_id` FOREIGN KEY (`job_id`) REFERENCES `IncBackups_incjob` (`id`) -)""" - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `websiteFunctions_gitlogs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `date` datetime(6) NOT NULL, - `type` varchar(5) NOT NULL, - `message` longtext NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `websiteFunctions_git_owner_id_ce74c7de_fk_websiteFu` (`owner_id`), - CONSTRAINT `websiteFunctions_git_owner_id_ce74c7de_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_websites` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `websiteFunctions_backupjob` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `logFile` varchar(1000) NOT NULL, - `ipAddress` varchar(50) NOT NULL, - `port` varchar(15) NOT NULL, - `jobFailedSites` int(11) NOT NULL, - `jobSuccessSites` int(11) NOT NULL, - `location` int(11) NOT NULL, - PRIMARY KEY (`id`) -)""" - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `websiteFunctions_backupjoblogs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `message` longtext NOT NULL, - `owner_id` int(11) NOT NULL, - `status` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `websiteFunctions_bac_owner_id_af3d15f9_fk_websiteFu` (`owner_id`), - CONSTRAINT `websiteFunctions_bac_owner_id_af3d15f9_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_backupjob` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `websiteFunctions_gdrive` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL, - `auth` longtext NOT NULL, - `runTime` varchar(20) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`), - KEY `websiteFunctions_gdr_owner_id_b5b1e86f_fk_loginSyst` (`owner_id`), - CONSTRAINT `websiteFunctions_gdr_owner_id_b5b1e86f_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `websiteFunctions_gdrivesites` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `domain` varchar(200) NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `websiteFunctions_gdr_owner_id_ff78b305_fk_websiteFu` (`owner_id`), - CONSTRAINT `websiteFunctions_gdr_owner_id_ff78b305_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_gdrive` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - query = """CREATE TABLE `websiteFunctions_gdrivejoblogs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `status` int(11) NOT NULL, - `message` longtext NOT NULL, - `owner_id` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `websiteFunctions_gdr_owner_id_4cf7983e_fk_websiteFu` (`owner_id`), - CONSTRAINT `websiteFunctions_gdr_owner_id_4cf7983e_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_gdrive` (`id`) -)""" - - try: - cursor.execute(query) - except: - pass - - try: - connection.close() - except: - pass - except: - pass - - @staticmethod - def enableServices(): - try: - servicePath = '/home/cyberpanel/powerdns' - writeToFile = open(servicePath, 'w+') - writeToFile.close() - - servicePath = '/home/cyberpanel/postfix' - writeToFile = open(servicePath, 'w+') - writeToFile.close() - - servicePath = '/home/cyberpanel/pureftpd' - writeToFile = open(servicePath, 'w+') - writeToFile.close() - except: - pass - - @staticmethod - def downloadAndUpgrade(versionNumbring, branch): - try: - ## Download latest version. - - ## Backup settings file. - - Upgrade.stdOut("Backing up settings file.") - - ## CyberPanel DB Creds - dbName = settings.DATABASES['default']['NAME'] - dbUser = settings.DATABASES['default']['USER'] - password = settings.DATABASES['default']['PASSWORD'] - host = settings.DATABASES['default']['HOST'] - port = settings.DATABASES['default']['PORT'] - - ## Root DB Creds - - rootdbName = settings.DATABASES['rootdb']['NAME'] - rootdbdbUser = settings.DATABASES['rootdb']['USER'] - rootdbpassword = settings.DATABASES['rootdb']['PASSWORD'] - - ## Complete db string - - completDBString = """\nDATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': '%s', - 'USER': '%s', - 'PASSWORD': '%s', - 'HOST': '%s', - 'PORT':'%s' - }, - 'rootdb': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': '%s', - 'USER': '%s', - 'PASSWORD': '%s', - 'HOST': '%s', - 'PORT': '%s', - }, -}\n""" % (dbName, dbUser, password, host, port, rootdbName, rootdbdbUser, rootdbpassword, host, port) - - settingsFile = '/usr/local/CyberCP/CyberCP/settings.py' - - Upgrade.stdOut("Settings file backed up.") - - ## Check git branch status - - os.chdir('/usr/local/CyberCP') - - command = 'git config --global user.email "support@cyberpanel.net"' - - if not Upgrade.executioner(command, command, 1): - return 0, 'Failed to execute %s' % (command) - - command = 'git config --global user.name "CyberPanel"' - - if not Upgrade.executioner(command, command, 1): - return 0, 'Failed to execute %s' % (command) - - command = 'git status' - currentBranch = subprocess.check_output(shlex.split(command)).decode() - - if currentBranch.find('On branch %s' % (branch)) > -1 and currentBranch.find( - 'On branch %s-dev' % (branch)) == -1: - - command = 'git stash' - if not Upgrade.executioner(command, command, 1): - return 0, 'Failed to execute %s' % (command) - - command = 'git pull' - if not Upgrade.executioner(command, command, 1): - return 0, 'Failed to execute %s' % (command) - - elif currentBranch.find('not a git repository') > -1: - - os.chdir('/usr/local') - - command = 'git clone https://github.com/usmannasir/cyberpanel' - if not Upgrade.executioner(command, command, 1): - return 0, 'Failed to execute %s' % (command) - - if os.path.exists('CyberCP'): - shutil.rmtree('CyberCP') - - shutil.move('cyberpanel', 'CyberCP') - - else: - - command = 'git fetch' - if not Upgrade.executioner(command, command, 1): - return 0, 'Failed to execute %s' % (command) - - command = 'git stash' - if not Upgrade.executioner(command, command, 1): - return 0, 'Failed to execute %s' % (command) - - command = 'git checkout %s' % (branch) - if not Upgrade.executioner(command, command, 1): - return 0, 'Failed to execute %s' % (command) - - command = 'git pull' - if not Upgrade.executioner(command, command, 1): - return 0, 'Failed to execute %s' % (command) - - ## Copy settings file - - settingsData = open(settingsFile, 'r').readlines() - - DATABASESCHECK = 0 - writeToFile = open(settingsFile, 'w') - - for items in settingsData: - if items.find('DATABASES = {') > -1: - DATABASESCHECK = 1 - - if DATABASESCHECK == 0: - writeToFile.write(items) - - if items.find('DATABASE_ROUTERS = [') > -1: - DATABASESCHECK = 0 - writeToFile.write(completDBString) - writeToFile.write(items) - - writeToFile.close() - - Upgrade.stdOut('Settings file restored!') - - Upgrade.staticContent() - - return 1, None - - except BaseException as msg: - return 0, str(msg) - - @staticmethod - def installLSCPD(branch): - try: - - Upgrade.stdOut("Starting LSCPD installation..") - - cwd = os.getcwd() - - os.chdir('/usr/local') - - command = 'yum -y install gcc gcc-c++ make autoconf glibc rcs' - Upgrade.executioner(command, 'LSCPD Pre-reqs [one]', 0) - - ## - - lscpdPath = '/usr/local/lscp/bin/lscpd' - - if os.path.exists(lscpdPath): - os.remove(lscpdPath) - - command = 'cp -f /usr/local/CyberCP/lscpd-0.3.1 /usr/local/lscp/bin/lscpd-0.3.1' - Upgrade.executioner(command, command, 0) - - command = 'rm -f /usr/local/lscp/bin/lscpd' - Upgrade.executioner(command, command, 0) - - command = 'mv /usr/local/lscp/bin/lscpd-0.3.1 /usr/local/lscp/bin/lscpd' - Upgrade.executioner(command, command, 0) - - command = f'chmod 755 {lscpdPath}' - Upgrade.executioner(command, 'LSCPD Download.', 0) - - command = 'yum -y install pcre-devel openssl-devel expat-devel geoip-devel zlib-devel udns-devel which curl' - Upgrade.executioner(command, 'LSCPD Pre-reqs [two]', 0) - - try: - pwd.getpwnam('lscpd') - except KeyError: - command = 'adduser lscpd -M -d /usr/local/lscp' - Upgrade.executioner(command, 'Add user LSCPD', 0) - - try: - grp.getgrnam('lscpd') - except KeyError: - command = 'groupadd lscpd' - Upgrade.executioner(command, 'Add group LSCPD', 0) - - command = 'usermod -a -G lscpd lscpd' - Upgrade.executioner(command, 'Add group LSCPD', 0) - - command = 'usermod -a -G lsadm lscpd' - Upgrade.executioner(command, 'Add group LSCPD', 0) - - command = 'systemctl daemon-reload' - Upgrade.executioner(command, 'daemon-reload LSCPD', 0) - - command = 'systemctl restart lscpd' - Upgrade.executioner(command, 'Restart LSCPD', 0) - - os.chdir(cwd) - - Upgrade.stdOut("LSCPD successfully installed!") - - except BaseException as msg: - Upgrade.stdOut(str(msg) + " [installLSCPD]") - - @staticmethod - def fixPermissions(): - try: - - try: - def generate_pass(length=14): - chars = string.ascii_uppercase + string.ascii_lowercase + string.digits - size = length - return ''.join(random.choice(chars) for x in range(size)) - - content = """SetPassword('%s'); -echo $oConfig->Save() ? 'Done' : 'Error'; - -?>""" % (generate_pass()) - - writeToFile = open('/usr/local/CyberCP/public/snappymail.php', 'w') - writeToFile.write(content) - writeToFile.close() - - command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/snappymail/data" - subprocess.call(shlex.split(command)) - - except: - pass - - Upgrade.stdOut("Fixing permissions..") - - command = "usermod -G lscpd,lsadm,nobody lscpd" - Upgrade.executioner(command, 'chown core code', 0) - - command = "usermod -G lscpd,lsadm,nogroup lscpd" - Upgrade.executioner(command, 'chown core code', 0) - - ###### fix Core CyberPanel permissions - - command = "find /usr/local/CyberCP -type d -exec chmod 0755 {} \;" - Upgrade.executioner(command, 'chown core code', 0) - - command = "find /usr/local/CyberCP -type f -exec chmod 0644 {} \;" - Upgrade.executioner(command, 'chown core code', 0) - - command = "chmod -R 755 /usr/local/CyberCP/bin" - Upgrade.executioner(command, 'chown core code', 0) - - ## change owner - - command = "chown -R root:root /usr/local/CyberCP" - Upgrade.executioner(command, 'chown core code', 0) - - ########### Fix LSCPD - - command = "find /usr/local/lscp -type d -exec chmod 0755 {} \;" - Upgrade.executioner(command, 'chown core code', 0) - - command = "find /usr/local/lscp -type f -exec chmod 0644 {} \;" - Upgrade.executioner(command, 'chown core code', 0) - - command = "chmod -R 755 /usr/local/lscp/bin" - Upgrade.executioner(command, 'chown core code', 0) - - command = "chmod -R 755 /usr/local/lscp/fcgi-bin" - Upgrade.executioner(command, 'chown core code', 0) - - command = "chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/tmp" - Upgrade.executioner(command, 'chown core code', 0) - - ## change owner - - command = "chown -R root:root /usr/local/lscp" - Upgrade.executioner(command, 'chown core code', 0) - - command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/snappymail/data" - Upgrade.executioner(command, 'chown core code', 0) - - command = "chmod 700 /usr/local/CyberCP/cli/cyberPanel.py" - Upgrade.executioner(command, 'chown core code', 0) - - command = "chmod 700 /usr/local/CyberCP/plogical/upgradeCritical.py" - Upgrade.executioner(command, 'chown core code', 0) - - command = "chmod 755 /usr/local/CyberCP/postfixSenderPolicy/client.py" - Upgrade.executioner(command, 'chown core code', 0) - - command = "chmod 640 /usr/local/CyberCP/CyberCP/settings.py" - Upgrade.executioner(command, 'chown core code', 0) - - command = "chown root:cyberpanel /usr/local/CyberCP/CyberCP/settings.py" - Upgrade.executioner(command, 'chown core code', 0) - - command = 'chmod +x /usr/local/CyberCP/CLManager/CLPackages.py' - Upgrade.executioner(command, 'chmod CLPackages', 0) - - files = ['/etc/yum.repos.d/MariaDB.repo', '/etc/pdns/pdns.conf', '/etc/systemd/system/lscpd.service', - '/etc/pure-ftpd/pure-ftpd.conf', '/etc/pure-ftpd/pureftpd-pgsql.conf', - '/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/pureftpd-ldap.conf', - '/etc/dovecot/dovecot.conf', '/usr/local/lsws/conf/httpd_config.xml', - '/usr/local/lsws/conf/modsec.conf', '/usr/local/lsws/conf/httpd.conf'] - - for items in files: - command = 'chmod 644 %s' % (items) - Upgrade.executioner(command, 'chown core code', 0) - - impFile = ['/etc/pure-ftpd/pure-ftpd.conf', '/etc/pure-ftpd/pureftpd-pgsql.conf', - '/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/pureftpd-ldap.conf', - '/etc/dovecot/dovecot.conf', '/etc/pdns/pdns.conf', '/etc/pure-ftpd/db/mysql.conf', - '/etc/powerdns/pdns.conf'] - - for items in impFile: - command = 'chmod 600 %s' % (items) - Upgrade.executioner(command, 'chown core code', 0) - - command = 'chmod 640 /etc/postfix/*.cf' - subprocess.call(command, shell=True) - - command = 'chmod 640 /etc/dovecot/*.conf' - subprocess.call(command, shell=True) - - command = 'chmod 640 /etc/dovecot/dovecot-sql.conf.ext' - subprocess.call(command, shell=True) - - fileM = ['/usr/local/lsws/FileManager/', '/usr/local/CyberCP/install/FileManager', - '/usr/local/CyberCP/serverStatus/litespeed/FileManager', - '/usr/local/lsws/Example/html/FileManager'] - - for items in fileM: - try: - shutil.rmtree(items) - except: - pass - - command = 'chmod 755 /etc/pure-ftpd/' - subprocess.call(command, shell=True) - - command = 'chmod 644 /etc/dovecot/dovecot.conf' - subprocess.call(command, shell=True) - - command = 'chmod 644 /etc/postfix/main.cf' - subprocess.call(command, shell=True) - - command = 'chmod 644 /etc/postfix/dynamicmaps.cf' - subprocess.call(command, shell=True) - - command = 'chmod +x /usr/local/CyberCP/plogical/renew.py' - Upgrade.executioner(command, command, 0) - - command = 'chmod +x /usr/local/CyberCP/CLManager/CLPackages.py' - Upgrade.executioner(command, command, 0) - - clScripts = ['/usr/local/CyberCP/CLScript/panel_info.py', - '/usr/local/CyberCP/CLScript/CloudLinuxPackages.py', - '/usr/local/CyberCP/CLScript/CloudLinuxUsers.py', - '/usr/local/CyberCP/CLScript/CloudLinuxDomains.py' - , '/usr/local/CyberCP/CLScript/CloudLinuxResellers.py', - '/usr/local/CyberCP/CLScript/CloudLinuxAdmins.py', - '/usr/local/CyberCP/CLScript/CloudLinuxDB.py', '/usr/local/CyberCP/CLScript/UserInfo.py'] - - for items in clScripts: - command = 'chmod +x %s' % (items) - Upgrade.executioner(command, 0) - - command = 'chmod 600 /usr/local/CyberCP/plogical/adminPass.py' - Upgrade.executioner(command, 0) - - command = 'chmod 600 /etc/cagefs/exclude/cyberpanelexclude' - Upgrade.executioner(command, 0) - - command = "find /usr/local/CyberCP/ -name '*.pyc' -delete" - Upgrade.executioner(command, 0) - - if os.path.exists(Upgrade.CentOSPath): - command = 'chown root:pdns /etc/pdns/pdns.conf' - Upgrade.executioner(command, 0) - - command = 'chmod 640 /etc/pdns/pdns.conf' - Upgrade.executioner(command, 0) - - command = 'chmod 640 /usr/local/lscp/cyberpanel/logs/access.log' - Upgrade.executioner(command, 0) - - command = '/usr/local/lsws/lsphp72/bin/php /usr/local/CyberCP/public/snappymail.php' - Upgrade.executioner(command, 0) - - command = 'chmod 600 /usr/local/CyberCP/public/snappymail.php' - Upgrade.executioner(command, 0) - - ### - - WriteToFile = open('/etc/fstab', 'a') - WriteToFile.write('proc /proc proc defaults,hidepid=2 0 0\n') - WriteToFile.close() - - command = 'mount -o remount,rw,hidepid=2 /proc' - Upgrade.executioner(command, 0) - - ### - - CentOSPath = '/etc/redhat-release' - - if not os.path.exists(CentOSPath): - group = 'nobody' - else: - group = 'nogroup' - - command = 'chown root:%s /usr/local/lsws/logs' % (group) - Upgrade.executioner(command, 0) - - command = 'chmod 750 /usr/local/lsws/logs' - Upgrade.executioner(command, 0) - - ## symlink protection - - writeToFile = open('/usr/lib/sysctl.d/50-default.conf', 'a') - writeToFile.writelines('fs.protected_hardlinks = 1\n') - writeToFile.writelines('fs.protected_symlinks = 1\n') - writeToFile.close() - - command = 'sysctl --system' - Upgrade.executioner(command, 0) - - command = 'chmod 700 %s' % ('/home/cyberpanel') - Upgrade.executioner(command, 0) - - destPrivKey = "/usr/local/lscp/conf/key.pem" - - command = 'chmod 600 %s' % (destPrivKey) - Upgrade.executioner(command, 0) - - Upgrade.stdOut("Permissions updated.") - - except BaseException as msg: - Upgrade.stdOut(str(msg) + " [installLSCPD]") - - @staticmethod - def AutoUpgradeAcme(): - command = '/root/.acme.sh/acme.sh --upgrade --auto-upgrade' - Upgrade.executioner(command, command, 0) - command = '/root/.acme.sh/acme.sh --set-default-ca --server letsencrypt' - Upgrade.executioner(command, command, 0) - - @staticmethod - def installPHP73(): - try: - if Upgrade.installedOutput.find('lsphp73') == -1: - command = 'yum install -y lsphp73 lsphp73-json lsphp73-xmlrpc lsphp73-xml lsphp73-tidy lsphp73-soap lsphp73-snmp ' \ - 'lsphp73-recode lsphp73-pspell lsphp73-process lsphp73-pgsql lsphp73-pear lsphp73-pdo lsphp73-opcache ' \ - 'lsphp73-odbc lsphp73-mysqlnd lsphp73-mcrypt lsphp73-mbstring lsphp73-ldap lsphp73-intl lsphp73-imap ' \ - 'lsphp73-gmp lsphp73-gd lsphp73-enchant lsphp73-dba lsphp73-common lsphp73-bcmath' - Upgrade.executioner(command, 'Install PHP 73, 0') - - if Upgrade.installedOutput.find('lsphp74') == -1: - command = 'yum install -y lsphp74 lsphp74-json lsphp74-xmlrpc lsphp74-xml lsphp74-tidy lsphp74-soap lsphp74-snmp ' \ - 'lsphp74-recode lsphp74-pspell lsphp74-process lsphp74-pgsql lsphp74-pear lsphp74-pdo lsphp74-opcache ' \ - 'lsphp74-odbc lsphp74-mysqlnd lsphp74-mcrypt lsphp74-mbstring lsphp74-ldap lsphp74-intl lsphp74-imap ' \ - 'lsphp74-gmp lsphp74-gd lsphp74-enchant lsphp74-dba lsphp74-common lsphp74-bcmath' - - Upgrade.executioner(command, 'Install PHP 74, 0') - - if Upgrade.installedOutput.find('lsphp80') == -1: - command = 'yum install lsphp80* -y' - subprocess.call(command, shell=True) - - if Upgrade.installedOutput.find('lsphp81') == -1: - command = 'yum install lsphp81* -y' - subprocess.call(command, shell=True) - - except: - command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install ' \ - 'lsphp7? lsphp7?-common lsphp7?-curl lsphp7?-dev lsphp7?-imap lsphp7?-intl lsphp7?-json ' \ - 'lsphp7?-ldap lsphp7?-mysql lsphp7?-opcache lsphp7?-pspell lsphp7?-recode ' \ - 'lsphp7?-sqlite3 lsphp7?-tidy' - Upgrade.executioner(command, 'Install PHP 73, 0') - - command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp80*' - os.system(command) - - command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp81*' - os.system(command) - - CentOSPath = '/etc/redhat-release' - - if not os.path.exists(CentOSPath): - #command = 'cp /usr/local/lsws/lsphp71/bin/php /usr/bin/' - #Upgrade.executioner(command, 'Set default PHP 7.0, 0') - - @staticmethod - def someDirectories(): - command = "mkdir -p /usr/local/lscpd/admin/" - Upgrade.executioner(command, 0) - - command = "mkdir -p /usr/local/lscp/cyberpanel/logs" - Upgrade.executioner(command, 0) - - @staticmethod - def upgradeDovecot(): - try: - Upgrade.stdOut("Upgrading Dovecot..") - CentOSPath = '/etc/redhat-release' - - dovecotConfPath = '/etc/dovecot/' - postfixConfPath = '/etc/postfix/' - - ## Take backup of configurations - - configbackups = '/home/cyberpanel/configbackups' - - command = 'mkdir %s' % (configbackups) - Upgrade.executioner(command, 0) - - command = 'cp -pR %s %s' % (dovecotConfPath, configbackups) - Upgrade.executioner(command, 0) - - command = 'cp -pR %s %s' % (postfixConfPath, configbackups) - Upgrade.executioner(command, 0) - - if Upgrade.FindOperatingSytem() == CENTOS8 or Upgrade.FindOperatingSytem() == CENTOS7: - - command = "yum makecache -y" - Upgrade.executioner(command, 0) - - command = "yum update -y" - Upgrade.executioner(command, 0) - - if Upgrade.FindOperatingSytem() == CENTOS8: - command = 'dnf remove dovecot23 dovecot23-mysql -y' - Upgrade.executioner(command, 0) - - command = 'dnf install --enablerepo=gf-plus dovecot23 dovecot23-mysql -y' - Upgrade.executioner(command, 0) - - import django - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") - django.setup() - from mailServer.models import EUsers - - Upgrade.stdOut("Upgrading passwords...") - for items in EUsers.objects.all(): - if items.password.find('CRYPT') > -1: - continue - command = 'doveadm pw -p %s' % (items.password) - items.password = subprocess.check_output(shlex.split(command)).decode("utf-8").strip('\n') - items.save() - - command = "systemctl restart dovecot" - Upgrade.executioner(command, 0) - - ### Postfix Upgrade - - command = 'yum remove postfix -y' - Upgrade.executioner(command, 0) - - command = 'yum clean all' - Upgrade.executioner(command, 0) - - if Upgrade.FindOperatingSytem() == CENTOS7: - command = 'yum makecache fast' - else: - command = 'yum makecache -y' - - Upgrade.executioner(command, 0) - - if Upgrade.FindOperatingSytem() == CENTOS7: - command = 'yum install --enablerepo=gf-plus -y postfix3 postfix3-ldap postfix3-mysql postfix3-pcre' - else: - command = 'dnf install --enablerepo=gf-plus postfix3 postfix3-mysql -y' - - Upgrade.executioner(command, 0) - - ### Restore dovecot/postfix conf - - command = 'cp -pR %s/dovecot/ /etc/' % (configbackups) - Upgrade.executioner(command, 0) - - command = 'cp -pR %s/postfix/ /etc/' % (configbackups) - Upgrade.executioner(command, 0) - - ## Restored - - command = 'systemctl restart postfix' - Upgrade.executioner(command, 0) - elif Upgrade.FindOperatingSytem() == Ubuntu20: - - debPath = '/etc/apt/sources.list.d/dovecot.list' - # writeToFile = open(debPath, 'w') - # writeToFile.write('deb https://repo.dovecot.org/ce-2.3-latest/ubuntu/focal focal main\n') - # writeToFile.close() - # - # command = "apt update -y" - # Upgrade.executioner(command, command) - # - # command = 'dpkg --configure -a' - # subprocess.call(command, shell=True) - # - # command = 'apt --fix-broken install -y' - # subprocess.call(command, shell=True) - # - # command = 'DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" upgrade -y' - # subprocess.call(command, shell=True) - - - dovecotConf = '/etc/dovecot/dovecot.conf' - - dovecotContent = open(dovecotConf, 'r').read() - - if dovecotContent.find('service stats') == -1: - writeToFile = open(dovecotConf, 'a') - - content = """\nservice stats { - unix_listener stats-reader { - user = vmail - group = vmail - mode = 0660 - } - unix_listener stats-writer { - user = vmail - group = vmail - mode = 0660 - } -}\n""" - - writeToFile.write(content) - writeToFile.close() - - command = 'systemctl restart dovecot' - Upgrade.executioner(command, command, 0) - - command = 'rm -rf %s' % (configbackups) - Upgrade.executioner(command, command, 0) - - Upgrade.stdOut("Dovecot upgraded.") - - except BaseException as msg: - Upgrade.stdOut(str(msg) + " [upgradeDovecot]") - - @staticmethod - def installRestic(): - CentOSPath = '/etc/redhat-release' - - if os.path.exists(CentOSPath): - if Upgrade.installedOutput.find('restic') == -1: - command = 'yum install restic -y' - Upgrade.executioner(command, 'Install Restic') - else: - - if Upgrade.installedOutput.find('restic/bionic,now 0.8') == -1: - command = 'apt-get update -y' - Upgrade.executioner(command, 'Install Restic') - - command = 'apt-get install restic -y' - Upgrade.executioner(command, 'Install Restic') - - @staticmethod - def UpdateMaxSSLCons(): - command = "sed -i 's|2000|10000|g' /usr/local/lsws/conf/httpd_config.xml" - Upgrade.executioner(command, 0) - - command = "sed -i 's|200|10000|g' /usr/local/lsws/conf/httpd_config.xml" - Upgrade.executioner(command, 0) - - @staticmethod - def installCLScripts(): - try: - - CentOSPath = '/etc/redhat-release' - - if os.path.exists(CentOSPath): - command = 'mkdir -p /opt/cpvendor/etc/' - Upgrade.executioner(command, 0) - - content = """[integration_scripts] - -panel_info = /usr/local/CyberCP/CLScript/panel_info.py -packages = /usr/local/CyberCP/CLScript/CloudLinuxPackages.py -users = /usr/local/CyberCP/CLScript/CloudLinuxUsers.py -domains = /usr/local/CyberCP/CLScript/CloudLinuxDomains.py -resellers = /usr/local/CyberCP/CLScript/CloudLinuxResellers.py -admins = /usr/local/CyberCP/CLScript/CloudLinuxAdmins.py -db_info = /usr/local/CyberCP/CLScript/CloudLinuxDB.py - -[lvemanager_config] -ui_user_info = /usr/local/CyberCP/CLScript/UserInfo.py -base_path = /usr/local/lvemanager -run_service = 1 -service_port = 9000 -""" - - if not os.path.exists('/opt/cpvendor/etc/integration.ini'): - writeToFile = open('/opt/cpvendor/etc/integration.ini', 'w') - writeToFile.write(content) - writeToFile.close() - - command = 'mkdir -p /etc/cagefs/exclude' - Upgrade.executioner(command, command, 0) - - content = """cyberpanel -docker -ftpuser -lscpd -opendkim -pdns -vmail -""" - - writeToFile = open('/etc/cagefs/exclude/cyberpanelexclude', 'w') - writeToFile.write(content) - writeToFile.close() - - except: - pass - - @staticmethod - def runSomeImportantBash(): - - # Remove invalid crons from /etc/crontab Reference: https://github.com/usmannasir/cyberpanel/issues/216 - command = """sed -i '/CyberCP/d' /etc/crontab""" - subprocess.call(command, shell=True) - - # Setup /usr/local/lsws/conf/httpd.conf to use new Logformat standard for better stats and accesslogs - command = """sed -i "s|^LogFormat.*|LogFormat '%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"' combined|g" /usr/local/lsws/conf/httpd.conf""" - subprocess.call(command, shell=True) - - # Fix all existing vhost confs to use new Logformat standard for better stats and accesslogs - command = """find /usr/local/lsws/conf/vhosts/ -type f -name 'vhost.conf' -exec sed -i "s/.*CustomLog.*/ LogFormat '%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"' combined\n&/g" {} \;""" - subprocess.call(command, shell=True) - - # Install any Cyberpanel missing crons to root crontab so its visible to users via crontab -l as root user - - # Install findBWUsage cron if missing - - CentOSPath = '/etc/redhat-release' - - if os.path.exists(CentOSPath): - cronPath = '/var/spool/cron/root' - else: - cronPath = '/var/spool/cron/crontabs/root' - - if os.path.exists(cronPath): - data = open(cronPath, 'r').read() - - if data.find('findBWUsage') == -1: - content = """ -0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/findBWUsage.py >/dev/null 2>&1 -0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py hourlyCleanup >/dev/null 2>&1 -0 0 1 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py monthlyCleanup >/dev/null 2>&1 -0 2 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/upgradeCritical.py >/dev/null 2>&1 -0 2 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/renew.py >/dev/null 2>&1 -7 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null -*/3 * * * * if ! find /home/*/public_html/ -maxdepth 2 -type f -newer /usr/local/lsws/cgid -name '.htaccess' -exec false {} +; then /usr/local/lsws/bin/lswsctrl restart; fi -""" - - writeToFile = open(cronPath, 'w') - writeToFile.write(content) - writeToFile.close() - - if data.find('IncScheduler.py') == -1: - content = """ -0 12 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Daily -0 0 * * 0 /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Weekly -""" - writeToFile = open(cronPath, 'a') - writeToFile.write(content) - writeToFile.close() - else: - content = """ -0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/findBWUsage.py >/dev/null 2>&1 -0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py hourlyCleanup >/dev/null 2>&1 -0 0 1 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py monthlyCleanup >/dev/null 2>&1 -0 2 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/upgradeCritical.py >/dev/null 2>&1 -0 2 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/renew.py >/dev/null 2>&1 -7 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null -0 0 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Daily -0 0 * * 0 /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Weekly -""" - writeToFile = open(cronPath, 'w') - writeToFile.write(content) - writeToFile.close() - - ### Check and remove OLS restart if lsws ent detected - - if not os.path.exists('/usr/local/lsws/bin/openlitespeed'): - - data = open(cronPath, 'r').readlines() - - writeToFile = open(cronPath, 'w') - - for items in data: - if items.find('-maxdepth 2 -type f -newer') > -1: - pass - else: - writeToFile.writelines(items) - - writeToFile.close() - - - if not os.path.exists(CentOSPath): - command = 'chmod 600 %s' % (cronPath) - Upgrade.executioner(command, 0) - - @staticmethod - def UpdateConfigOfCustomACL(): - sys.path.append('/usr/local/CyberCP') - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") - import django - django.setup() - from loginSystem.models import ACL - for acl in ACL.objects.all(): - if acl.name == 'admin' or acl.name == 'reseller' or acl.name == 'user': - continue - elif acl.config == '{}': - acl.config = '{"adminStatus":%s, "versionManagement": %s, "createNewUser": %s, "listUsers": %s, "deleteUser": %s, "resellerCenter": %s, "changeUserACL": %s, "createWebsite": %s, "modifyWebsite": %s, "suspendWebsite": %s, "deleteWebsite": %s, "createPackage": %s, "listPackages": %s, "deletePackage": %s, "modifyPackage": %s, "createDatabase": %s, "deleteDatabase": %s, "listDatabases": %s, "createNameServer": %s, "createDNSZone": %s, "deleteZone": %s, "addDeleteRecords": %s, "createEmail": %s, "listEmails": %s, "deleteEmail": %s, "emailForwarding": %s, "changeEmailPassword": %s, "dkimManager": %s, "createFTPAccount": %s, "deleteFTPAccount": %s, "listFTPAccounts": %s, "createBackup": %s, "restoreBackup": %s, "addDeleteDestinations": %s, "scheduleBackups": %s, "remoteBackups": %s, "googleDriveBackups": %s, "manageSSL": %s, "hostnameSSL": %s, "mailServerSSL": %s }' \ - % (str(acl.adminStatus), str(acl.versionManagement), str(acl.createNewUser), - str(acl.listUsers), str(acl.deleteUser), str(acl.resellerCenter), str(acl.changeUserACL), - str(acl.createWebsite), str(acl.modifyWebsite), str(acl.suspendWebsite), str(acl.deleteWebsite), - str(acl.createPackage), str(acl.listPackages), str(acl.deletePackage), str(acl.modifyPackage), - str(acl.createDatabase), str(acl.deleteDatabase), str(acl.listDatabases), str(acl.createNameServer), - str(acl.createDNSZone), str(acl.deleteZone), str(acl.addDeleteRecords), str(acl.createEmail), - str(acl.listEmails), str(acl.deleteEmail), str(acl.emailForwarding), str(acl.changeEmailPassword), - str(acl.dkimManager), str(acl.createFTPAccount), str(acl.deleteFTPAccount), str(acl.listFTPAccounts), - str(acl.createBackup), str(acl.restoreBackup), str(acl.addDeleteDestinations), str(acl.scheduleBackups), str(acl.remoteBackups), '1', - str(acl.manageSSL), str(acl.hostnameSSL), str(acl.mailServerSSL)) - acl.save() - - @staticmethod - def upgrade(branch): - - # Upgrade.stdOut("Upgrades are currently disabled") - # return 0 - - if os.path.exists(Upgrade.CentOSPath): - command = 'yum list installed' - Upgrade.installedOutput = subprocess.check_output(shlex.split(command)).decode() - else: - command = 'apt list' - Upgrade.installedOutput = subprocess.check_output(shlex.split(command)).decode() - - command = 'systemctl stop cpssh' - Upgrade.executioner(command, 'fix csf if there', 0) - - ## Add LSPHP7.4 TO LSWS Ent configs - - if not os.path.exists('/usr/local/lsws/bin/openlitespeed'): - - if os.path.exists('httpd_config.xml'): - os.remove('httpd_config.xml') - - command = 'wget https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/install/litespeed/httpd_config.xml' - Upgrade.executioner(command, command, 0) - # os.remove('/usr/local/lsws/conf/httpd_config.xml') - # shutil.copy('httpd_config.xml', '/usr/local/lsws/conf/httpd_config.xml') - - postfixPath = '/home/cyberpanel/postfix' - pdns = '/home/cyberpanel/pdns' - pureftpd = '/home/cyberpanel/ftp' - - Upgrade.updateRepoURL() - - os.chdir("/usr/local") - - command = 'yum remove yum-plugin-priorities -y' - Upgrade.executioner(command, 'remove yum-plugin-priorities', 0) - - ## Current Version - - command = "systemctl stop lscpd" - Upgrade.executioner(command, 'stop lscpd', 0) - - Upgrade.fixSudoers() - Upgrade.mountTemp() - Upgrade.dockerUsers() - Upgrade.setupComposer() - - ## - - versionNumbring = Upgrade.downloadLink() - - if os.path.exists('/usr/local/CyberPanel.' + versionNumbring): - os.remove('/usr/local/CyberPanel.' + versionNumbring) - - ## - - Upgrade.downloadAndUpgrade(versionNumbring, branch) - Upgrade.download_install_phpmyadmin() - Upgrade.downoad_and_install_raindloop() - - ## - - ## - - Upgrade.mailServerMigrations() - Upgrade.emailMarketingMigrationsa() - Upgrade.dockerMigrations() - Upgrade.CLMigrations() - Upgrade.IncBackupMigrations() - Upgrade.installRestic() - - ## - - # Upgrade.setupVirtualEnv() - - ## - - Upgrade.applyLoginSystemMigrations() - - ## Put function here to update custom ACLs - - Upgrade.UpdateConfigOfCustomACL() - - Upgrade.s3BackupMigrations() - Upgrade.containerMigrations() - Upgrade.manageServiceMigrations() - Upgrade.enableServices() - - Upgrade.installPHP73() - Upgrade.setupCLI() - Upgrade.someDirectories() - Upgrade.installLSCPD(branch) - - ### General migrations are not needed any more - - # Upgrade.GeneralMigrations() - - # Upgrade.p3() - - ## Also disable email service upgrade - - # if os.path.exists(postfixPath): - # Upgrade.upgradeDovecot() - - - ## Upgrade version - - Upgrade.fixPermissions() - - ## - - ### Disable version upgrade too - - # Upgrade.upgradeVersion() - - Upgrade.UpdateMaxSSLCons() - - ## Update LSCPD PHP - - phpPath = '/usr/local/lscp/fcgi-bin/lsphp' - - try: - os.remove(phpPath) - except: - pass - - command = 'cp /usr/local/lsws/lsphp73/bin/lsphp %s' % (phpPath) - Upgrade.executioner(command, 0) - - try: - command = "systemctl start lscpd" - Upgrade.executioner(command, 'Start LSCPD', 0) - except: - pass - - command = 'csf -uf' - Upgrade.executioner(command, 'fix csf if there', 0) - command = 'systemctl stop cpssh' - Upgrade.executioner(command, 'fix csf if there', 0) - Upgrade.AutoUpgradeAcme() - Upgrade.installCLScripts() - Upgrade.runSomeImportantBash() - - ## Move static files - - imunifyPath = '/usr/local/CyberCP/public/imunify' - - if os.path.exists(imunifyPath): - command = "yum reinstall imunify360-firewall-generic -y" - Upgrade.executioner(command, command, 1) - - imunifyAVPath = '/etc/sysconfig/imunify360/integration.conf' - - if os.path.exists(imunifyAVPath): - command = "yum reinstall imunify-antivirus-generic -y" - Upgrade.executioner(command, command, 1) - - Upgrade.stdOut("Upgrade Completed.") - -def main(): - - parser = argparse.ArgumentParser(description='CyberPanel Installer') - parser.add_argument('branch', help='Install from branch name.') - - args = parser.parse_args() - - Upgrade.upgrade(args.branch) - - -if __name__ == "__main__": - main() From 1f3d5afe6a1b949af000958b5c051da2847a9803 Mon Sep 17 00:00:00 2001 From: Master3395 Date: Mon, 15 Sep 2025 01:39:21 +0200 Subject: [PATCH 02/11] Add PhpMyAdmin access middleware and session check for user authentication - Updated settings.py to include PhpMyAdminAccessMiddleware for enhanced access control. - Modified phpmyadminsignin.php to check user session and redirect to the login page if not authenticated. --- CyberCP/phpmyadminMiddleware.py | 32 ++++++ CyberCP/settings.py | 3 +- deploy_phpmyadmin_redirect.sh | 54 +++++++++ phpmyadmin_htaccess | 25 +++++ phpmyadmin_index_redirect.php | 22 ++++ plogical/phpmyadminsignin.php | 7 ++ rollback_phpmyadmin_redirect.sh | 49 ++++++++ to-do/SECURITY_INSTALLATION.md | 192 -------------------------------- 8 files changed, 191 insertions(+), 193 deletions(-) create mode 100644 CyberCP/phpmyadminMiddleware.py create mode 100644 deploy_phpmyadmin_redirect.sh create mode 100644 phpmyadmin_htaccess create mode 100644 phpmyadmin_index_redirect.php create mode 100644 rollback_phpmyadmin_redirect.sh delete mode 100644 to-do/SECURITY_INSTALLATION.md diff --git a/CyberCP/phpmyadminMiddleware.py b/CyberCP/phpmyadminMiddleware.py new file mode 100644 index 000000000..0b218f67c --- /dev/null +++ b/CyberCP/phpmyadminMiddleware.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +""" +phpMyAdmin Access Control Middleware + +This middleware checks if users are trying to access phpMyAdmin directly +without being logged into CyberPanel and redirects them to the login page. +""" + +from django.shortcuts import redirect +from django.http import HttpResponseRedirect +from django.urls import reverse + + +class PhpMyAdminAccessMiddleware: + """ + Middleware to control phpMyAdmin access and redirect unauthenticated users to login page. + """ + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + # Check if the request is for phpMyAdmin + if request.path.startswith('/phpmyadmin/'): + # Check if user is authenticated (has session) + if 'userID' not in request.session: + # Redirect to CyberPanel login page + login_url = '/base/' + return HttpResponseRedirect(login_url) + + response = self.get_response(request) + return response diff --git a/CyberCP/settings.py b/CyberCP/settings.py index 242636410..ad059c6a7 100644 --- a/CyberCP/settings.py +++ b/CyberCP/settings.py @@ -87,7 +87,8 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'CyberCP.secMiddleware.secMiddleware' + 'CyberCP.secMiddleware.secMiddleware', + 'CyberCP.phpmyadminMiddleware.PhpMyAdminAccessMiddleware' ] ROOT_URLCONF = 'CyberCP.urls' diff --git a/deploy_phpmyadmin_redirect.sh b/deploy_phpmyadmin_redirect.sh new file mode 100644 index 000000000..203971f78 --- /dev/null +++ b/deploy_phpmyadmin_redirect.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# CyberPanel phpMyAdmin Access Control Deployment Script +# This script implements redirect functionality for unauthenticated phpMyAdmin access + +echo "=== CyberPanel phpMyAdmin Access Control Deployment ===" + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + echo "Please run this script as root" + exit 1 +fi + +# Backup original phpMyAdmin index.php if it exists +if [ -f "/usr/local/CyberCP/public/phpmyadmin/index.php" ]; then + echo "Backing up original phpMyAdmin index.php..." + cp /usr/local/CyberCP/public/phpmyadmin/index.php /usr/local/CyberCP/public/phpmyadmin/index.php.backup.$(date +%Y%m%d_%H%M%S) +fi + +# Deploy the redirect index.php +echo "Deploying phpMyAdmin access control..." +cp /usr/local/CyberCP/phpmyadmin_index_redirect.php /usr/local/CyberCP/public/phpmyadmin/index.php + +# Deploy .htaccess for additional protection +echo "Deploying .htaccess protection..." +cp /usr/local/CyberCP/phpmyadmin_htaccess /usr/local/CyberCP/public/phpmyadmin/.htaccess + +# Set proper permissions +echo "Setting permissions..." +chown lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/index.php +chmod 644 /usr/local/CyberCP/public/phpmyadmin/index.php +chown lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/.htaccess +chmod 644 /usr/local/CyberCP/public/phpmyadmin/.htaccess + +# Restart LiteSpeed to ensure changes take effect +echo "Restarting LiteSpeed..." +systemctl restart lscpd + +echo "=== Deployment Complete ===" +echo "" +echo "phpMyAdmin access control has been deployed successfully!" +echo "" +echo "What this does:" +echo "- Users trying to access phpMyAdmin directly without being logged into CyberPanel" +echo " will now be redirected to the CyberPanel login page (/base/)" +echo "- Authenticated users will continue to access phpMyAdmin normally" +echo "" +echo "To revert changes, restore the backup:" +echo "cp /usr/local/CyberCP/public/phpmyadmin/index.php.backup.* /usr/local/CyberCP/public/phpmyadmin/index.php" +echo "" +echo "Test the implementation by:" +echo "1. Opening an incognito/private browser window" +echo "2. Going to https://your-server:2087/phpmyadmin/" +echo "3. You should be redirected to the CyberPanel login page" diff --git a/phpmyadmin_htaccess b/phpmyadmin_htaccess new file mode 100644 index 000000000..e316d5b8c --- /dev/null +++ b/phpmyadmin_htaccess @@ -0,0 +1,25 @@ +# CyberPanel phpMyAdmin Access Control +# Place this file as /usr/local/CyberCP/public/phpmyadmin/.htaccess + +# Enable rewrite engine +RewriteEngine On + +# Check if user is not authenticated and redirect to login +RewriteCond %{HTTP_COOKIE} !sessionid= +RewriteRule ^(.*)$ /base/ [R=302,L] + +# Additional security headers +Header always set X-Frame-Options DENY +Header always set X-Content-Type-Options nosniff +Header always set X-XSS-Protection "1; mode=block" + +# Prevent direct access to sensitive files + + Order Allow,Deny + Deny from all + + + + Order Allow,Deny + Deny from all + diff --git a/phpmyadmin_index_redirect.php b/phpmyadmin_index_redirect.php new file mode 100644 index 000000000..a6c8a0fa2 --- /dev/null +++ b/phpmyadmin_index_redirect.php @@ -0,0 +1,22 @@ + diff --git a/plogical/phpmyadminsignin.php b/plogical/phpmyadminsignin.php index 3b2f92d44..7076a8758 100644 --- a/plogical/phpmyadminsignin.php +++ b/plogical/phpmyadminsignin.php @@ -1,5 +1,12 @@ /dev/null | head -n1) + +if [ -z "$LATEST_BACKUP" ]; then + echo "No backup found. Cannot rollback changes." + echo "You may need to reinstall phpMyAdmin or restore from your own backup." + exit 1 +fi + +echo "Found backup: $LATEST_BACKUP" +echo "Restoring original phpMyAdmin index.php..." + +# Restore the original index.php +cp "$LATEST_BACKUP" /usr/local/CyberCP/public/phpmyadmin/index.php + +# Remove the .htaccess file if it exists +if [ -f "/usr/local/CyberCP/public/phpmyadmin/.htaccess" ]; then + echo "Removing .htaccess file..." + rm /usr/local/CyberCP/public/phpmyadmin/.htaccess +fi + +# Set proper permissions +echo "Setting permissions..." +chown lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/index.php +chmod 644 /usr/local/CyberCP/public/phpmyadmin/index.php + +# Restart LiteSpeed to ensure changes take effect +echo "Restarting LiteSpeed..." +systemctl restart lscpd + +echo "=== Rollback Complete ===" +echo "" +echo "phpMyAdmin access control has been reverted!" +echo "phpMyAdmin should now work as it did before the changes." +echo "" +echo "Backup file used: $LATEST_BACKUP" diff --git a/to-do/SECURITY_INSTALLATION.md b/to-do/SECURITY_INSTALLATION.md deleted file mode 100644 index dd72886fa..000000000 --- a/to-do/SECURITY_INSTALLATION.md +++ /dev/null @@ -1,192 +0,0 @@ -# CyberPanel Secure Installation Guide - -## Overview - -This document describes the secure installation process for CyberPanel that eliminates hardcoded passwords and implements environment-based configuration. - -## Security Improvements - -### ✅ **Fixed Security Vulnerabilities** - -1. **Hardcoded Database Passwords** - Now generated securely during installation -2. **Hardcoded Django Secret Key** - Now generated using cryptographically secure random generation -3. **Environment Variables** - All sensitive configuration moved to `.env` file -4. **File Permissions** - `.env` file set to 600 (owner read/write only) - -### 🔐 **Security Features** - -- **Cryptographically Secure Passwords**: Uses Python's `secrets` module for password generation -- **Environment-based Configuration**: Sensitive data stored in `.env` file, not in code -- **Secure File Permissions**: Environment files protected with 600 permissions -- **Credential Backup**: Automatic backup of credentials for recovery -- **Fallback Security**: Maintains backward compatibility with fallback method - -## Installation Process - -### 1. **Automatic Secure Installation** - -The installation script now automatically: - -1. Generates secure random passwords for: - - MySQL root user - - CyberPanel database user - - Django secret key - -2. Creates `.env` file with secure configuration: - ```bash - # Generated during installation - SECRET_KEY=your_64_character_secure_key - DB_PASSWORD=your_24_character_secure_password - ROOT_DB_PASSWORD=your_24_character_secure_password - ``` - -3. Creates `.env.backup` file for credential recovery -4. Sets secure file permissions (600) on all environment files - -### 2. **Manual Installation** (if needed) - -If you need to manually generate environment configuration: - -```bash -cd /usr/local/CyberCP -python install/env_generator.py /usr/local/CyberCP -``` - -## File Structure - -``` -/usr/local/CyberCP/ -├── .env # Main environment configuration (600 permissions) -├── .env.backup # Credential backup (600 permissions) -├── .env.template # Template for manual configuration -├── .gitignore # Prevents .env files from being committed -└── CyberCP/ - └── settings.py # Updated to use environment variables -``` - -## Security Best Practices - -### ✅ **Do's** - -- Keep `.env` and `.env.backup` files secure -- Record credentials from `.env.backup` and delete the file after installation -- Use strong, unique passwords for production deployments -- Regularly rotate database passwords -- Monitor access to environment files - -### ❌ **Don'ts** - -- Never commit `.env` files to version control -- Don't share `.env` files via insecure channels -- Don't use default passwords in production -- Don't leave `.env.backup` files on the system after recording credentials - -## Recovery - -### **Lost Credentials** - -If you lose your database credentials: - -1. Check if `.env.backup` file exists: - ```bash - sudo cat /usr/local/CyberCP/.env.backup - ``` - -2. If backup doesn't exist, you'll need to reset MySQL passwords using MySQL recovery procedures - -### **Regenerate Environment** - -To regenerate environment configuration: - -```bash -cd /usr/local/CyberCP -sudo python install/env_generator.py /usr/local/CyberCP -``` - -## Configuration Options - -### **Environment Variables** - -| Variable | Description | Default | -|----------|-------------|---------| -| `SECRET_KEY` | Django secret key | Generated (64 chars) | -| `DB_PASSWORD` | CyberPanel DB password | Generated (24 chars) | -| `ROOT_DB_PASSWORD` | MySQL root password | Generated (24 chars) | -| `DEBUG` | Debug mode | False | -| `ALLOWED_HOSTS` | Allowed hosts | localhost,127.0.0.1,hostname | - -### **Custom Configuration** - -To use custom passwords during installation: - -```bash -python install/env_generator.py /usr/local/CyberCP "your_root_password" "your_db_password" -``` - -## Troubleshooting - -### **Installation Fails** - -If the new secure installation fails: - -1. Check installation logs for error messages -2. The system will automatically fallback to the original installation method -3. Verify Python dependencies are installed: - ```bash - pip install python-dotenv - ``` - -### **Environment Loading Issues** - -If Django can't load environment variables: - -1. Ensure `.env` file exists and has correct permissions: - ```bash - ls -la /usr/local/CyberCP/.env - # Should show: -rw------- 1 root root - ``` - -2. Install python-dotenv if missing: - ```bash - pip install python-dotenv - ``` - -## Migration from Old Installation - -### **Existing Installations** - -For existing CyberPanel installations with hardcoded passwords: - -1. **Backup current configuration**: - ```bash - cp /usr/local/CyberCP/CyberCP/settings.py /usr/local/CyberCP/CyberCP/settings.py.backup - ``` - -2. **Generate new environment configuration**: - ```bash - cd /usr/local/CyberCP - python install/env_generator.py /usr/local/CyberCP - ``` - -3. **Update settings.py** (already done in new installations): - - The settings.py file now supports environment variables - - It will fallback to hardcoded values if .env is not available - -4. **Test the configuration**: - ```bash - cd /usr/local/CyberCP - python manage.py check - ``` - -## Support - -For issues with the secure installation: - -1. Check the installation logs -2. Verify file permissions -3. Ensure all dependencies are installed -4. Review the fallback installation method if needed - ---- - -**Security Notice**: This installation method significantly improves security by eliminating hardcoded credentials. Always ensure proper file permissions and secure handling of environment files. From 96226989b1d1d41a6a6c77be186b34cc7cf7cd85 Mon Sep 17 00:00:00 2001 From: Master3395 Date: Mon, 15 Sep 2025 02:01:11 +0200 Subject: [PATCH 03/11] Add user notification preferences and related API endpoints - Introduced a new model `UserNotificationPreferences` to store user-specific notification dismissal settings. - Added context processor to include notification preferences in templates. - Implemented API endpoints to dismiss backup and AI scanner notifications permanently. - Updated front-end logic to handle notification dismissal via server-side checks and API calls. - Enhanced documentation to include a new Custom CSS Guide for theme customization. --- CyberCP/settings.py | 1 + README.md | 23 +- baseTemplate/apps.py | 3 + baseTemplate/context_processors.py | 28 +- .../0002_usernotificationpreferences.py | 32 + baseTemplate/models.py | 18 +- baseTemplate/signals.py | 23 + .../templates/baseTemplate/index.html | 60 +- baseTemplate/urls.py | 3 + baseTemplate/views.py | 87 +- guides/CUSTOM_CSS_GUIDE.md | 829 ++++++++++++++++++ guides/INDEX.md | 12 +- run_migration.py | 28 + 13 files changed, 1122 insertions(+), 25 deletions(-) create mode 100644 baseTemplate/migrations/0002_usernotificationpreferences.py create mode 100644 baseTemplate/signals.py create mode 100644 guides/CUSTOM_CSS_GUIDE.md create mode 100644 run_migration.py diff --git a/CyberCP/settings.py b/CyberCP/settings.py index ad059c6a7..91d6b1353 100644 --- a/CyberCP/settings.py +++ b/CyberCP/settings.py @@ -107,6 +107,7 @@ TEMPLATES = [ 'django.contrib.messages.context_processors.messages', 'baseTemplate.context_processors.version_context', 'baseTemplate.context_processors.cosmetic_context', + 'baseTemplate.context_processors.notification_preferences_context', ], }, }, diff --git a/README.md b/README.md index 2cf210db0..814159cb3 100755 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ CyberPanel comes with comprehensive documentation and step-by-step guides: - 🐳 **[Docker Command Execution](guides/Docker_Command_Execution_Guide.md)** - Execute commands in Docker containers - 🤖 **[AI Scanner Setup](guides/AIScannerDocs.md)** - Configure AI-powered security scanning - 📧 **[Mautic Installation](guides/MAUTIC_INSTALLATION_GUIDE.md)** - Email marketing platform setup +- 🎨 **[Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md)** - Create custom themes for CyberPanel 2.5.5-dev --- @@ -109,7 +110,6 @@ Install CyberPanel easily with the following command: sh <(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh) ``` - --- ## 📊 Upgrading CyberPanel @@ -125,6 +125,7 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr ## 🆕 Recent Updates & Fixes ### **Bandwidth Reset Issue Fixed** (January 2025) + - **Issue**: Monthly bandwidth usage was not resetting, causing cumulative values to grow indefinitely - **Solution**: Implemented automatic monthly bandwidth reset for all websites and child domains - **Affected OS**: All supported operating systems (Ubuntu, AlmaLinux, RockyLinux, RHEL, CloudLinux, CentOS) @@ -132,6 +133,7 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr - **Documentation**: See [Bandwidth Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md) ### **New Operating System Support Added** (January 2025) + - **Ubuntu 24.04.3**: Full compatibility with latest Ubuntu LTS - **AlmaLinux 10**: Full compatibility with latest AlmaLinux release - **Long-term Support**: Both supported until 2029-2030 @@ -157,17 +159,19 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr - 🐳 [Docker Command Execution](guides/Docker_Command_Execution_Guide.md) - Execute commands in Docker containers - 🤖 [AI Scanner Setup](guides/AIScannerDocs.md) - Configure AI-powered security scanning - 📧 [Mautic Installation](guides/MAUTIC_INSTALLATION_GUIDE.md) - Email marketing platform setup +- 🎨 [Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md) - Create custom themes for CyberPanel 2.5.5+ - 📚 [All Guides Index](guides/INDEX.md) - Complete documentation hub ### 🔗 **Direct Guide Links** -| Feature | Guide | Description | -| ----------- | ---------------------------------------------------------- | ------------------------------ | -| 🐳 Docker | [Command Execution](guides/Docker_Command_Execution_Guide.md) | Execute commands in containers | -| 🤖 Security | [AI Scanner](guides/AIScannerDocs.md) | AI-powered security scanning | -| 📧 Email | [Mautic Setup](guides/MAUTIC_INSTALLATION_GUIDE.md) | Email marketing platform | -| 📊 Bandwidth | [Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md) | Fix bandwidth reset issues | -| 📚 All | [Complete Index](guides/INDEX.md) | Browse all available guides | +| Feature | Guide | Description | +| ------------ | ---------------------------------------------------------- | ---------------------------------- | +| 🐳 Docker | [Command Execution](guides/Docker_Command_Execution_Guide.md) | Execute commands in containers | +| 🤖 Security | [AI Scanner](guides/AIScannerDocs.md) | AI-powered security scanning | +| 📧 Email | [Mautic Setup](guides/MAUTIC_INSTALLATION_GUIDE.md) | Email marketing platform | +| 🎨 Design | [Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md) | Create custom themes for 2.5.5-dev | +| 📊 Bandwidth | [Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md) | Fix bandwidth reset issues | +| 📚 All | [Complete Index](guides/INDEX.md) | Browse all available guides | --- @@ -176,12 +180,13 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr ### **Common Issues & Solutions** #### **Bandwidth Not Resetting Monthly** + - **Issue**: Bandwidth usage shows cumulative values instead of monthly usage - **Solution**: Run the bandwidth reset script: `/usr/local/CyberCP/scripts/reset_bandwidth.sh` - **Prevention**: Ensure monthly cron job is running: `0 0 1 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py monthlyCleanup` - #### **General Support** + - Check logs: `/usr/local/lscp/logs/error.log` - Verify cron jobs: `crontab -l` - Test manual reset: Use provided scripts in `/usr/local/CyberCP/scripts/` diff --git a/baseTemplate/apps.py b/baseTemplate/apps.py index f5ce2e6d1..773d21648 100644 --- a/baseTemplate/apps.py +++ b/baseTemplate/apps.py @@ -6,3 +6,6 @@ from django.apps import AppConfig class BasetemplateConfig(AppConfig): name = 'baseTemplate' + + def ready(self): + import baseTemplate.signals \ No newline at end of file diff --git a/baseTemplate/context_processors.py b/baseTemplate/context_processors.py index 13d871c10..696f9d840 100644 --- a/baseTemplate/context_processors.py +++ b/baseTemplate/context_processors.py @@ -23,4 +23,30 @@ def cosmetic_context(request): cosmetic.save() return { 'cosmetic': cosmetic - } \ No newline at end of file + } + +def notification_preferences_context(request): + """Add user notification preferences to all templates""" + try: + if 'userID' in request.session: + from .models import UserNotificationPreferences + from loginSystem.models import Administrator + user = Administrator.objects.get(pk=request.session['userID']) + try: + preferences = UserNotificationPreferences.objects.get(user=user) + return { + 'backup_notification_dismissed': preferences.backup_notification_dismissed, + 'ai_scanner_notification_dismissed': preferences.ai_scanner_notification_dismissed + } + except UserNotificationPreferences.DoesNotExist: + return { + 'backup_notification_dismissed': False, + 'ai_scanner_notification_dismissed': False + } + except: + pass + + return { + 'backup_notification_dismissed': False, + 'ai_scanner_notification_dismissed': False + } \ No newline at end of file diff --git a/baseTemplate/migrations/0002_usernotificationpreferences.py b/baseTemplate/migrations/0002_usernotificationpreferences.py new file mode 100644 index 000000000..3db48ac84 --- /dev/null +++ b/baseTemplate/migrations/0002_usernotificationpreferences.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2024-01-01 00:00 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('baseTemplate', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='UserNotificationPreferences', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('backup_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the backup notification')), + ('ai_scanner_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the AI scanner notification')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='notification_preferences', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'User Notification Preferences', + 'verbose_name_plural': 'User Notification Preferences', + }, + ), + ] diff --git a/baseTemplate/models.py b/baseTemplate/models.py index 3ced4ced5..b02b2ca38 100644 --- a/baseTemplate/models.py +++ b/baseTemplate/models.py @@ -2,6 +2,7 @@ from django.db import models +from django.contrib.auth.models import User # Create your models here. @@ -11,4 +12,19 @@ class version(models.Model): build = models.IntegerField() class CyberPanelCosmetic(models.Model): - MainDashboardCSS = models.TextField(default='') \ No newline at end of file + MainDashboardCSS = models.TextField(default='') + +class UserNotificationPreferences(models.Model): + """Model to store user notification dismissal preferences""" + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='notification_preferences') + backup_notification_dismissed = models.BooleanField(default=False, help_text="Whether user has dismissed the backup notification") + ai_scanner_notification_dismissed = models.BooleanField(default=False, help_text="Whether user has dismissed the AI scanner notification") + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + verbose_name = "User Notification Preferences" + verbose_name_plural = "User Notification Preferences" + + def __str__(self): + return f"Notification Preferences for {self.user.username}" \ No newline at end of file diff --git a/baseTemplate/signals.py b/baseTemplate/signals.py new file mode 100644 index 000000000..6c7b22340 --- /dev/null +++ b/baseTemplate/signals.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.contrib.auth.models import User +from .models import UserNotificationPreferences + + +@receiver(post_save, sender=User) +def create_user_notification_preferences(sender, instance, created, **kwargs): + """Create default notification preferences when a new user is created""" + if created: + UserNotificationPreferences.objects.create( + user=instance, + backup_notification_dismissed=False, + ai_scanner_notification_dismissed=False + ) + + +@receiver(post_save, sender=User) +def save_user_notification_preferences(sender, instance, **kwargs): + """Save notification preferences when user is saved""" + if hasattr(instance, 'notification_preferences'): + instance.notification_preferences.save() diff --git a/baseTemplate/templates/baseTemplate/index.html b/baseTemplate/templates/baseTemplate/index.html index 3a6986ddd..742a8f49f 100644 --- a/baseTemplate/templates/baseTemplate/index.html +++ b/baseTemplate/templates/baseTemplate/index.html @@ -1841,10 +1841,10 @@ // Backup notification banner logic function checkBackupStatus() { - // Check if user has dismissed the notification in this session - if (sessionStorage.getItem('backupNotificationDismissed') === 'true') { - return; - } + // Check if user has dismissed the notification permanently (from server-side context) + {% if backup_notification_dismissed %} + return; // Notification already dismissed permanently + {% endif %} // Check if user has backup configured (you'll need to implement this API) // For now, we'll show it by default unless they have a backup plan @@ -1871,16 +1871,34 @@ const body = document.body; banner.classList.remove('show'); body.classList.remove('notification-shown'); - // Remember dismissal for this session - sessionStorage.setItem('backupNotificationDismissed', 'true'); + + // Dismiss permanently via API + fetch('/base/dismiss_backup_notification', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': getCookie('csrftoken') + } + }) + .then(response => response.json()) + .then(data => { + if (data.status === 1) { + console.log('Backup notification dismissed permanently'); + } else { + console.error('Failed to dismiss backup notification:', data.error); + } + }) + .catch(error => { + console.error('Error dismissing backup notification:', error); + }); } // AI Scanner Notification Functions function checkAIScannerStatus() { - // Check if user has dismissed the notification in this session - if (sessionStorage.getItem('aiScannerNotificationDismissed') === 'true') { - return; - } + // Check if user has dismissed the notification permanently (from server-side context) + {% if ai_scanner_notification_dismissed %} + return; // Notification already dismissed permanently + {% endif %} // Check if we're not already on the AI Scanner page if (!window.location.href.includes('aiscanner')) { @@ -1900,8 +1918,26 @@ const body = document.body; banner.classList.remove('show'); body.classList.remove('ai-scanner-shown'); - // Remember dismissal for this session - sessionStorage.setItem('aiScannerNotificationDismissed', 'true'); + + // Dismiss permanently via API + fetch('/base/dismiss_ai_scanner_notification', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': getCookie('csrftoken') + } + }) + .then(response => response.json()) + .then(data => { + if (data.status === 1) { + console.log('AI scanner notification dismissed permanently'); + } else { + console.error('Failed to dismiss AI scanner notification:', data.error); + } + }) + .catch(error => { + console.error('Error dismissing AI scanner notification:', error); + }); } // Check both notification statuses when page loads diff --git a/baseTemplate/urls.py b/baseTemplate/urls.py index 6ebdc293b..c6190ea66 100644 --- a/baseTemplate/urls.py +++ b/baseTemplate/urls.py @@ -24,4 +24,7 @@ urlpatterns = [ re_path(r'^getSSHUserActivity$', views.getSSHUserActivity, name='getSSHUserActivity'), re_path(r'^getTopProcesses$', views.getTopProcesses, name='getTopProcesses'), re_path(r'^analyzeSSHSecurity$', views.analyzeSSHSecurity, name='analyzeSSHSecurity'), + re_path(r'^dismiss_backup_notification$', views.dismiss_backup_notification, name='dismiss_backup_notification'), + re_path(r'^dismiss_ai_scanner_notification$', views.dismiss_ai_scanner_notification, name='dismiss_ai_scanner_notification'), + re_path(r'^get_notification_preferences$', views.get_notification_preferences, name='get_notification_preferences'), ] diff --git a/baseTemplate/views.py b/baseTemplate/views.py index 4e4bfef57..22adee65d 100644 --- a/baseTemplate/views.py +++ b/baseTemplate/views.py @@ -6,7 +6,7 @@ from django.http import HttpResponse from plogical.getSystemInformation import SystemInformation import json from loginSystem.views import loadLoginPage -from .models import version +from .models import version, UserNotificationPreferences import requests import subprocess import shlex @@ -1305,3 +1305,88 @@ def getTopProcesses(request): except Exception as e: return HttpResponse(json.dumps({'error': str(e)}), content_type='application/json', status=500) + +@csrf_exempt +@require_POST +def dismiss_backup_notification(request): + """API endpoint to permanently dismiss the backup notification for the current user""" + try: + user_id = request.session.get('userID') + if not user_id: + return HttpResponse(json.dumps({'status': 0, 'error': 'Not logged in'}), content_type='application/json', status=403) + + # Get or create user notification preferences + user = Administrator.objects.get(pk=user_id) + preferences, created = UserNotificationPreferences.objects.get_or_create( + user=user, + defaults={ + 'backup_notification_dismissed': False, + 'ai_scanner_notification_dismissed': False + } + ) + + # Mark backup notification as dismissed + preferences.backup_notification_dismissed = True + preferences.save() + + return HttpResponse(json.dumps({'status': 1, 'message': 'Backup notification dismissed permanently'}), content_type='application/json') + + except Exception as e: + return HttpResponse(json.dumps({'status': 0, 'error': str(e)}), content_type='application/json', status=500) + +@csrf_exempt +@require_POST +def dismiss_ai_scanner_notification(request): + """API endpoint to permanently dismiss the AI scanner notification for the current user""" + try: + user_id = request.session.get('userID') + if not user_id: + return HttpResponse(json.dumps({'status': 0, 'error': 'Not logged in'}), content_type='application/json', status=403) + + # Get or create user notification preferences + user = Administrator.objects.get(pk=user_id) + preferences, created = UserNotificationPreferences.objects.get_or_create( + user=user, + defaults={ + 'backup_notification_dismissed': False, + 'ai_scanner_notification_dismissed': False + } + ) + + # Mark AI scanner notification as dismissed + preferences.ai_scanner_notification_dismissed = True + preferences.save() + + return HttpResponse(json.dumps({'status': 1, 'message': 'AI scanner notification dismissed permanently'}), content_type='application/json') + + except Exception as e: + return HttpResponse(json.dumps({'status': 0, 'error': str(e)}), content_type='application/json', status=500) + +@csrf_exempt +@require_GET +def get_notification_preferences(request): + """API endpoint to get current user's notification preferences""" + try: + user_id = request.session.get('userID') + if not user_id: + return HttpResponse(json.dumps({'status': 0, 'error': 'Not logged in'}), content_type='application/json', status=403) + + # Get user notification preferences + user = Administrator.objects.get(pk=user_id) + try: + preferences = UserNotificationPreferences.objects.get(user=user) + return HttpResponse(json.dumps({ + 'status': 1, + 'backup_notification_dismissed': preferences.backup_notification_dismissed, + 'ai_scanner_notification_dismissed': preferences.ai_scanner_notification_dismissed + }), content_type='application/json') + except UserNotificationPreferences.DoesNotExist: + # Return default values if preferences don't exist yet + return HttpResponse(json.dumps({ + 'status': 1, + 'backup_notification_dismissed': False, + 'ai_scanner_notification_dismissed': False + }), content_type='application/json') + + except Exception as e: + return HttpResponse(json.dumps({'status': 0, 'error': str(e)}), content_type='application/json', status=500) diff --git a/guides/CUSTOM_CSS_GUIDE.md b/guides/CUSTOM_CSS_GUIDE.md new file mode 100644 index 000000000..cc5d6f969 --- /dev/null +++ b/guides/CUSTOM_CSS_GUIDE.md @@ -0,0 +1,829 @@ +# CyberPanel 2.5.5-dev Custom CSS Guide + +A comprehensive guide for creating custom CSS that fully works with the new design system of CyberPanel 2.5.5-dev. + +## Table of Contents + +1. [Overview](#overview) +2. [Design System Architecture](#design-system-architecture) +3. [CSS Variables Reference](#css-variables-reference) +4. [Component Structure](#component-structure) +5. [Customization Examples](#customization-examples) +6. [Best Practices](#best-practices) +7. [Troubleshooting](#troubleshooting) +8. [Advanced Techniques](#advanced-techniques) + +## Overview + +CyberPanel 2.5.5-dev features a modern, CSS-variable-based design system that supports both light and dark themes. The system is built with: + +- **CSS Custom Properties (Variables)** for consistent theming +- **Modern CSS Grid and Flexbox** layouts +- **Responsive design** principles +- **Dark mode support** with automatic theme switching +- **Component-based architecture** for easy customization + +## Design System Architecture + +### Core Structure + +The design system is built around CSS custom properties defined in `:root` and `[data-theme="dark"]` selectors: + +```css +:root { + /* Light Theme Variables */ + --bg-primary: #f0f0ff; + --bg-secondary: white; + --text-primary: #2f3640; + --accent-color: #5856d6; + /* ... more variables */ +} + +[data-theme="dark"] { + /* Dark Theme Variables */ + --bg-primary: #0f0f23; + --bg-secondary: #1a1a3e; + --text-primary: #e4e4e7; + --accent-color: #7c7ff3; + /* ... more variables */ +} +``` + +### Key Components + +1. **Header** (`#header`) - Top navigation bar +2. **Sidebar** (`#sidebar`) - Left navigation panel +3. **Main Content** (`#main-content`) - Page content area +4. **Cards** (`.content-card`) - Content containers +5. **Buttons** (`.btn`) - Interactive elements +6. **Forms** (`.form-*`) - Input elements + +## CSS Variables Reference + +### Color Variables + +#### Background Colors +```css +--bg-primary /* Main background color */ +--bg-secondary /* Card/container background */ +--bg-sidebar /* Sidebar background */ +--bg-sidebar-item /* Sidebar menu item background */ +--bg-hover /* Hover state background */ +``` + +#### Text Colors +```css +--text-primary /* Main text color */ +--text-secondary /* Secondary text color */ +--text-heading /* Heading text color */ +``` + +#### Accent Colors +```css +--accent-color /* Primary accent color */ +--accent-hover /* Accent hover state */ +--danger-color /* Error/danger color */ +--success-color /* Success color */ +``` + +#### Border & Shadow +```css +--border-color /* Default border color */ +--shadow-color /* Default shadow color */ +``` + +### Special Variables + +#### Gradients +```css +--warning-bg /* Warning banner gradient */ +--ai-banner-bg /* AI scanner banner gradient */ +``` + +#### Status Colors +```css +--success-bg /* Success background */ +--success-border /* Success border */ +--danger-bg /* Danger background */ +--danger-border /* Danger border */ +--warning-bg /* Warning background */ +--info-bg /* Info background */ +``` + +## Component Structure + +### Header Component + +```css +#header { + background: var(--bg-secondary); + height: 80px; + display: flex; + align-items: center; + padding: 0 30px; + box-shadow: 0 2px 12px var(--shadow-color); + position: fixed; + top: 0; + left: 260px; + right: 0; + z-index: 1000; +} +``` + +**Customization Example:** +```css +/* Change header height and add custom styling */ +#header { + height: 100px; + background: linear-gradient(135deg, var(--accent-color), var(--accent-hover)); + border-bottom: 3px solid var(--accent-color); +} + +#header .logo-text .brand { + font-size: 32px; + text-shadow: 0 2px 4px rgba(0,0,0,0.1); +} +``` + +### Sidebar Component + +```css +#sidebar { + width: 260px; + background: var(--bg-sidebar); + height: 100vh; + position: fixed; + left: 0; + top: 0; + overflow-y: auto; + z-index: 1001; +} +``` + +**Customization Example:** +```css +/* Make sidebar wider with custom styling */ +#sidebar { + width: 300px; + background: linear-gradient(180deg, var(--bg-sidebar), var(--bg-secondary)); + border-right: 2px solid var(--accent-color); +} + +#sidebar .menu-item { + margin: 4px 20px; + border-radius: 12px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +#sidebar .menu-item:hover { + transform: translateX(5px); + box-shadow: 0 4px 12px var(--shadow-color); +} +``` + +### Content Cards + +```css +.content-card { + background: var(--bg-secondary); + border-radius: 12px; + padding: 30px; + box-shadow: 0 2px 8px var(--shadow-color); + border: 1px solid var(--border-color); + margin-bottom: 25px; +} +``` + +**Customization Example:** +```css +/* Add glassmorphism effect to cards */ +.content-card { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); +} + +[data-theme="dark"] .content-card { + background: rgba(26, 26, 62, 0.3); + border: 1px solid rgba(255, 255, 255, 0.1); +} +``` + +## Customization Examples + +### 1. Complete Theme Override + +```css +/* Custom Purple Theme */ +:root { + --bg-primary: #f8f4ff; + --bg-secondary: #ffffff; + --bg-sidebar: #f3f0ff; + --bg-hover: #e8e0ff; + --text-primary: #2d1b69; + --text-secondary: #6b46c1; + --accent-color: #8b5cf6; + --accent-hover: #7c3aed; + --border-color: #e0d7ff; + --shadow-color: rgba(139, 92, 246, 0.1); +} + +[data-theme="dark"] { + --bg-primary: #1a0b2e; + --bg-secondary: #2d1b69; + --bg-sidebar: #1e0a3e; + --bg-hover: #3d2a7a; + --text-primary: #f3f0ff; + --text-secondary: #c4b5fd; + --accent-color: #a78bfa; + --accent-hover: #8b5cf6; + --border-color: #4c1d95; + --shadow-color: rgba(139, 92, 246, 0.3); +} +``` + +### 2. Custom Button Styles + +```css +/* Custom button variants */ +.btn-custom { + background: linear-gradient(135deg, var(--accent-color), var(--accent-hover)); + border: none; + border-radius: 20px; + padding: 12px 24px; + color: white; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3); + transition: all 0.3s ease; +} + +.btn-custom:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(139, 92, 246, 0.4); +} + +.btn-custom:active { + transform: translateY(0); +} +``` + +### 3. Custom Sidebar Menu Items + +```css +/* Animated sidebar menu items */ +#sidebar .menu-item { + position: relative; + overflow: hidden; +} + +#sidebar .menu-item::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); + transition: left 0.5s; +} + +#sidebar .menu-item:hover::before { + left: 100%; +} + +#sidebar .menu-item .icon-wrapper { + position: relative; + z-index: 1; +} +``` + +### 4. Custom Form Styling + +```css +/* Modern form inputs */ +.form-control { + border: 2px solid var(--border-color); + border-radius: 12px; + padding: 12px 16px; + font-size: 14px; + transition: all 0.3s ease; + background: var(--bg-secondary); +} + +.form-control:focus { + border-color: var(--accent-color); + box-shadow: 0 0 0 4px rgba(139, 92, 246, 0.1); + transform: translateY(-1px); +} + +.form-control::placeholder { + color: var(--text-secondary); + opacity: 0.7; +} +``` + +### 5. Custom Notifications + +```css +/* Custom notification banners */ +.notification-banner { + background: linear-gradient(135deg, var(--accent-color), var(--accent-hover)); + border-radius: 16px; + padding: 20px; + margin: 20px; + box-shadow: 0 8px 32px rgba(139, 92, 246, 0.3); + position: relative; + overflow: hidden; +} + +.notification-banner::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4); + animation: rainbow 3s linear infinite; +} + +@keyframes rainbow { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} +``` + +## Best Practices + +### 1. Use CSS Variables + +Always use CSS variables instead of hardcoded values: + +```css +/* ✅ Good */ +.custom-element { + background: var(--bg-secondary); + color: var(--text-primary); + border: 1px solid var(--border-color); +} + +/* ❌ Bad */ +.custom-element { + background: white; + color: #2f3640; + border: 1px solid #e8e9ff; +} +``` + +### 2. Support Both Themes + +Always provide both light and dark theme support: + +```css +.custom-element { + background: var(--bg-secondary); + color: var(--text-primary); +} + +/* Dark theme specific adjustments */ +[data-theme="dark"] .custom-element { + /* Additional dark theme styling if needed */ +} +``` + +### 3. Use Semantic Class Names + +```css +/* ✅ Good */ +.primary-button { } +.content-container { } +.navigation-item { } + +/* ❌ Bad */ +.red-button { } +.big-box { } +.item1 { } +``` + +### 4. Maintain Responsive Design + +```css +.custom-element { + padding: 20px; + font-size: 16px; +} + +@media (max-width: 768px) { + .custom-element { + padding: 15px; + font-size: 14px; + } +} +``` + +### 5. Use Modern CSS Features + +```css +.custom-element { + /* Use CSS Grid for layouts */ + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + + /* Use Flexbox for alignment */ + align-items: center; + justify-content: space-between; + + /* Use CSS custom properties for calculations */ + --element-height: 60px; + height: var(--element-height); + + /* Use modern selectors */ + &:hover { } + &:focus-within { } +} +``` + +## Troubleshooting + +### Common Issues + +#### 1. Custom CSS Not Applying + +**Problem:** Custom CSS doesn't appear to be working. + +**Solution:** +- Check CSS specificity - use more specific selectors +- Ensure CSS is placed after the base styles +- Use `!important` sparingly and only when necessary + +```css +/* Increase specificity */ +#main-content .content-card .custom-element { + background: var(--bg-secondary); +} +``` + +#### 2. Dark Mode Not Working + +**Problem:** Custom styles don't adapt to dark mode. + +**Solution:** +- Always use CSS variables +- Test both light and dark themes +- Provide dark mode specific overrides when needed + +```css +.custom-element { + background: var(--bg-secondary); + color: var(--text-primary); +} + +[data-theme="dark"] .custom-element { + /* Dark mode specific adjustments */ + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} +``` + +#### 3. Responsive Issues + +**Problem:** Custom styles break on mobile devices. + +**Solution:** +- Use relative units (rem, em, %) +- Test on different screen sizes +- Use CSS Grid and Flexbox for responsive layouts + +```css +.custom-element { + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 1rem; +} + +@media (max-width: 768px) { + .custom-element { + padding: 0.5rem; + } +} +``` + +## Advanced Techniques + +### 1. CSS Animations + +```css +/* Smooth page transitions */ +.page-transition { + animation: fadeIn 0.3s ease-in-out; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +/* Hover animations */ +.interactive-element { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.interactive-element:hover { + transform: translateY(-2px) scale(1.02); + box-shadow: 0 8px 25px var(--shadow-color); +} +``` + +### 2. CSS Grid Layouts + +```css +/* Advanced grid layouts */ +.dashboard-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; + padding: 20px; +} + +.dashboard-card { + grid-column: span 1; + background: var(--bg-secondary); + border-radius: 12px; + padding: 20px; + box-shadow: 0 2px 8px var(--shadow-color); +} + +.dashboard-card.featured { + grid-column: span 2; +} + +@media (max-width: 768px) { + .dashboard-card.featured { + grid-column: span 1; + } +} +``` + +### 3. CSS Custom Properties with JavaScript + +```css +/* Dynamic theming with CSS variables */ +:root { + --custom-accent: #5856d6; + --custom-accent-hover: #4644c0; +} + +.custom-theme { + --accent-color: var(--custom-accent); + --accent-hover: var(--custom-accent-hover); +} +``` + +### 4. Advanced Selectors + +```css +/* Complex selectors for specific styling */ +#sidebar .menu-item:not(.active):hover { + background: var(--bg-hover); + transform: translateX(5px); +} + +.content-card > *:first-child { + margin-top: 0; +} + +.content-card > *:last-child { + margin-bottom: 0; +} + +/* Attribute selectors */ +[data-status="success"] { + border-left: 4px solid var(--success-color); +} + +[data-status="error"] { + border-left: 4px solid var(--danger-color); +} +``` + +### 5. CSS Functions and Calculations + +```css +/* Using CSS functions */ +.responsive-text { + font-size: clamp(14px, 2.5vw, 18px); + line-height: calc(1.5em + 0.5vw); +} + +.dynamic-spacing { + padding: calc(1rem + 2vw); + margin: calc(0.5rem + 1vw); +} + +/* CSS custom properties with calculations */ +:root { + --base-size: 16px; + --scale-factor: 1.2; + --large-size: calc(var(--base-size) * var(--scale-factor)); +} +``` + +## Implementation Guide + +### Step 1: Access the Design Page + +1. Log into CyberPanel +2. Navigate to **Design** in the sidebar +3. Scroll down to the **Custom CSS** section + +### Step 2: Add Your Custom CSS + +1. Paste your custom CSS into the textarea +2. Click **Save Changes** +3. Refresh the page to see your changes + +### Step 3: Test Your Changes + +1. Test in both light and dark modes +2. Test on different screen sizes +3. Verify all interactive elements work correctly + +### Step 4: Iterate and Refine + +1. Make adjustments as needed +2. Test thoroughly before finalizing +3. Document your customizations + +## Example: Complete Custom Theme + +Here's a complete example of a custom theme that you can use as a starting point: + +```css +/* Custom CyberPanel Theme - Ocean Blue */ + +/* Light Theme */ +:root { + --bg-primary: #f0f9ff; + --bg-secondary: #ffffff; + --bg-sidebar: #e0f2fe; + --bg-sidebar-item: #ffffff; + --bg-hover: #bae6fd; + --text-primary: #0c4a6e; + --text-secondary: #0369a1; + --text-heading: #0c4a6e; + --border-color: #bae6fd; + --shadow-color: rgba(6, 105, 161, 0.1); + --accent-color: #0284c7; + --accent-hover: #0369a1; + --danger-color: #dc2626; + --success-color: #059669; + --warning-bg: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); + --ai-banner-bg: linear-gradient(135deg, #0284c7 0%, #0369a1 50%, #0c4a6e 100%); +} + +/* Dark Theme */ +[data-theme="dark"] { + --bg-primary: #0c4a6e; + --bg-secondary: #075985; + --bg-sidebar: #0c4a6e; + --bg-sidebar-item: #075985; + --bg-hover: #0369a1; + --text-primary: #e0f2fe; + --text-secondary: #bae6fd; + --text-heading: #f0f9ff; + --border-color: #0369a1; + --shadow-color: rgba(0, 0, 0, 0.3); + --accent-color: #38bdf8; + --accent-hover: #0ea5e9; + --danger-color: #f87171; + --success-color: #34d399; + --warning-bg: linear-gradient(135deg, #78350f 0%, #92400e 100%); + --ai-banner-bg: linear-gradient(135deg, #0c4a6e 0%, #075985 50%, #0369a1 100%); +} + +/* Custom Header Styling */ +#header { + background: linear-gradient(135deg, var(--accent-color), var(--accent-hover)); + box-shadow: 0 4px 20px var(--shadow-color); +} + +#header .logo-text .brand { + color: white; + text-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +/* Custom Sidebar Styling */ +#sidebar { + background: linear-gradient(180deg, var(--bg-sidebar), var(--bg-secondary)); + border-right: 3px solid var(--accent-color); +} + +#sidebar .menu-item { + border-radius: 12px; + margin: 4px 20px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +#sidebar .menu-item:hover { + transform: translateX(8px); + box-shadow: 0 4px 15px var(--shadow-color); +} + +#sidebar .menu-item.active { + background: linear-gradient(135deg, var(--accent-color), var(--accent-hover)); + box-shadow: 0 4px 15px rgba(2, 132, 199, 0.3); +} + +/* Custom Content Cards */ +.content-card { + background: var(--bg-secondary); + border-radius: 16px; + box-shadow: 0 4px 20px var(--shadow-color); + border: 1px solid var(--border-color); + position: relative; + overflow: hidden; +} + +.content-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, var(--accent-color), var(--accent-hover)); +} + +/* Custom Buttons */ +.btn { + border-radius: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.btn-primary { + background: linear-gradient(135deg, var(--accent-color), var(--accent-hover)); + box-shadow: 0 4px 15px rgba(2, 132, 199, 0.3); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(2, 132, 199, 0.4); +} + +/* Custom Form Elements */ +.form-control { + border: 2px solid var(--border-color); + border-radius: 12px; + transition: all 0.3s ease; +} + +.form-control:focus { + border-color: var(--accent-color); + box-shadow: 0 0 0 4px rgba(2, 132, 199, 0.1); + transform: translateY(-1px); +} + +/* Custom Animations */ +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.content-card { + animation: slideIn 0.5s ease-out; +} + +/* Responsive Design */ +@media (max-width: 768px) { + #sidebar { + width: 100%; + height: auto; + position: relative; + } + + #header { + left: 0; + } + + .content-card { + margin: 10px; + padding: 20px; + } +} +``` + +This guide provides everything you need to create beautiful, functional custom CSS for CyberPanel 2.5.5+. Remember to always test your changes thoroughly and use the CSS variables for consistency across themes. diff --git a/guides/INDEX.md b/guides/INDEX.md index 6df7c3900..d31a90f61 100644 --- a/guides/INDEX.md +++ b/guides/INDEX.md @@ -14,6 +14,9 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu ### 📧 Email & Marketing - **[Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)** - Step-by-step guide for installing and configuring Mautic email marketing platform +### 🎨 Customization & Design +- **[Custom CSS Guide](CUSTOM_CSS_GUIDE.md)** - Complete guide for creating custom CSS that works with CyberPanel 2.5.5-dev design system + ### 📖 General Documentation - **[README](../README.md)** - Main CyberPanel documentation with installation instructions and feature overview - **[Contributing Guide](CONTRIBUTING.md)** - Guidelines for contributing to the CyberPanel project @@ -23,7 +26,8 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu 1. **New to CyberPanel?** Start with the [README](../README.md) for installation and basic setup 2. **Need Docker help?** Check the [Docker Command Execution Guide](Docker_Command_Execution_Guide.md) 3. **Setting up email marketing?** Follow the [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md) -4. **Want to contribute?** Read the [Contributing Guide](CONTRIBUTING.md) +4. **Want to customize the interface?** Check the [Custom CSS Guide](CUSTOM_CSS_GUIDE.md) +5. **Want to contribute?** Read the [Contributing Guide](CONTRIBUTING.md) ## 🔍 Finding What You Need @@ -31,6 +35,7 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu - **Docker Features**: [Docker Command Execution Guide](Docker_Command_Execution_Guide.md) - **Security Features**: [AI Scanner Documentation](AIScannerDocs.md) - **Email Marketing**: [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md) +- **Customization & Design**: [Custom CSS Guide](CUSTOM_CSS_GUIDE.md) - **Development**: [Contributing Guide](CONTRIBUTING.md) ## 📝 Guide Categories @@ -45,6 +50,11 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu - Third-party applications - Custom configurations +### 🎨 **Customization** +- Custom CSS theming +- Design system integration +- Interface personalization + ### 📖 **Documentation** - Installation guides - Configuration tutorials diff --git a/run_migration.py b/run_migration.py new file mode 100644 index 000000000..913a3473f --- /dev/null +++ b/run_migration.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Migration script for UserNotificationPreferences model +Run this script to apply the database migration for notification preferences +""" + +import os +import sys +import django + +# Add the project directory to Python path +sys.path.append('/usr/local/CyberCP') + +# Set up Django environment +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'CyberCP.settings') +django.setup() + +from django.core.management import execute_from_command_line + +if __name__ == '__main__': + print("Running migration for UserNotificationPreferences...") + try: + execute_from_command_line(['manage.py', 'migrate', 'baseTemplate']) + print("Migration completed successfully!") + except Exception as e: + print(f"Migration failed: {e}") + sys.exit(1) From 813bd2e4f95503c789d77bd43e9195bed286b95e Mon Sep 17 00:00:00 2001 From: Master3395 Date: Mon, 15 Sep 2025 02:12:27 +0200 Subject: [PATCH 04/11] Add CyberPanel port 7080 rule to firewall database for visibility - Implemented a check to ensure the CyberPanel port 7080 rule exists in the database. - Added functionality to create the rule if it does not exist, including error handling and logging. - Updated the rules list after potential creation to reflect changes in the database. --- firewall/firewallManager.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/firewall/firewallManager.py b/firewall/firewallManager.py index e9cf462de..5375343f8 100644 --- a/firewall/firewallManager.py +++ b/firewall/firewallManager.py @@ -60,6 +60,30 @@ class FirewallManager: rules = FirewallRules.objects.all() + # Ensure CyberPanel port 7080 rule exists in database for visibility + cyberpanel_rule_exists = False + for rule in rules: + if rule.port == '7080': + cyberpanel_rule_exists = True + break + + if not cyberpanel_rule_exists: + # Create database entry for port 7080 (already enabled in system firewall) + try: + cyberpanel_rule = FirewallRules( + name="CyberPanel Admin", + proto="tcp", + port="7080", + ipAddress="0.0.0.0/0" + ) + cyberpanel_rule.save() + logging.CyberCPLogFileWriter.writeToFile("Added CyberPanel port 7080 to firewall database for UI visibility") + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to add CyberPanel port 7080 to database: {str(e)}") + + # Refresh rules after potential creation + rules = FirewallRules.objects.all() + json_data = "[" checker = 0 @@ -96,7 +120,6 @@ class FirewallManager: else: return ACLManager.loadErrorJson('add_status', 0) - ruleName = data['ruleName'] ruleProtocol = data['ruleProtocol'] rulePort = data['rulePort'] @@ -126,7 +149,6 @@ class FirewallManager: else: return ACLManager.loadErrorJson('delete_status', 0) - ruleID = data['id'] ruleProtocol = data['proto'] rulePort = data['port'] From e2f06058c675c37d058d0d27751f865ef010aa78 Mon Sep 17 00:00:00 2001 From: Master3395 Date: Mon, 15 Sep 2025 21:34:39 +0200 Subject: [PATCH 05/11] Enhance OS compatibility and documentation for CyberPanel - Added support for Debian 11, 12, and 13 in the OS detection logic within cyberpanel.sh. - Updated README.md to include Debian versions with their respective support timelines. - Introduced a new Debian 13 Installation Guide in the documentation. - Revised OS compatibility details in the testPlugin documentation to reflect full support for Debian versions. --- README.md | 11 +- cyberpanel.sh | 9 +- guides/DEBIAN_13_INSTALLATION_GUIDE.md | 327 +++++++++++++++++++ guides/INDEX.md | 13 +- testPlugin/OS_COMPATIBILITY.md | 8 +- test_debian13_support.sh | 430 +++++++++++++++++++++++++ 6 files changed, 785 insertions(+), 13 deletions(-) create mode 100644 guides/DEBIAN_13_INSTALLATION_GUIDE.md create mode 100644 test_debian13_support.sh diff --git a/README.md b/README.md index 814159cb3..cbe86b132 100755 --- a/README.md +++ b/README.md @@ -80,6 +80,9 @@ CyberPanel runs on x86_64 architecture and supports the following operating syst - **Ubuntu 24.04.3** - Supported until April 2029 ⭐ **NEW!** - **Ubuntu 22.04** - Supported until April 2027 - **Ubuntu 20.04** - Supported until April 2025 +- **Debian 13** - Supported until 2029 ⭐ **NEW!** +- **Debian 12** - Supported until 2027 +- **Debian 11** - Supported until 2026 - **AlmaLinux 10** - Supported until May 2030 ⭐ **NEW!** - **AlmaLinux 9** - Supported until May 2032 - **AlmaLinux 8** - Supported until May 2029 @@ -94,7 +97,6 @@ CyberPanel runs on x86_64 architecture and supports the following operating syst Additional operating systems may be supported through third-party repositories or community efforts: -- **Debian** - May work with Ubuntu-compatible packages - **openEuler** - Community-supported with limited testing - **Other RHEL derivatives** - May work with AlmaLinux/RockyLinux packages @@ -124,7 +126,7 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr ## 🆕 Recent Updates & Fixes -### **Bandwidth Reset Issue Fixed** (January 2025) +### **Bandwidth Reset Issue Fixed** (September 2025) - **Issue**: Monthly bandwidth usage was not resetting, causing cumulative values to grow indefinitely - **Solution**: Implemented automatic monthly bandwidth reset for all websites and child domains @@ -132,11 +134,12 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr - **Manual Reset**: Use `/usr/local/CyberCP/scripts/reset_bandwidth.sh` for immediate reset - **Documentation**: See [Bandwidth Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md) -### **New Operating System Support Added** (January 2025) +### **New Operating System Support Added** (September 2025) - **Ubuntu 24.04.3**: Full compatibility with latest Ubuntu LTS +- **Debian 13**: Full compatibility with latest Debian stable release - **AlmaLinux 10**: Full compatibility with latest AlmaLinux release -- **Long-term Support**: Both supported until 2029-2030 +- **Long-term Support**: All supported until 2029-2030 --- diff --git a/cyberpanel.sh b/cyberpanel.sh index 3271f7818..b3c988a92 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -549,12 +549,14 @@ elif grep -q -E "Rocky Linux" /etc/os-release ; then Server_OS="RockyLinux" elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04|Ubuntu 24.04" /etc/os-release ; then Server_OS="Ubuntu" +elif grep -q -E "Debian GNU/Linux 11|Debian GNU/Linux 12|Debian GNU/Linux 13" /etc/os-release ; then + Server_OS="Debian" elif grep -q -E "openEuler 20.03|openEuler 22.03" /etc/os-release ; then Server_OS="openEuler" else echo -e "Unable to detect your system..." - echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n" - Debug_Log2 "CyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03... [404]" + echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, Debian 11, Debian 12, Debian 13, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n" + Debug_Log2 "CyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, Debian 11, Debian 12, Debian 13, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03... [404]" exit fi @@ -568,6 +570,9 @@ if [[ $Server_OS = "CloudLinux" ]] || [[ "$Server_OS" = "AlmaLinux" ]] || [[ "$S Server_OS="CentOS" #CloudLinux gives version id like 7.8, 7.9, so cut it to show first number only #treat CloudLinux, Rocky and Alma as CentOS +elif [[ "$Server_OS" = "Debian" ]] ; then + Server_OS="Ubuntu" + #Treat Debian as Ubuntu for package management (both use apt-get) fi if [[ "$Debug" = "On" ]] ; then diff --git a/guides/DEBIAN_13_INSTALLATION_GUIDE.md b/guides/DEBIAN_13_INSTALLATION_GUIDE.md new file mode 100644 index 000000000..f714ac6ff --- /dev/null +++ b/guides/DEBIAN_13_INSTALLATION_GUIDE.md @@ -0,0 +1,327 @@ +# Debian 13 Installation Guide for CyberPanel + +## 🎯 Overview + +This guide provides step-by-step instructions for installing CyberPanel on Debian 13 (Bookworm). Debian 13 support has been added to CyberPanel with full compatibility for package management, service configuration, and web server setup. + +## 📋 Prerequisites + +### System Requirements +- **OS**: Debian 13 (Bookworm) x86_64 +- **RAM**: Minimum 1GB (2GB+ recommended) +- **Storage**: Minimum 10GB free space (20GB+ recommended) +- **CPU**: 2+ cores recommended +- **Network**: Internet connection required + +### Supported Debian Versions +- ✅ **Debian 13** (Bookworm) - Full Support +- ✅ **Debian 12** (Bookworm) - Full Support +- ✅ **Debian 11** (Bullseye) - Full Support + +## 🚀 Installation Steps + +### Step 1: Update System + +```bash +# Update package lists +sudo apt update + +# Upgrade system packages +sudo apt upgrade -y + +# Install essential packages +sudo apt install -y curl wget git +``` + +### Step 2: Download and Run CyberPanel Installer + +```bash +# Download the latest CyberPanel installer +wget https://cyberpanel.sh/install.sh + +# Make the installer executable +chmod +x install.sh + +# Run the installer +sudo ./install.sh +``` + +### Step 3: Follow Installation Prompts + +The installer will guide you through: + +1. **License Agreement**: Accept the terms +2. **Installation Type**: Choose between: + - OpenLiteSpeed (Free) + - LiteSpeed Enterprise (Requires license) +3. **MySQL Configuration**: + - Single MySQL instance (recommended) + - Double MySQL instance (for high availability) +4. **Additional Services**: + - Postfix/Dovecot (Email server) + - PowerDNS (DNS server) + - PureFTPD (FTP server) + +### Step 4: Verify Installation + +```bash +# Check CyberPanel service status +sudo systemctl status lscpd + +# Check web server status +sudo systemctl status apache2 + +# Check if CyberPanel is accessible +curl -I http://localhost:8090 +``` + +## 🔧 Post-Installation Configuration + +### Access CyberPanel + +1. Open your web browser +2. Navigate to: `http://your-server-ip:8090` +3. Default login credentials: + - **Username**: `admin` + - **Password**: `123456` (change immediately!) + +### Change Default Password + +```bash +# Login to CyberPanel CLI +sudo cyberpanel + +# Change admin password +cyberpanel --change-password admin +``` + +### Configure Firewall + +```bash +# Allow CyberPanel ports +sudo ufw allow 8090/tcp +sudo ufw allow 80/tcp +sudo ufw allow 443/tcp +sudo ufw allow 21/tcp +sudo ufw allow 25/tcp +sudo ufw allow 53/tcp +sudo ufw allow 587/tcp +sudo ufw allow 993/tcp +sudo ufw allow 995/tcp + +# Enable firewall +sudo ufw enable +``` + +## 🐛 Troubleshooting + +### Common Issues + +#### 1. OS Detection Failed +**Problem**: Installer doesn't recognize Debian 13 +**Solution**: Ensure you're running the latest installer version + +```bash +# Download latest installer +wget https://cyberpanel.sh/install.sh +chmod +x install.sh +sudo ./install.sh +``` + +#### 2. Package Installation Failed +**Problem**: apt-get errors during installation +**Solution**: Update repositories and retry + +```bash +# Update package lists +sudo apt update + +# Fix broken packages +sudo apt --fix-broken install + +# Retry installation +sudo ./install.sh +``` + +#### 3. Service Won't Start +**Problem**: CyberPanel service fails to start +**Solution**: Check logs and restart services + +```bash +# Check service status +sudo systemctl status lscpd + +# Check logs +sudo journalctl -u lscpd -f + +# Restart service +sudo systemctl restart lscpd +``` + +#### 4. Web Server Issues +**Problem**: Apache2 configuration problems +**Solution**: Reconfigure web server + +```bash +# Check Apache2 status +sudo systemctl status apache2 + +# Test configuration +sudo apache2ctl configtest + +# Restart Apache2 +sudo systemctl restart apache2 +``` + +### Log Files + +Important log locations: +- **CyberPanel**: `/usr/local/CyberCP/logs/` +- **Apache2**: `/var/log/apache2/` +- **System**: `/var/log/syslog` +- **Installation**: `/root/cyberpanel-install.log` + +## 🔒 Security Considerations + +### Initial Security Setup + +1. **Change Default Password** + ```bash + sudo cyberpanel --change-password admin + ``` + +2. **Update System** + ```bash + sudo apt update && sudo apt upgrade -y + ``` + +3. **Configure Firewall** + ```bash + sudo ufw enable + sudo ufw default deny incoming + sudo ufw default allow outgoing + ``` + +4. **Enable Fail2Ban** + ```bash + sudo apt install fail2ban -y + sudo systemctl enable fail2ban + sudo systemctl start fail2ban + ``` + +### SSL Certificate Setup + +1. **Access CyberPanel Web Interface** +2. **Navigate to**: SSL → Let's Encrypt +3. **Enter your domain name** +4. **Click "Issue" to get free SSL certificate** + +## 📊 Performance Optimization + +### System Optimization + +```bash +# Optimize Apache2 for Debian +sudo nano /etc/apache2/apache2.conf + +# Add these lines: +ServerTokens Prod +ServerSignature Off +KeepAlive On +MaxKeepAliveRequests 100 +KeepAliveTimeout 5 +``` + +### PHP Optimization + +1. **Access CyberPanel Web Interface** +2. **Navigate to**: PHP → PHP Settings +3. **Configure**: + - Memory limit: 256M + - Max execution time: 300 + - Upload max filesize: 64M + +## 🔄 Updates and Maintenance + +### Update CyberPanel + +```bash +# Update to latest version +sudo cyberpanel --update + +# Or use the upgrade script +sudo ./cyberpanel_upgrade.sh +``` + +### System Maintenance + +```bash +# Update system packages +sudo apt update && sudo apt upgrade -y + +# Clean package cache +sudo apt autoremove -y +sudo apt autoclean + +# Check disk usage +df -h + +# Check memory usage +free -h +``` + +## 📚 Additional Resources + +### Documentation +- [CyberPanel Official Docs](https://cyberpanel.net/docs/) +- [Debian 13 Release Notes](https://www.debian.org/releases/bookworm/releasenotes) +- [Apache2 Configuration Guide](https://httpd.apache.org/docs/2.4/) + +### Community Support +- [CyberPanel Community Forum](https://forums.cyberpanel.net/) +- [GitHub Issues](https://github.com/usmannasir/cyberpanel/issues) +- [Discord Server](https://discord.gg/cyberpanel) + +### Testing Compatibility + +Run the compatibility test script: + +```bash +# Download test script +wget https://raw.githubusercontent.com/cyberpanel/cyberpanel/main/test_debian13_support.sh + +# Make executable +chmod +x test_debian13_support.sh + +# Run test +sudo ./test_debian13_support.sh +``` + +## ✅ Verification Checklist + +After installation, verify these components: + +- [ ] CyberPanel web interface accessible +- [ ] Admin password changed +- [ ] SSL certificate installed +- [ ] Firewall configured +- [ ] Email server working (if installed) +- [ ] DNS server working (if installed) +- [ ] FTP server working (if installed) +- [ ] System updates applied +- [ ] Logs are clean +- [ ] Services are running + +## 🆘 Getting Help + +If you encounter issues: + +1. **Check the logs** (see Troubleshooting section) +2. **Run the compatibility test** +3. **Search the documentation** +4. **Ask in the community forum** +5. **Create a GitHub issue** with detailed information + +--- + +**Note**: This guide is specifically for Debian 13. For other operating systems, refer to the main CyberPanel documentation. diff --git a/guides/INDEX.md b/guides/INDEX.md index d31a90f61..f42331e6a 100644 --- a/guides/INDEX.md +++ b/guides/INDEX.md @@ -14,6 +14,9 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu ### 📧 Email & Marketing - **[Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)** - Step-by-step guide for installing and configuring Mautic email marketing platform +### 🐧 Operating System Support +- **[Debian 13 Installation Guide](DEBIAN_13_INSTALLATION_GUIDE.md)** - Complete installation and configuration guide for CyberPanel on Debian 13 (Bookworm) + ### 🎨 Customization & Design - **[Custom CSS Guide](CUSTOM_CSS_GUIDE.md)** - Complete guide for creating custom CSS that works with CyberPanel 2.5.5-dev design system @@ -24,14 +27,16 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu ## 🚀 Quick Start 1. **New to CyberPanel?** Start with the [README](../README.md) for installation and basic setup -2. **Need Docker help?** Check the [Docker Command Execution Guide](Docker_Command_Execution_Guide.md) -3. **Setting up email marketing?** Follow the [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md) -4. **Want to customize the interface?** Check the [Custom CSS Guide](CUSTOM_CSS_GUIDE.md) -5. **Want to contribute?** Read the [Contributing Guide](CONTRIBUTING.md) +2. **Installing on Debian 13?** Follow the [Debian 13 Installation Guide](DEBIAN_13_INSTALLATION_GUIDE.md) +3. **Need Docker help?** Check the [Docker Command Execution Guide](Docker_Command_Execution_Guide.md) +4. **Setting up email marketing?** Follow the [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md) +5. **Want to customize the interface?** Check the [Custom CSS Guide](CUSTOM_CSS_GUIDE.md) +6. **Want to contribute?** Read the [Contributing Guide](CONTRIBUTING.md) ## 🔍 Finding What You Need - **Installation & Setup**: [README](../README.md) +- **Debian 13 Installation**: [Debian 13 Installation Guide](DEBIAN_13_INSTALLATION_GUIDE.md) - **Docker Features**: [Docker Command Execution Guide](Docker_Command_Execution_Guide.md) - **Security Features**: [AI Scanner Documentation](AIScannerDocs.md) - **Email Marketing**: [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md) diff --git a/testPlugin/OS_COMPATIBILITY.md b/testPlugin/OS_COMPATIBILITY.md index f616418de..b01441843 100644 --- a/testPlugin/OS_COMPATIBILITY.md +++ b/testPlugin/OS_COMPATIBILITY.md @@ -10,7 +10,9 @@ The CyberPanel Test Plugin is designed to work seamlessly across all CyberPanel- |------------------|---------|----------------|----------------|-----------------|-----------------| | **Ubuntu** | 22.04 | ✅ Full Support | 3.10+ | apt-get | systemctl | | **Ubuntu** | 20.04 | ✅ Full Support | 3.8+ | apt-get | systemctl | -| **Debian** | 11+ | ✅ Full Support | 3.9+ | apt-get | systemctl | +| **Debian** | 13 | ✅ Full Support | 3.11+ | apt-get | systemctl | +| **Debian** | 12 | ✅ Full Support | 3.10+ | apt-get | systemctl | +| **Debian** | 11 | ✅ Full Support | 3.9+ | apt-get | systemctl | | **AlmaLinux** | 10 | ✅ Full Support | 3.11+ | dnf | systemctl | | **AlmaLinux** | 9 | ✅ Full Support | 3.9+ | dnf | systemctl | | **AlmaLinux** | 8 | ✅ Full Support | 3.6+ | dnf/yum | systemctl | @@ -457,6 +459,6 @@ sudo rm -f /home/cyberpanel/plugins/testPlugin --- -**Last Updated**: December 2024 +**Last Updated**: September 2025 **Compatibility Version**: 1.0.0 -**Next Review**: March 2025 +**Next Review**: March 2026 diff --git a/test_debian13_support.sh b/test_debian13_support.sh new file mode 100644 index 000000000..81650f704 --- /dev/null +++ b/test_debian13_support.sh @@ -0,0 +1,430 @@ +#!/bin/bash + +# Debian 13 Support Test Script for CyberPanel +# This script tests the compatibility of CyberPanel with Debian 13 + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Test results +TESTS_PASSED=0 +TESTS_FAILED=0 +TESTS_TOTAL=0 + +# Function to print colored output +print_status() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +print_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" + ((TESTS_PASSED++)) +} + +print_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" + ((TESTS_FAILED++)) +} + +print_test_header() { + echo -e "\n${BLUE}=== $1 ===${NC}" + ((TESTS_TOTAL++)) +} + +# Function to run a test +run_test() { + local test_name="$1" + local test_command="$2" + local expected_result="$3" + + print_test_header "$test_name" + + if eval "$test_command" >/dev/null 2>&1; then + if [[ "$expected_result" == "success" ]]; then + print_success "$test_name passed" + return 0 + else + print_error "$test_name failed (unexpected success)" + return 1 + fi + else + if [[ "$expected_result" == "failure" ]]; then + print_success "$test_name passed (expected failure)" + return 0 + else + print_error "$test_name failed" + return 1 + fi + fi +} + +# Function to check OS detection +test_os_detection() { + print_test_header "OS Detection Test" + + # Check if we're on Debian 13 + if [[ -f /etc/os-release ]]; then + source /etc/os-release + if [[ "$ID" == "debian" && "$VERSION_ID" == "13" ]]; then + print_success "Debian 13 detected correctly" + else + print_warning "Not running on Debian 13 (Current: $ID $VERSION_ID)" + print_status "This test is designed for Debian 13, but will continue with current OS" + fi + else + print_error "Cannot detect OS - /etc/os-release not found" + return 1 + fi +} + +# Function to test CyberPanel OS detection logic +test_cyberpanel_os_detection() { + print_test_header "CyberPanel OS Detection Logic Test" + + # Test the OS detection logic from cyberpanel.sh + if grep -q -E "Debian GNU/Linux 11|Debian GNU/Linux 12|Debian GNU/Linux 13" /etc/os-release; then + print_success "CyberPanel OS detection logic recognizes Debian 11/12/13" + else + print_error "CyberPanel OS detection logic does not recognize current Debian version" + return 1 + fi +} + +# Function to test package manager compatibility +test_package_manager() { + print_test_header "Package Manager Compatibility Test" + + # Test apt-get availability + if command -v apt-get >/dev/null 2>&1; then + print_success "apt-get package manager is available" + else + print_error "apt-get package manager not found" + return 1 + fi + + # Test apt-get update (dry run) + if apt-get update --dry-run >/dev/null 2>&1; then + print_success "apt-get update works correctly" + else + print_warning "apt-get update failed (may be network related)" + fi +} + +# Function to test systemd compatibility +test_systemd_compatibility() { + print_test_header "Systemd Compatibility Test" + + # Test systemctl availability + if command -v systemctl >/dev/null 2>&1; then + print_success "systemctl is available" + else + print_error "systemctl not found" + return 1 + fi + + # Test systemd status + if systemctl is-system-running >/dev/null 2>&1; then + print_success "systemd is running" + else + print_warning "systemd status unclear" + fi +} + +# Function to test Python compatibility +test_python_compatibility() { + print_test_header "Python Compatibility Test" + + # Test Python 3 availability + if command -v python3 >/dev/null 2>&1; then + local python_version=$(python3 --version 2>&1 | cut -d' ' -f2) + print_success "Python 3 is available: $python_version" + + # Check if Python version is compatible (3.6+) + local major_version=$(echo "$python_version" | cut -d'.' -f1) + local minor_version=$(echo "$python_version" | cut -d'.' -f2) + + if [[ $major_version -ge 3 && $minor_version -ge 6 ]]; then + print_success "Python version is compatible (3.6+)" + else + print_warning "Python version may not be fully compatible (requires 3.6+)" + fi + else + print_error "Python 3 not found" + return 1 + fi + + # Test pip3 availability + if command -v pip3 >/dev/null 2>&1; then + print_success "pip3 is available" + else + print_warning "pip3 not found (may need to be installed)" + fi +} + +# Function to test web server compatibility +test_web_server_compatibility() { + print_test_header "Web Server Compatibility Test" + + # Test Apache2 availability + if command -v apache2 >/dev/null 2>&1; then + print_success "Apache2 is available" + else + print_warning "Apache2 not found (will be installed by CyberPanel)" + fi + + # Test if Apache2 can be installed + if apt-cache show apache2 >/dev/null 2>&1; then + print_success "Apache2 package is available in repositories" + else + print_error "Apache2 package not found in repositories" + return 1 + fi +} + +# Function to test required packages availability +test_required_packages() { + print_test_header "Required Packages Availability Test" + + local required_packages=( + "curl" + "wget" + "git" + "build-essential" + "python3-dev" + "python3-pip" + "python3-venv" + "software-properties-common" + "apt-transport-https" + "ca-certificates" + "gnupg" + ) + + local available_count=0 + local total_count=${#required_packages[@]} + + for package in "${required_packages[@]}"; do + if apt-cache show "$package" >/dev/null 2>&1; then + print_success "$package is available" + ((available_count++)) + else + print_warning "$package not found in repositories" + fi + done + + print_status "Available packages: $available_count/$total_count" + + if [[ $available_count -ge $((total_count * 8 / 10)) ]]; then + print_success "Most required packages are available" + else + print_warning "Many required packages are missing" + fi +} + +# Function to test LiteSpeed repository compatibility +test_litespeed_repo_compatibility() { + print_test_header "LiteSpeed Repository Compatibility Test" + + # Test if we can access LiteSpeed Debian repository + if curl -s --head "http://rpms.litespeedtech.com/debian/" | head -n 1 | grep -q "200 OK"; then + print_success "LiteSpeed Debian repository is accessible" + else + print_warning "LiteSpeed Debian repository may not be accessible" + fi + + # Test if we can download the repository setup script + if wget --spider "http://rpms.litespeedtech.com/debian/enable_lst_debian_repo.sh" 2>/dev/null; then + print_success "LiteSpeed repository setup script is available" + else + print_warning "LiteSpeed repository setup script may not be available" + fi +} + +# Function to test MariaDB compatibility +test_mariadb_compatibility() { + print_test_header "MariaDB Compatibility Test" + + # Test MariaDB repository accessibility + if curl -s --head "https://mariadb.org/mariadb_release_signing_key.pgp" | head -n 1 | grep -q "200 OK"; then + print_success "MariaDB signing key is accessible" + else + print_warning "MariaDB signing key may not be accessible" + fi + + # Test if MariaDB packages are available + if apt-cache show mariadb-server >/dev/null 2>&1; then + print_success "MariaDB packages are available in repositories" + else + print_warning "MariaDB packages not found in default repositories" + fi +} + +# Function to test network connectivity +test_network_connectivity() { + print_test_header "Network Connectivity Test" + + local test_urls=( + "https://github.com" + "https://pypi.org" + "http://rpms.litespeedtech.com" + "https://mariadb.org" + ) + + local accessible_count=0 + local total_count=${#test_urls[@]} + + for url in "${test_urls[@]}"; do + if curl -s --head "$url" >/dev/null 2>&1; then + print_success "$url is accessible" + ((accessible_count++)) + else + print_warning "$url is not accessible" + fi + done + + print_status "Accessible URLs: $accessible_count/$total_count" + + if [[ $accessible_count -ge $((total_count * 3 / 4)) ]]; then + print_success "Network connectivity is good" + else + print_warning "Network connectivity may be limited" + fi +} + +# Function to test system resources +test_system_resources() { + print_test_header "System Resources Test" + + # Test available memory + local total_memory=$(free -m | awk 'NR==2{print $2}') + if [[ $total_memory -ge 1024 ]]; then + print_success "Sufficient memory available: ${total_memory}MB" + else + print_warning "Low memory: ${total_memory}MB (recommended: 1GB+)" + fi + + # Test available disk space + local available_space=$(df / | awk 'NR==2{print $4}') + local available_gb=$((available_space / 1024 / 1024)) + if [[ $available_gb -ge 10 ]]; then + print_success "Sufficient disk space: ${available_gb}GB" + else + print_warning "Low disk space: ${available_gb}GB (recommended: 10GB+)" + fi + + # Test CPU cores + local cpu_cores=$(nproc) + if [[ $cpu_cores -ge 2 ]]; then + print_success "Sufficient CPU cores: $cpu_cores" + else + print_warning "Limited CPU cores: $cpu_cores (recommended: 2+)" + fi +} + +# Function to run all tests +run_all_tests() { + echo -e "${BLUE}========================================${NC}" + echo -e "${BLUE} CyberPanel Debian 13 Compatibility Test${NC}" + echo -e "${BLUE}========================================${NC}\n" + + test_os_detection + test_cyberpanel_os_detection + test_package_manager + test_systemd_compatibility + test_python_compatibility + test_web_server_compatibility + test_required_packages + test_litespeed_repo_compatibility + test_mariadb_compatibility + test_network_connectivity + test_system_resources + + echo -e "\n${BLUE}========================================${NC}" + echo -e "${BLUE} Test Results Summary${NC}" + echo -e "${BLUE}========================================${NC}" + echo -e "Total Tests: $TESTS_TOTAL" + echo -e "Passed: ${GREEN}$TESTS_PASSED${NC}" + echo -e "Failed: ${RED}$TESTS_FAILED${NC}" + + if [[ $TESTS_FAILED -eq 0 ]]; then + echo -e "\n${GREEN}✅ All tests passed! Debian 13 appears to be compatible with CyberPanel.${NC}" + return 0 + elif [[ $TESTS_FAILED -le 2 ]]; then + echo -e "\n${YELLOW}⚠️ Most tests passed. Debian 13 should be compatible with minor issues.${NC}" + return 0 + else + echo -e "\n${RED}❌ Multiple tests failed. Debian 13 may have compatibility issues.${NC}" + return 1 + fi +} + +# Function to show usage +show_usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " -h, --help Show this help message" + echo " -v, --verbose Enable verbose output" + echo " --quick Run only essential tests" + echo "" + echo "This script tests CyberPanel compatibility with Debian 13." + echo "Run as root for best results." +} + +# Main execution +main() { + local verbose=false + local quick=false + + # Parse command line arguments + while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_usage + exit 0 + ;; + -v|--verbose) + verbose=true + shift + ;; + --quick) + quick=true + shift + ;; + *) + echo "Unknown option: $1" + show_usage + exit 1 + ;; + esac + done + + # Check if running as root + if [[ $EUID -ne 0 ]]; then + print_warning "Not running as root. Some tests may fail." + print_status "Consider running: sudo $0" + fi + + # Run tests + if [[ "$quick" == "true" ]]; then + print_status "Running quick compatibility test..." + test_os_detection + test_cyberpanel_os_detection + test_package_manager + test_systemd_compatibility + else + run_all_tests + fi +} + +# Run main function +main "$@" From e2dfdd454e63db270e8c1e30d46ec61e27b3c158 Mon Sep 17 00:00:00 2001 From: Master3395 Date: Mon, 15 Sep 2025 22:57:49 +0200 Subject: [PATCH 06/11] Improve container management and Docker Compose generation - Enhanced port validation in ContainerManager to check for valid port numbers and handle errors gracefully. - Updated volume handling to ensure proper structure and existence checks. - Added a new helper function to generate Docker Compose YAML, consolidating logic for service configuration, including ports, volumes, and environment variables. - Removed duplicate Docker Compose generation code to streamline functionality. --- dockerManager/container.py | 42 +++-- .../static/dockerManager/dockerManager.js | 143 ++++++------------ 2 files changed, 77 insertions(+), 108 deletions(-) diff --git a/dockerManager/container.py b/dockerManager/container.py index a4ccada72..1bd25f751 100644 --- a/dockerManager/container.py +++ b/dockerManager/container.py @@ -323,17 +323,26 @@ class ContainerManager(multi.Thread): if 'ExposedPorts' in inspectImage['Config']: for item in inspectImage['Config']['ExposedPorts']: - # Do not allow priviledged port numbers - if int(data[item]) < 1024 or int(data[item]) > 65535: - data_ret = {'createContainerStatus': 0, 'error_message': "Choose port between 1024 and 65535"} - json_data = json.dumps(data_ret) - return HttpResponse(json_data) - portConfig[item] = data[item] + # Check if port data exists and is valid + if item in data and data[item]: + try: + port_num = int(data[item]) + # Do not allow priviledged port numbers + if port_num < 1024 or port_num > 65535: + data_ret = {'createContainerStatus': 0, 'error_message': "Choose port between 1024 and 65535"} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + portConfig[item] = data[item] + except (ValueError, TypeError): + data_ret = {'createContainerStatus': 0, 'error_message': f"Invalid port number: {data[item]}"} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) volumes = {} - for index, volume in volList.items(): - volumes[volume['src']] = {'bind': volume['dest'], - 'mode': 'rw'} + if volList: + for index, volume in volList.items(): + if isinstance(volume, dict) and 'src' in volume and 'dest' in volume: + volumes[volume['src']] = {'bind': volume['dest'], 'mode': 'rw'} ## Create Configurations admin = Administrator.objects.get(userName=dockerOwner) @@ -351,10 +360,15 @@ class ContainerManager(multi.Thread): try: container = client.containers.create(**containerArgs) except Exception as err: - if "port is already allocated" in err: # We need to delete container if port is not available + # Check if it's a port allocation error by converting to string first + error_message = str(err) + if "port is already allocated" in error_message: # We need to delete container if port is not available print("Deleting container") - container.remove(force=True) - data_ret = {'createContainerStatus': 0, 'error_message': str(err)} + try: + container.remove(force=True) + except: + pass # Container might not exist yet + data_ret = {'createContainerStatus': 0, 'error_message': error_message} json_data = json.dumps(data_ret) return HttpResponse(json_data) @@ -376,7 +390,9 @@ class ContainerManager(multi.Thread): except Exception as msg: - data_ret = {'createContainerStatus': 0, 'error_message': str(msg)} + # Ensure error message is properly converted to string + error_message = str(msg) if msg else "Unknown error occurred" + data_ret = {'createContainerStatus': 0, 'error_message': error_message} json_data = json.dumps(data_ret) return HttpResponse(json_data) diff --git a/dockerManager/static/dockerManager/dockerManager.js b/dockerManager/static/dockerManager/dockerManager.js index 5a89b644d..5d411a70d 100644 --- a/dockerManager/static/dockerManager/dockerManager.js +++ b/dockerManager/static/dockerManager/dockerManager.js @@ -273,6 +273,54 @@ app.controller('runContainer', function ($scope, $http) { } }; + // Helper function to generate Docker Compose YAML + function generateDockerComposeYml(containerInfo) { + var yml = 'version: \'3.8\'\n\n'; + yml += 'services:\n'; + yml += ' ' + containerInfo.name + ':\n'; + yml += ' image: ' + containerInfo.image + '\n'; + yml += ' container_name: ' + containerInfo.name + '\n'; + + // Add ports + var ports = Object.keys(containerInfo.ports); + if (ports.length > 0) { + yml += ' ports:\n'; + for (var i = 0; i < ports.length; i++) { + var port = ports[i]; + if (containerInfo.ports[port]) { + yml += ' - "' + containerInfo.ports[port] + ':' + port + '"\n'; + } + } + } + + // Add volumes + var volumes = Object.keys(containerInfo.volumes); + if (volumes.length > 0) { + yml += ' volumes:\n'; + for (var i = 0; i < volumes.length; i++) { + var volume = volumes[i]; + if (containerInfo.volumes[volume]) { + yml += ' - ' + containerInfo.volumes[volume] + ':' + volume + '\n'; + } + } + } + + // Add environment variables + var envVars = Object.keys(containerInfo.environment); + if (envVars.length > 0) { + yml += ' environment:\n'; + for (var i = 0; i < envVars.length; i++) { + var envVar = envVars[i]; + yml += ' - ' + envVar + '=' + containerInfo.environment[envVar] + '\n'; + } + } + + // Add restart policy + yml += ' restart: unless-stopped\n'; + + return yml; + } + // Docker Compose Functions for runContainer $scope.generateDockerCompose = function() { // Get container information from form @@ -1434,101 +1482,6 @@ app.controller('viewContainer', function ($scope, $http, $interval, $timeout) { }); }; - // Helper function to generate Docker Compose YAML - function generateDockerComposeYml(containerInfo) { - var yml = 'version: \'3.8\'\n\n'; - yml += 'services:\n'; - yml += ' ' + containerInfo.name + ':\n'; - yml += ' image: ' + containerInfo.image + '\n'; - yml += ' container_name: ' + containerInfo.name + '\n'; - - // Add ports - var ports = Object.keys(containerInfo.ports); - if (ports.length > 0) { - yml += ' ports:\n'; - for (var i = 0; i < ports.length; i++) { - var port = ports[i]; - if (containerInfo.ports[port]) { - yml += ' - "' + containerInfo.ports[port] + ':' + port + '"\n'; - } - } - } - - // Add volumes - var volumes = Object.keys(containerInfo.volumes); - if (volumes.length > 0) { - yml += ' volumes:\n'; - for (var i = 0; i < volumes.length; i++) { - var volume = volumes[i]; - if (containerInfo.volumes[volume]) { - yml += ' - ' + containerInfo.volumes[volume] + ':' + volume + '\n'; - } - } - } - - // Add environment variables - var envVars = Object.keys(containerInfo.environment); - if (envVars.length > 0) { - yml += ' environment:\n'; - for (var i = 0; i < envVars.length; i++) { - var envVar = envVars[i]; - yml += ' - ' + envVar + '=' + containerInfo.environment[envVar] + '\n'; - } - } - - // Add restart policy - yml += ' restart: unless-stopped\n'; - - return yml; - } - - // Helper function to generate Docker Compose YAML (for runContainer) - function generateDockerComposeYml(containerInfo) { - var yml = 'version: \'3.8\'\n\n'; - yml += 'services:\n'; - yml += ' ' + containerInfo.name + ':\n'; - yml += ' image: ' + containerInfo.image + '\n'; - yml += ' container_name: ' + containerInfo.name + '\n'; - - // Add ports - var ports = Object.keys(containerInfo.ports); - if (ports.length > 0) { - yml += ' ports:\n'; - for (var i = 0; i < ports.length; i++) { - var port = ports[i]; - if (containerInfo.ports[port]) { - yml += ' - "' + containerInfo.ports[port] + ':' + port + '"\n'; - } - } - } - - // Add volumes - var volumes = Object.keys(containerInfo.volumes); - if (volumes.length > 0) { - yml += ' volumes:\n'; - for (var i = 0; i < volumes.length; i++) { - var volume = volumes[i]; - if (containerInfo.volumes[volume]) { - yml += ' - ' + containerInfo.volumes[volume] + ':' + volume + '\n'; - } - } - } - - // Add environment variables - var envVars = Object.keys(containerInfo.environment); - if (envVars.length > 0) { - yml += ' environment:\n'; - for (var i = 0; i < envVars.length; i++) { - var envVar = envVars[i]; - yml += ' - ' + envVar + '=' + containerInfo.environment[envVar] + '\n'; - } - } - - // Add restart policy - yml += ' restart: unless-stopped\n'; - - return yml; - } $scope.showTop = function () { $scope.topHead = []; From d8f120e73ba4c0473937f8c9acaab511bad8bbdb Mon Sep 17 00:00:00 2001 From: Master3395 Date: Tue, 16 Sep 2025 23:24:41 +0200 Subject: [PATCH 07/11] Add firewall IP blocking feature and enhance security alerts - Introduced a new API endpoint to block IP addresses via firewalld, allowing users to manage security directly from the dashboard. - Updated the front-end to include a button for blocking IPs detected during brute force attacks, enhancing user interaction. - Revised README.md and documentation to include details about the new firewall blocking feature and its usage. - Improved the SSH security analysis function to streamline firewall command execution and ensure firewalld is active. --- README.md | 5 +- .../baseTemplate/custom-js/system-status.js | 62 +++++++ .../templates/baseTemplate/homePage.html | 17 ++ baseTemplate/urls.py | 1 + baseTemplate/views.py | 126 ++++++++++++--- guides/FIREWALL_BLOCKING_FEATURE.md | 152 ++++++++++++++++++ guides/INDEX.md | 8 +- test_firewall_blocking.py | 64 ++++++++ 8 files changed, 414 insertions(+), 21 deletions(-) create mode 100644 guides/FIREWALL_BLOCKING_FEATURE.md create mode 100644 test_firewall_blocking.py diff --git a/README.md b/README.md index cbe86b132..ab5ee0242 100755 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Web Hosting Control Panel powered by OpenLiteSpeed, designed to simplify hosting - 📧 **Email Support** (SnappyMail). - 🕌 **File Manager** for quick file access. - 🌐 **PHP Management** made easy. -- 🔒 **Firewall** (FirewallD & ConfigServer Firewall Integration). +- 🔒 **Firewall** (FirewallD Integration with One-Click IP Blocking). - 📀 **One-click Backups and Restores**. - 🐳 **Docker Management** with command execution capabilities. - 🤖 **AI-Powered Security Scanner** for enhanced protection. @@ -31,6 +31,7 @@ CyberPanel comes with comprehensive documentation and step-by-step guides: - 🤖 **[AI Scanner Setup](guides/AIScannerDocs.md)** - Configure AI-powered security scanning - 📧 **[Mautic Installation](guides/MAUTIC_INSTALLATION_GUIDE.md)** - Email marketing platform setup - 🎨 **[Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md)** - Create custom themes for CyberPanel 2.5.5-dev +- 🛡️ **[Firewall Blocking Feature](guides/FIREWALL_BLOCKING_FEATURE.md)** - One-click IP blocking from dashboard --- @@ -163,6 +164,7 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr - 🤖 [AI Scanner Setup](guides/AIScannerDocs.md) - Configure AI-powered security scanning - 📧 [Mautic Installation](guides/MAUTIC_INSTALLATION_GUIDE.md) - Email marketing platform setup - 🎨 [Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md) - Create custom themes for CyberPanel 2.5.5+ +- 🛡️ [Firewall Blocking Feature](guides/FIREWALL_BLOCKING_FEATURE.md) - One-click IP blocking from dashboard - 📚 [All Guides Index](guides/INDEX.md) - Complete documentation hub ### 🔗 **Direct Guide Links** @@ -171,6 +173,7 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr | ------------ | ---------------------------------------------------------- | ---------------------------------- | | 🐳 Docker | [Command Execution](guides/Docker_Command_Execution_Guide.md) | Execute commands in containers | | 🤖 Security | [AI Scanner](guides/AIScannerDocs.md) | AI-powered security scanning | +| 🛡️ Firewall | [Firewall Blocking Feature](guides/FIREWALL_BLOCKING_FEATURE.md) | One-click IP blocking from dashboard | | 📧 Email | [Mautic Setup](guides/MAUTIC_INSTALLATION_GUIDE.md) | Email marketing platform | | 🎨 Design | [Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md) | Create custom themes for 2.5.5-dev | | 📊 Bandwidth | [Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md) | Fix bandwidth reset issues | diff --git a/baseTemplate/static/baseTemplate/custom-js/system-status.js b/baseTemplate/static/baseTemplate/custom-js/system-status.js index 63c4e4e42..3d5c7bcd6 100644 --- a/baseTemplate/static/baseTemplate/custom-js/system-status.js +++ b/baseTemplate/static/baseTemplate/custom-js/system-status.js @@ -980,6 +980,10 @@ app.controller('dashboardStatsController', function ($scope, $http, $timeout) { $scope.showAddonRequired = false; $scope.addonInfo = {}; + // IP Blocking functionality + $scope.blockingIP = null; + $scope.blockedIPs = {}; + $scope.analyzeSSHSecurity = function() { $scope.loadingSecurityAnalysis = true; $scope.showAddonRequired = false; @@ -999,6 +1003,64 @@ app.controller('dashboardStatsController', function ($scope, $http, $timeout) { $scope.loadingSecurityAnalysis = false; }); }; + + $scope.blockIPAddress = function(ipAddress) { + if (!$scope.blockingIP) { + $scope.blockingIP = ipAddress; + + var data = { + ip_address: ipAddress + }; + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post('/base/blockIPAddress', data, config).then(function (response) { + $scope.blockingIP = null; + if (response.data && response.data.status === 1) { + // Mark IP as blocked + $scope.blockedIPs[ipAddress] = true; + + // Show success notification + new PNotify({ + title: 'Success', + text: `IP address ${ipAddress} has been blocked successfully using ${response.data.firewall.toUpperCase()}`, + type: 'success', + delay: 5000 + }); + + // Refresh security analysis to update alerts + $scope.analyzeSSHSecurity(); + } else { + // Show error notification + new PNotify({ + title: 'Error', + text: response.data && response.data.error ? response.data.error : 'Failed to block IP address', + type: 'error', + delay: 5000 + }); + } + }, function (err) { + $scope.blockingIP = null; + var errorMessage = 'Failed to block IP address'; + if (err.data && err.data.error) { + errorMessage = err.data.error; + } else if (err.data && err.data.message) { + errorMessage = err.data.message; + } + + new PNotify({ + title: 'Error', + text: errorMessage, + type: 'error', + delay: 5000 + }); + }); + } + }; // Initial fetch $scope.refreshTopProcesses(); diff --git a/baseTemplate/templates/baseTemplate/homePage.html b/baseTemplate/templates/baseTemplate/homePage.html index d6b4409c2..5523d03d3 100644 --- a/baseTemplate/templates/baseTemplate/homePage.html +++ b/baseTemplate/templates/baseTemplate/homePage.html @@ -663,6 +663,23 @@ Recommendation:

{$ alert.recommendation $}

+ +
+ + + Blocked + +
SSH Security Analysis") + print("3. Look for 'Brute Force Attack Detected' alerts") + print("4. Click the 'Block IP' button next to malicious IPs") + print() + + print("Expected behavior:") + print("- Button shows loading state during blocking") + print("- Success notification appears on successful blocking") + print("- IP is marked as 'Blocked' in the interface") + print("- Security analysis refreshes to update alerts") + print() + + print("Firewall Commands:") + print("- firewalld: firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address= drop'") + print("- firewalld reload: firewall-cmd --reload") + print() + +if __name__ == "__main__": + test_firewall_blocking() From ee6543f6bb71595a40a93d2e2ba5ae10e8844dd9 Mon Sep 17 00:00:00 2001 From: Master3395 Date: Tue, 16 Sep 2025 23:53:53 +0200 Subject: [PATCH 08/11] Remove deprecated files and enhance IP blocking feature - Deleted obsolete files: SECURITY_INSTALLATION.md, test.php, and test.sh. - Improved IP blocking functionality in views.py by adding validation for private and reserved IP addresses, enhancing security against self-blocking. - Updated error handling to capture specific issues related to IP validation and command execution. - Revised FIREWALL_BLOCKING_FEATURE.md to reflect new validation and security measures. --- SECURITY_INSTALLATION.md | 193 ---------------------------- baseTemplate/views.py | 66 ++++++++-- guides/FIREWALL_BLOCKING_FEATURE.md | 47 ++++++- test.php | 5 - test.sh | 0 5 files changed, 93 insertions(+), 218 deletions(-) delete mode 100644 SECURITY_INSTALLATION.md delete mode 100644 test.php delete mode 100755 test.sh diff --git a/SECURITY_INSTALLATION.md b/SECURITY_INSTALLATION.md deleted file mode 100644 index 60cd302c3..000000000 --- a/SECURITY_INSTALLATION.md +++ /dev/null @@ -1,193 +0,0 @@ -# CyberPanel Secure Installation Guide - -## Overview - -This document describes the secure installation process for CyberPanel that eliminates hardcoded passwords and implements environment-based configuration. - -## Security Improvements - -### ✅ **Fixed Security Vulnerabilities** - -1. **Hardcoded Database Passwords** - Now generated securely during installation -2. **Hardcoded Django Secret Key** - Now generated using cryptographically secure random generation -3. **Environment Variables** - All sensitive configuration moved to `.env` file -4. **File Permissions** - `.env` file set to 600 (owner read/write only) - -### 🔐 **Security Features** - -- **Cryptographically Secure Passwords**: Uses Python's `secrets` module for password generation -- **Environment-based Configuration**: Sensitive data stored in `.env` file, not in code -- **Secure File Permissions**: Environment files protected with 600 permissions -- **Credential Backup**: Automatic backup of credentials for recovery -- **Fallback Security**: Maintains backward compatibility with fallback method - -## Installation Process - -### 1. **Automatic Secure Installation** - -The installation script now automatically: - -1. Generates secure random passwords for: - - MySQL root user - - CyberPanel database user - - Django secret key - -2. Creates `.env` file with secure configuration: - ```bash - # Generated during installation - SECRET_KEY=your_64_character_secure_key - DB_PASSWORD=your_24_character_secure_password - ROOT_DB_PASSWORD=your_24_character_secure_password - ``` - -3. Creates `.env.backup` file for credential recovery -4. Sets secure file permissions (600) on all environment files - -### 2. **Manual Installation** (if needed) - -If you need to manually generate environment configuration: - -```bash -cd /usr/local/CyberCP -python install/env_generator.py /usr/local/CyberCP -``` - -## File Structure - -``` -/usr/local/CyberCP/ -├── .env # Main environment configuration (600 permissions) -├── .env.backup # Credential backup (600 permissions) -├── .env.template # Template for manual configuration -├── .gitignore # Prevents .env files from being committed -└── CyberCP/ - └── settings.py # Updated to use environment variables -``` - -## Security Best Practices - -### ✅ **Do's** - -- Keep `.env` and `.env.backup` files secure -- Record credentials from `.env.backup` and delete the file after installation -- Use strong, unique passwords for production deployments -- Regularly rotate database passwords -- Monitor access to environment files - -### ❌ **Don'ts** - -- Never commit `.env` files to version control -- Don't share `.env` files via insecure channels -- Don't use default passwords in production -- Don't leave `.env.backup` files on the system after recording credentials - -## Recovery - -### **Lost Credentials** - -If you lose your database credentials: - -1. Check if `.env.backup` file exists: - ```bash - sudo cat /usr/local/CyberCP/.env.backup - ``` - -2. If backup doesn't exist, you'll need to reset MySQL passwords using MySQL recovery procedures - -### **Regenerate Environment** - -To regenerate environment configuration: - -```bash -cd /usr/local/CyberCP -sudo python install/env_generator.py /usr/local/CyberCP -``` - -## Configuration Options - -### **Environment Variables** - -| Variable | Description | Default | -|----------|-------------|---------| -| `SECRET_KEY` | Django secret key | Generated (64 chars) | -| `DB_PASSWORD` | CyberPanel DB password | Generated (24 chars) | -| `ROOT_DB_PASSWORD` | MySQL root password | Generated (24 chars) | -| `DEBUG` | Debug mode | False | -| `ALLOWED_HOSTS` | Allowed hosts | localhost,127.0.0.1,hostname | - -### **Custom Configuration** - -To use custom passwords during installation: - -```bash -python install/env_generator.py /usr/local/CyberCP "your_root_password" "your_db_password" -``` - -## Troubleshooting - -### **Installation Fails** - -If the new secure installation fails: - -1. Check installation logs for error messages -2. The system will automatically fallback to the original installation method -3. Verify Python dependencies are installed: - ```bash - pip install python-dotenv - ``` - -### **Environment Loading Issues** - -If Django can't load environment variables: - -1. Ensure `.env` file exists and has correct permissions: - ```bash - ls -la /usr/local/CyberCP/.env - # Should show: -rw------- 1 root root - ``` - -2. Install python-dotenv if missing: - ```bash - pip install python-dotenv - ``` - -## Migration from Old Installation - -### **Existing Installations** - -For existing CyberPanel installations with hardcoded passwords: - -1. **Backup current configuration**: - ```bash - cp /usr/local/CyberCP/CyberCP/settings.py /usr/local/CyberCP/CyberCP/settings.py.backup - ``` - -2. **Generate new environment configuration**: - ```bash - cd /usr/local/CyberCP - python install/env_generator.py /usr/local/CyberCP - ``` - -3. **Update settings.py** (already done in new installations): - - The settings.py file now supports environment variables - - It will fallback to hardcoded values if .env is not available - -4. **Test the configuration**: - ```bash - cd /usr/local/CyberCP - python manage.py check - ``` - -## Support - -For issues with the secure installation: - -1. Check the installation logs -2. Verify file permissions -3. Ensure all dependencies are installed -4. Review the fallback installation method if needed - ---- - -**Security Notice**: This installation method significantly improves security by eliminating hardcoded credentials. Always ensure proper file permissions and secure handling of environment files. - diff --git a/baseTemplate/views.py b/baseTemplate/views.py index e9da11248..eedf0d871 100644 --- a/baseTemplate/views.py +++ b/baseTemplate/views.py @@ -1129,8 +1129,9 @@ def blockIPAddress(request): 'error': 'IP address is required' }), content_type='application/json', status=400) - # Validate IP address format + # Validate IP address format and check for private/reserved ranges import re + import ipaddress ip_pattern = r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$' if not re.match(ip_pattern, ip_address): return HttpResponse(json.dumps({ @@ -1138,39 +1139,78 @@ def blockIPAddress(request): 'error': 'Invalid IP address format' }), content_type='application/json', status=400) + # Check for private/reserved IP ranges to prevent self-blocking + try: + ip_obj = ipaddress.ip_address(ip_address) + if ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_link_local or ip_obj.is_reserved: + return HttpResponse(json.dumps({ + 'status': 0, + 'error': 'Cannot block private, loopback, link-local, or reserved IP addresses' + }), content_type='application/json', status=400) + + # Additional check for common problematic ranges + if (ip_address.startswith('127.') or # Loopback + ip_address.startswith('169.254.') or # Link-local + ip_address.startswith('224.') or # Multicast + ip_address.startswith('255.') or # Broadcast + ip_address in ['0.0.0.0', '::1']): # Invalid/loopback + return HttpResponse(json.dumps({ + 'status': 0, + 'error': 'Cannot block system or reserved IP addresses' + }), content_type='application/json', status=400) + + except ValueError: + return HttpResponse(json.dumps({ + 'status': 0, + 'error': 'Invalid IP address' + }), content_type='application/json', status=400) + # Use firewalld (CSF has been discontinued) firewall_cmd = 'firewalld' try: - # Verify firewalld is active - firewalld_check = ProcessUtilities.outputExecutioner('systemctl is-active firewalld') - if not (firewalld_check and 'active' in firewalld_check): + # Verify firewalld is active using subprocess for better security + import subprocess + firewalld_check = subprocess.run(['systemctl', 'is-active', 'firewalld'], + capture_output=True, text=True, timeout=10) + if not (firewalld_check.returncode == 0 and 'active' in firewalld_check.stdout): return HttpResponse(json.dumps({ 'status': 0, 'error': 'Firewalld is not active. Please enable firewalld service.' }), content_type='application/json', status=500) + except subprocess.TimeoutExpired: + return HttpResponse(json.dumps({ + 'status': 0, + 'error': 'Timeout checking firewalld status' + }), content_type='application/json', status=500) except Exception as e: return HttpResponse(json.dumps({ 'status': 0, 'error': f'Cannot check firewalld status: {str(e)}' }), content_type='application/json', status=500) - # Block the IP address using firewalld + # Block the IP address using firewalld with subprocess for better security success = False error_message = '' try: - # Use firewalld to block IP - command = f'firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address={ip_address} drop"' - result = ProcessUtilities.executioner(command) - if result == 0: + # Use subprocess with explicit argument lists to prevent injection + rich_rule = f'rule family=ipv4 source address={ip_address} drop' + add_rule_cmd = ['firewall-cmd', '--permanent', '--add-rich-rule', rich_rule] + + # Execute the add rule command + result = subprocess.run(add_rule_cmd, capture_output=True, text=True, timeout=30) + if result.returncode == 0: # Reload firewall rules - reload_result = ProcessUtilities.executioner('firewall-cmd --reload') - if reload_result == 0: + reload_cmd = ['firewall-cmd', '--reload'] + reload_result = subprocess.run(reload_cmd, capture_output=True, text=True, timeout=30) + if reload_result.returncode == 0: success = True else: - error_message = 'Failed to reload firewall rules' + error_message = f'Failed to reload firewall rules: {reload_result.stderr}' else: - error_message = 'Failed to add firewall rule' + error_message = f'Failed to add firewall rule: {result.stderr}' + except subprocess.TimeoutExpired: + error_message = 'Firewall command timed out' except Exception as e: error_message = f'Firewall command failed: {str(e)}' diff --git a/guides/FIREWALL_BLOCKING_FEATURE.md b/guides/FIREWALL_BLOCKING_FEATURE.md index 5a12143af..65c4a18fd 100644 --- a/guides/FIREWALL_BLOCKING_FEATURE.md +++ b/guides/FIREWALL_BLOCKING_FEATURE.md @@ -80,23 +80,56 @@ firewall-cmd --reload 1. **Admin-Only Access**: Feature restricted to administrators 2. **Premium Feature**: Requires CyberPanel addons -3. **IP Validation**: Validates IP address format before blocking -4. **Firewalld Verification**: Ensures firewalld service is active -5. **Audit Logging**: All blocking actions are logged -6. **Error Handling**: Comprehensive error handling and user feedback +3. **Enhanced IP Validation**: Validates IP address format and prevents blocking private/reserved ranges +4. **Command Injection Protection**: Uses subprocess with explicit argument lists +5. **Timeout Protection**: Prevents hanging processes with configurable timeouts +6. **Firewalld Verification**: Ensures firewalld service is active +7. **Audit Logging**: All blocking actions are logged +8. **Comprehensive Error Handling**: Detailed error messages with captured stderr ## Error Handling The feature includes robust error handling for: -- Invalid IP addresses +- Invalid IP addresses and formats +- Private/reserved IP address ranges - Firewalld service not active -- Firewall command failures +- Firewall command failures and timeouts - Network connectivity issues - Permission errors +- Command injection attempts +- Process timeouts ## Testing -A test script is provided (`test_firewall_blocking.py`) for manual testing, though the feature is best tested through the web interface. +A test script is provided for basic functionality testing: +- `test_firewall_blocking.py` - Basic functionality testing + +The feature is best tested through the web interface with various IP address types. + +## Enhanced Security Features + +### IP Range Validation +The system now prevents blocking of: +- **Private IP ranges**: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 +- **Loopback addresses**: 127.0.0.0/8 +- **Link-local addresses**: 169.254.0.0/16 +- **Multicast addresses**: 224.0.0.0/4 +- **Broadcast addresses**: 255.255.255.255 +- **Reserved addresses**: 0.0.0.0 + +### Command Execution Security +- **Subprocess with explicit arguments**: Prevents command injection +- **Timeout protection**: 10s for status checks, 30s for firewall commands +- **Error capture**: Captures both stdout and stderr for better debugging +- **No shell interpretation**: Eliminates shell injection vulnerabilities + +### Example Error Messages +``` +"Cannot block private, loopback, link-local, or reserved IP addresses" +"Cannot block system or reserved IP addresses" +"Timeout checking firewalld status" +"Firewall command timed out" +``` ## Browser Compatibility diff --git a/test.php b/test.php deleted file mode 100644 index 57a4844d4..000000000 --- a/test.php +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/test.sh b/test.sh deleted file mode 100755 index e69de29bb..000000000 From 17b4965816309223c2a850395b4def8e534f6d6a Mon Sep 17 00:00:00 2001 From: Master3395 Date: Wed, 17 Sep 2025 00:06:58 +0200 Subject: [PATCH 09/11] Enhance bandwidth usage calculation with resource management - Introduced memory and processing time limits to prevent system overload during bandwidth calculations. - Added error handling for file operations and improved logging for better traceability. - Implemented batch processing of log lines to manage memory usage effectively. - Updated methods to safely parse log data and handle large files, ensuring robust performance. - Refactored code for clarity and maintainability, including the addition of helper functions for file size and memory limit settings. --- plogical/findBWUsage.py | 247 +++++++++++++++++++++++++++++----------- 1 file changed, 183 insertions(+), 64 deletions(-) diff --git a/plogical/findBWUsage.py b/plogical/findBWUsage.py index c4982b9b9..698862440 100644 --- a/plogical/findBWUsage.py +++ b/plogical/findBWUsage.py @@ -1,65 +1,157 @@ import sys sys.path.append('/usr/local/CyberCP') import os +import gc +import time from plogical import CyberCPLogFileWriter as logging import shlex import subprocess import validators +import resource class findBWUsage: + # Configuration constants + MAX_MEMORY_MB = 512 # Maximum memory usage in MB + MAX_PROCESSING_TIME = 300 # Maximum processing time in seconds (5 minutes) + MAX_LOG_LINES_PER_BATCH = 10000 # Process logs in batches + MAX_FILE_SIZE_MB = 100 # Skip files larger than 100MB + @staticmethod def parse_last_digits(line): - return line.split(' ') + """Safely parse log line and extract bandwidth data""" + try: + parts = line.split(' ') + if len(parts) < 10: + return None + # Extract the size field (index 9) and clean it + size_str = parts[9].replace('"', '').strip() + if size_str == '-': + return 0 + return int(size_str) + except (ValueError, IndexError, AttributeError): + return None + + @staticmethod + def get_file_size_mb(filepath): + """Get file size in MB""" + try: + return os.path.getsize(filepath) / (1024 * 1024) + except OSError: + return 0 + + @staticmethod + def set_memory_limit(): + """Set memory limit to prevent system overload""" + try: + # Set memory limit to MAX_MEMORY_MB + memory_limit = findBWUsage.MAX_MEMORY_MB * 1024 * 1024 # Convert to bytes + resource.setrlimit(resource.RLIMIT_AS, (memory_limit, memory_limit)) + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to set memory limit: {str(e)}") @staticmethod def calculateBandwidth(domainName): + """Calculate bandwidth usage for a domain with memory protection""" + start_time = time.time() + try: - path = "/home/"+domainName+"/logs/"+domainName+".access_log" - + path = "/home/" + domainName + "/logs/" + domainName + ".access_log" + if not os.path.exists(path): return 0 - from processUtilities import ProcessUtilities - logData = ProcessUtilities.outputExecutioner('cat %s' % (path), 'nobody').splitlines() - logDataLines = len(logData) + + # Check file size before processing + file_size_mb = findBWUsage.get_file_size_mb(path) + if file_size_mb > findBWUsage.MAX_FILE_SIZE_MB: + logging.CyberCPLogFileWriter.writeToFile(f"Skipping large file {path} ({file_size_mb:.2f}MB)") + return 0 - if not os.path.exists("/home/"+domainName+"/logs"): + if not os.path.exists("/home/" + domainName + "/logs"): return 0 bwmeta = "/home/cyberpanel/%s.bwmeta" % (domainName) - - if not os.path.exists(path): - writeMeta = open(bwmeta, 'w') - writeMeta.writelines('0\n0\n') - writeMeta.close() - os.chmod(bwmeta, 0o600) - return 1 - + + # Initialize metadata + currentUsed = 0 + currentLinesRead = 0 + + # Read existing metadata if os.path.exists(bwmeta): - data = open(bwmeta).readlines() - currentUsed = int(data[0].strip("\n")) - currentLinesRead = int(data[1].strip("\n")) - if currentLinesRead > logDataLines: + try: + with open(bwmeta, 'r') as f: + data = f.readlines() + if len(data) >= 2: + currentUsed = int(data[0].strip("\n")) + currentLinesRead = int(data[1].strip("\n")) + except (ValueError, IndexError): + currentUsed = 0 currentLinesRead = 0 - else: - currentUsed = 0 - currentLinesRead = 0 - startLine = currentLinesRead + # Process log file in streaming mode to avoid memory issues + try: + with open(path, 'r', encoding='utf-8', errors='ignore') as logfile: + # Skip to the last processed line + for _ in range(currentLinesRead): + try: + next(logfile) + except StopIteration: + break + + lines_processed = 0 + batch_size = 0 + + for line in logfile: + # Check processing time limit + if time.time() - start_time > findBWUsage.MAX_PROCESSING_TIME: + logging.CyberCPLogFileWriter.writeToFile(f"Processing timeout for {domainName}") + break + + line = line.strip() + if len(line) > 10: + bandwidth = findBWUsage.parse_last_digits(line) + if bandwidth is not None: + currentUsed += bandwidth + + currentLinesRead += 1 + lines_processed += 1 + batch_size += 1 + + # Process in batches to manage memory + if batch_size >= findBWUsage.MAX_LOG_LINES_PER_BATCH: + # Force garbage collection + gc.collect() + batch_size = 0 + + # Check memory usage + try: + import psutil + process = psutil.Process() + memory_mb = process.memory_info().rss / (1024 * 1024) + if memory_mb > findBWUsage.MAX_MEMORY_MB: + logging.CyberCPLogFileWriter.writeToFile(f"Memory limit reached for {domainName}") + break + except ImportError: + pass # psutil not available, continue processing + + except (IOError, OSError) as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error reading log file {path}: {str(e)}") + return 0 - for line in logData[startLine:]: - line = line.strip('"\n') - currentLinesRead = currentLinesRead + 1 - if len(line)>10: - currentUsed = int(findBWUsage.parse_last_digits(line)[9].replace('"', '')) + currentUsed + # Write updated metadata + try: + with open(bwmeta, 'w') as f: + f.write(f"{currentUsed}\n{currentLinesRead}\n") + os.chmod(bwmeta, 0o600) + except (IOError, OSError) as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error writing metadata {bwmeta}: {str(e)}") + return 0 - writeMeta = open(bwmeta,'w') - writeMeta.writelines(str(currentUsed)+"\n") - writeMeta.writelines(str(currentLinesRead) + "\n") - writeMeta.close() + # Log processing statistics + processing_time = time.time() - start_time + if processing_time > 10: # Log if processing took more than 10 seconds + logging.CyberCPLogFileWriter.writeToFile(f"Processed {domainName}: {lines_processed} lines in {processing_time:.2f}s") - os.chmod(bwmeta, 0o600) - - except BaseException as msg: + except Exception as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [calculateBandwidth]") return 0 @@ -67,67 +159,95 @@ class findBWUsage: @staticmethod def startCalculations(): + """Start bandwidth calculations with resource protection""" try: + # Set memory limit + findBWUsage.set_memory_limit() + + start_time = time.time() + domains_processed = 0 + for directories in os.listdir("/home"): + # Check overall processing time + if time.time() - start_time > findBWUsage.MAX_PROCESSING_TIME * 2: + logging.CyberCPLogFileWriter.writeToFile("Overall processing timeout reached") + break + if validators.domain(directories): - findBWUsage.calculateBandwidth(directories) - except BaseException as msg: + try: + result = findBWUsage.calculateBandwidth(directories) + domains_processed += 1 + + # Force garbage collection after each domain + gc.collect() + + # Small delay to prevent system overload + time.sleep(0.1) + + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error processing domain {directories}: {str(e)}") + continue + + total_time = time.time() - start_time + logging.CyberCPLogFileWriter.writeToFile(f"Bandwidth calculation completed: {domains_processed} domains in {total_time:.2f}s") + + except Exception as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startCalculations]") return 0 @staticmethod - def findDomainBW(domainName,totalAllowed): + def findDomainBW(domainName, totalAllowed): + """Find domain bandwidth usage with improved error handling""" try: - path = "/home/"+domainName+"/logs/"+domainName+".access_log" + path = "/home/" + domainName + "/logs/" + domainName + ".access_log" - if not os.path.exists("/home/"+domainName+"/logs"): - return [0,0] + if not os.path.exists("/home/" + domainName + "/logs"): + return [0, 0] - bwmeta = "/home/" + domainName + "/logs/bwmeta" + bwmeta = "/home/cyberpanel/%s.bwmeta" % (domainName) if not os.path.exists(path): - return [0,0] - - + return [0, 0] if os.path.exists(bwmeta): try: - data = open(bwmeta).readlines() + with open(bwmeta, 'r') as f: + data = f.readlines() + + if len(data) < 1: + return [0, 0] + currentUsed = int(data[0].strip("\n")) + inMB = int(float(currentUsed) / (1024.0 * 1024.0)) - inMB = int(float(currentUsed)/(1024.0*1024.0)) + if totalAllowed <= 0: + totalAllowed = 999999 percentage = float(100) / float(totalAllowed) - percentage = float(percentage) * float(inMB) - except: - return [0,0] - if percentage > 100.0: - percentage = 100 + if percentage > 100.0: + percentage = 100 - return [inMB,percentage] + return [inMB, percentage] + except (ValueError, IndexError, IOError): + return [0, 0] else: return [0, 0] - except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [findDomainBW]") - return 0 + return [0, 0] except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [findDomainBW]") - return 0 - - return 1 + return [0, 0] @staticmethod def changeSystemLanguage(): + """Change system language with improved error handling""" try: - command = 'localectl set-locale LANG=en_US.UTF-8' - cmd = shlex.split(command) - res = subprocess.call(cmd) if res == 1: @@ -135,12 +255,10 @@ class findBWUsage: else: pass - print("###############################################") print(" Language Changed to English ") print("###############################################") - except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [changeSystemLanguage]") return 0 @@ -151,4 +269,5 @@ class findBWUsage: return 1 -findBWUsage.startCalculations() \ No newline at end of file +if __name__ == "__main__": + findBWUsage.startCalculations() \ No newline at end of file From 694cb03c805f76ac0ba1364bb47278f0dc56a072 Mon Sep 17 00:00:00 2001 From: Master3395 Date: Wed, 17 Sep 2025 00:32:07 +0200 Subject: [PATCH 10/11] Add firewall rule management features and enhance repository setup - Implemented functionality to edit existing firewall rules, including validation and error handling. - Added endpoints for exporting and importing firewall rules in JSON format, allowing users to manage rules more efficiently. - Enhanced the user interface with modals for editing rules and buttons for exporting/importing rules. - Updated the `cyberpanel.sh` script to support AlmaLinux 10 and improved LiteSpeed GPG key import with fallback options. - Refactored repository setup to accommodate different OS versions, ensuring compatibility with CentOS and AlmaLinux. --- cyberpanel.sh | 43 ++- firewall/EXPORT_IMPORT_FIREWALL_RULES.md | 121 +++++++++ firewall/firewallManager.py | 231 ++++++++++++++++ firewall/static/firewall/firewall.js | 247 +++++++++++++++++ firewall/templates/firewall/firewall.html | 308 +++++++++++++++++++++- firewall/urls.py | 7 + firewall/views.py | 33 +++ key.pem | 20 -- 8 files changed, 977 insertions(+), 33 deletions(-) create mode 100644 firewall/EXPORT_IMPORT_FIREWALL_RULES.md delete mode 100644 key.pem diff --git a/cyberpanel.sh b/cyberpanel.sh index b3c988a92..c1e4d195e 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -270,6 +270,11 @@ setup_epel_repo() { yum install -y https://cyberpanel.sh/dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm Check_Return "yum repo" "no_exit" ;; + "10") + # AlmaLinux 10 EPEL support + yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm + Check_Return "yum repo" "no_exit" + ;; esac } @@ -309,11 +314,12 @@ gpgcheck=1 EOF elif [[ "$Server_OS_Version" = "10" ]] && uname -m | grep -q 'x86_64'; then cat </etc/yum.repos.d/MariaDB.repo -# MariaDB 10.11 CentOS repository list - created 2021-08-06 02:01 UTC +# MariaDB 10.11 RHEL10 repository list - AlmaLinux 10 compatible # http://downloads.mariadb.org/mariadb/repositories/ [mariadb] name = MariaDB -baseurl = http://yum.mariadb.org/10.11/rhel9-amd64/ +baseurl = http://yum.mariadb.org/10.11/rhel10-amd64/ +module_hotfixes=1 gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB enabled=1 gpgcheck=1 @@ -1117,8 +1123,14 @@ log_function_start "Pre_Install_Setup_Repository" log_info "Setting up package repositories for $Server_OS $Server_OS_Version" if [[ $Server_OS = "CentOS" ]] ; then log_debug "Importing LiteSpeed GPG key" - rpm --import https://cyberpanel.sh/rpms.litespeedtech.com/centos/RPM-GPG-KEY-litespeed - #import the LiteSpeed GPG key + # Import LiteSpeed GPG key with fallback + rpm --import https://cyberpanel.sh/rpms.litespeedtech.com/centos/RPM-GPG-KEY-litespeed || { + warning "Primary GPG key import failed, trying alternative source" + rpm --import https://rpms.litespeedtech.com/centos/RPM-GPG-KEY-litespeed || { + error "Failed to import LiteSpeed GPG key from all sources" + return 1 + } + } yum clean all yum autoremove -y epel-release @@ -1151,7 +1163,12 @@ if [[ $Server_OS = "CentOS" ]] ; then dnf config-manager --set-enabled crb fi - yum install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm + # Install appropriate remi-release based on version + if [[ "$Server_OS_Version" = "9" ]]; then + yum install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm + elif [[ "$Server_OS_Version" = "10" ]]; then + yum install -y https://rpms.remirepo.net/enterprise/remi-release-10.rpm + fi Check_Return "yum repo" "no_exit" fi @@ -1341,8 +1358,22 @@ if [[ "$Server_OS" = "CentOS" ]] || [[ "$Server_OS" = "openEuler" ]] ; then #!/bin/bash - dnf install -y libnsl zip wget strace net-tools curl which bc telnet htop libevent-devel gcc libattr-devel xz-devel MariaDB-server MariaDB-client MariaDB-devel curl-devel git platform-python-devel tar socat python3 zip unzip bind-utils gpgme-devel openssl-devel + dnf install -y libnsl zip wget strace net-tools curl which bc telnet htop libevent-devel gcc libattr-devel xz-devel MariaDB-server MariaDB-client MariaDB-devel curl-devel git platform-python-devel tar socat python3 zip unzip bind-utils gpgme-devel openssl-devel boost-devel boost-program-options Check_Return + + # Fix boost library compatibility for galera-4 on AlmaLinux 10 + if [[ "$Server_OS_Version" = "10" ]]; then + # Create symlink for boost libraries if needed + if [ ! -f /usr/lib64/libboost_program_options.so.1.75.0 ]; then + BOOST_VERSION=$(find /usr/lib64 -name "libboost_program_options.so.*" | head -1 | sed 's/.*libboost_program_options\.so\.//') + if [ -n "$BOOST_VERSION" ]; then + ln -sf /usr/lib64/libboost_program_options.so.$BOOST_VERSION /usr/lib64/libboost_program_options.so.1.75.0 + log_info "Created boost library symlink for galera-4 compatibility: $BOOST_VERSION -> 1.75.0" + else + warning "Could not find boost libraries, galera-4 may not work properly" + fi + fi + fi elif [[ "$Server_OS_Version" = "20" ]] || [[ "$Server_OS_Version" = "22" ]] || [[ "$Server_OS_Version" = "24" ]] ; then dnf install -y libnsl zip wget strace net-tools curl which bc telnet htop libevent-devel gcc libattr-devel xz-devel mariadb-devel curl-devel git python3-devel tar socat python3 zip unzip bind-utils gpgme-devel Check_Return diff --git a/firewall/EXPORT_IMPORT_FIREWALL_RULES.md b/firewall/EXPORT_IMPORT_FIREWALL_RULES.md new file mode 100644 index 000000000..9fb19b983 --- /dev/null +++ b/firewall/EXPORT_IMPORT_FIREWALL_RULES.md @@ -0,0 +1,121 @@ +# Firewall Rules Export/Import Feature + +## Overview + +This feature allows CyberPanel administrators to export and import firewall rules between servers, making it easy to replicate security configurations across multiple servers. + +## Features + +### Export Functionality +- Exports all custom firewall rules to a JSON file +- Excludes default CyberPanel rules (CyberPanel Admin, SSHCustom) to prevent conflicts +- Includes metadata such as export timestamp and rule count +- Downloads file directly to the user's browser + +### Import Functionality +- Imports firewall rules from a previously exported JSON file +- Validates file format before processing +- Skips duplicate rules (same name, protocol, port, and IP address) +- Excludes default CyberPanel rules from import +- Provides detailed import summary (imported, skipped, error counts) +- Shows specific error messages for failed imports + +## Usage + +### Exporting Rules +1. Navigate to the Firewall section in CyberPanel +2. Click the "Export Rules" button in the Firewall Rules panel header +3. The system will generate and download a JSON file containing your custom rules + +### Importing Rules +1. Navigate to the Firewall section in CyberPanel +2. Click the "Import Rules" button in the Firewall Rules panel header +3. Select a previously exported JSON file +4. The system will process the import and show a summary of results + +## File Format + +The exported JSON file has the following structure: + +```json +{ + "version": "1.0", + "exported_at": "2024-01-15 14:30:25", + "total_rules": 5, + "rules": [ + { + "name": "Custom Web Server", + "proto": "tcp", + "port": "8080", + "ipAddress": "0.0.0.0/0" + }, + { + "name": "Database Access", + "proto": "tcp", + "port": "3306", + "ipAddress": "192.168.1.0/24" + } + ] +} +``` + +## Security Considerations + +- Only administrators can export/import firewall rules +- Default CyberPanel rules are excluded to prevent system conflicts +- Import process validates file format and rule data +- Failed imports are logged for troubleshooting +- Duplicate rules are automatically skipped + +## Error Handling + +The system provides comprehensive error handling: +- Invalid file format detection +- Missing required fields validation +- Individual rule import error tracking +- Detailed error messages for troubleshooting +- Import summary with counts of successful, skipped, and failed imports + +## Technical Implementation + +### Backend Components +- `exportFirewallRules()` method in `FirewallManager` +- `importFirewallRules()` method in `FirewallManager` +- New URL patterns for export/import endpoints +- File upload handling for import functionality + +### Frontend Components +- Export/Import buttons in firewall UI +- File download handling for exports +- File upload dialog for imports +- Progress indicators and error messaging +- Import summary display + +### Database Integration +- Uses existing `FirewallRules` model +- Maintains referential integrity +- Preserves rule relationships and constraints + +## Benefits + +1. **Time Efficiency**: Significantly reduces time to replicate firewall rules across servers +2. **Error Reduction**: Minimizes human error in manual rule creation +3. **Consistency**: Ensures identical security policies across multiple servers +4. **Backup**: Provides a way to backup and restore firewall configurations +5. **Migration**: Simplifies server migration and setup processes + +## Compatibility + +- Compatible with CyberPanel's existing firewall system +- Works with both TCP and UDP protocols +- Supports all IP address formats (single IPs, CIDR ranges) +- Maintains compatibility with existing firewall utilities + +## Future Enhancements + +Potential future improvements could include: +- Rule conflict detection and resolution +- Selective rule import (choose specific rules) +- Rule templates and presets +- Bulk rule management +- Integration with configuration management tools diff --git a/firewall/firewallManager.py b/firewall/firewallManager.py index 5375343f8..cec9ef97d 100644 --- a/firewall/firewallManager.py +++ b/firewall/firewallManager.py @@ -168,6 +168,79 @@ class FirewallManager: final_json = json.dumps(final_dic) return HttpResponse(final_json) + def editRule(self, userID = None, data = None): + """ + Edit an existing firewall rule + """ + try: + currentACL = ACLManager.loadedACL(userID) + + if currentACL['admin'] == 1: + pass + else: + return ACLManager.loadErrorJson('edit_status', 0) + + ruleID = data['id'] + newRuleName = data['ruleName'] + newRuleProtocol = data['ruleProtocol'] + newRulePort = data['rulePort'] + newRuleIP = data['ruleIP'] + + # Get the existing rule + try: + existingRule = FirewallRules.objects.get(id=ruleID) + except FirewallRules.DoesNotExist: + final_dic = {'status': 0, 'edit_status': 0, 'error_message': 'Rule not found'} + final_json = json.dumps(final_dic) + return HttpResponse(final_json) + + # Store old values for system firewall update + oldProtocol = existingRule.proto + oldPort = existingRule.port + oldIP = existingRule.ipAddress + + # Check if any values actually changed + if (existingRule.name == newRuleName and + existingRule.proto == newRuleProtocol and + existingRule.port == newRulePort and + existingRule.ipAddress == newRuleIP): + final_dic = {'status': 1, 'edit_status': 1, 'error_message': "No changes detected"} + final_json = json.dumps(final_dic) + return HttpResponse(final_json) + + # Check if another rule with the same name already exists (excluding current rule) + if existingRule.name != newRuleName: + duplicateRule = FirewallRules.objects.filter(name=newRuleName).exclude(id=ruleID).first() + if duplicateRule: + final_dic = {'status': 0, 'edit_status': 0, 'error_message': f'A rule with name "{newRuleName}" already exists'} + final_json = json.dumps(final_dic) + return HttpResponse(final_json) + + # Update the rule in the system firewall + # First remove the old rule + FirewallUtilities.deleteRule(oldProtocol, oldPort, oldIP) + + # Then add the new rule + FirewallUtilities.addRule(newRuleProtocol, newRulePort, newRuleIP) + + # Update the database record + existingRule.name = newRuleName + existingRule.proto = newRuleProtocol + existingRule.port = newRulePort + existingRule.ipAddress = newRuleIP + existingRule.save() + + logging.CyberCPLogFileWriter.writeToFile(f"Firewall rule edited successfully. ID: {ruleID}, Name: {newRuleName}") + + final_dic = {'status': 1, 'edit_status': 1, 'error_message': "None"} + final_json = json.dumps(final_dic) + return HttpResponse(final_json) + + except BaseException as msg: + final_dic = {'status': 0, 'edit_status': 0, 'error_message': str(msg)} + final_json = json.dumps(final_dic) + return HttpResponse(final_json) + def reloadFirewall(self, userID = None, data = None): try: @@ -1755,6 +1828,164 @@ class FirewallManager: final_json = json.dumps(final_dic) return HttpResponse(final_json) + def exportFirewallRules(self, userID = None): + """ + Export all custom firewall rules to a JSON file, excluding default CyberPanel rules + """ + try: + currentACL = ACLManager.loadedACL(userID) + + if currentACL['admin'] == 1: + pass + else: + return ACLManager.loadErrorJson('exportStatus', 0) + + # Get all firewall rules + rules = FirewallRules.objects.all() + + # Default CyberPanel rules to exclude + default_rules = ['CyberPanel Admin', 'SSHCustom'] + + # Filter out default rules + custom_rules = [] + for rule in rules: + if rule.name not in default_rules: + custom_rules.append({ + 'name': rule.name, + 'proto': rule.proto, + 'port': rule.port, + 'ipAddress': rule.ipAddress + }) + + # Create export data with metadata + export_data = { + 'version': '1.0', + 'exported_at': time.strftime('%Y-%m-%d %H:%M:%S'), + 'total_rules': len(custom_rules), + 'rules': custom_rules + } + + # Create JSON response with file download + json_content = json.dumps(export_data, indent=2) + + logging.CyberCPLogFileWriter.writeToFile(f"Firewall rules exported successfully. Total rules: {len(custom_rules)}") + + # Return file as download + response = HttpResponse(json_content, content_type='application/json') + response['Content-Disposition'] = f'attachment; filename="firewall_rules_export_{int(time.time())}.json"' + + return response + + except BaseException as msg: + final_dic = {'exportStatus': 0, 'error_message': str(msg)} + final_json = json.dumps(final_dic) + return HttpResponse(final_json) + + def importFirewallRules(self, userID = None, data = None): + """ + Import firewall rules from a JSON file + """ + try: + currentACL = ACLManager.loadedACL(userID) + + if currentACL['admin'] == 1: + pass + else: + return ACLManager.loadErrorJson('importStatus', 0) + + # Handle file upload + if hasattr(self.request, 'FILES') and 'import_file' in self.request.FILES: + import_file = self.request.FILES['import_file'] + + # Read file content + import_data = json.loads(import_file.read().decode('utf-8')) + else: + # Fallback to file path method + import_file_path = data.get('import_file_path', '') + + if not import_file_path or not os.path.exists(import_file_path): + final_dic = {'importStatus': 0, 'error_message': 'Import file not found or invalid path'} + final_json = json.dumps(final_dic) + return HttpResponse(final_json) + + # Read and parse the import file + with open(import_file_path, 'r') as f: + import_data = json.load(f) + + # Validate the import data structure + if 'rules' not in import_data: + final_dic = {'importStatus': 0, 'error_message': 'Invalid import file format. Missing rules array.'} + final_json = json.dumps(final_dic) + return HttpResponse(final_json) + + imported_count = 0 + skipped_count = 0 + error_count = 0 + errors = [] + + # Default CyberPanel rules to exclude from import + default_rules = ['CyberPanel Admin', 'SSHCustom'] + + for rule_data in import_data['rules']: + try: + # Skip default rules + if rule_data.get('name', '') in default_rules: + skipped_count += 1 + continue + + # Check if rule already exists + existing_rule = FirewallRules.objects.filter( + name=rule_data['name'], + proto=rule_data['proto'], + port=rule_data['port'], + ipAddress=rule_data['ipAddress'] + ).first() + + if existing_rule: + skipped_count += 1 + continue + + # Add the rule to the system firewall + FirewallUtilities.addRule( + rule_data['proto'], + rule_data['port'], + rule_data['ipAddress'] + ) + + # Add the rule to the database + new_rule = FirewallRules( + name=rule_data['name'], + proto=rule_data['proto'], + port=rule_data['port'], + ipAddress=rule_data['ipAddress'] + ) + new_rule.save() + + imported_count += 1 + + except Exception as e: + error_count += 1 + errors.append(f"Rule '{rule_data.get('name', 'Unknown')}': {str(e)}") + logging.CyberCPLogFileWriter.writeToFile(f"Error importing rule {rule_data.get('name', 'Unknown')}: {str(e)}") + + logging.CyberCPLogFileWriter.writeToFile(f"Firewall rules import completed. Imported: {imported_count}, Skipped: {skipped_count}, Errors: {error_count}") + + final_dic = { + 'importStatus': 1, + 'error_message': "None", + 'imported_count': imported_count, + 'skipped_count': skipped_count, + 'error_count': error_count, + 'errors': errors + } + final_json = json.dumps(final_dic) + return HttpResponse(final_json) + + except BaseException as msg: + final_dic = {'importStatus': 0, 'error_message': str(msg)} + final_json = json.dumps(final_dic) + return HttpResponse(final_json) + diff --git a/firewall/static/firewall/firewall.js b/firewall/static/firewall/firewall.js index 1d666ed78..e70ca46e8 100644 --- a/firewall/static/firewall/firewall.js +++ b/firewall/static/firewall/firewall.js @@ -16,6 +16,10 @@ app.controller('firewallController', function ($scope, $http) { $scope.couldNotConnect = true; $scope.rulesDetails = false; + // Edit modal variables + $scope.showEditModal = false; + $scope.editingRule = {}; + firewallStatus(); populateCurrentRecords(); @@ -503,6 +507,249 @@ app.controller('firewallController', function ($scope, $http) { }; + // Export/Import Functions + $scope.exportRules = function () { + $scope.rulesLoading = false; + $scope.actionFailed = true; + $scope.actionSuccess = true; + + url = "/firewall/exportFirewallRules"; + + var data = {}; + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data, config).then(exportSuccess, exportError); + + function exportSuccess(response) { + $scope.rulesLoading = true; + + // Check if response is JSON (error) or file download + if (typeof response.data === 'string' && response.data.includes('{')) { + try { + var errorData = JSON.parse(response.data); + if (errorData.exportStatus === 0) { + $scope.actionFailed = false; + $scope.actionSuccess = true; + $scope.errorMessage = errorData.error_message; + return; + } + } catch (e) { + // If not JSON, assume it's the file content + } + } + + // If we get here, it's a successful file download + $scope.actionFailed = true; + $scope.actionSuccess = false; + } + + function exportError(response) { + $scope.rulesLoading = true; + $scope.actionFailed = false; + $scope.actionSuccess = true; + $scope.errorMessage = "Could not connect to server. Please refresh this page."; + } + }; + + $scope.importRules = function () { + // Create file input element + var input = document.createElement('input'); + input.type = 'file'; + input.accept = '.json'; + input.style.display = 'none'; + + input.onchange = function(event) { + var file = event.target.files[0]; + if (file) { + var reader = new FileReader(); + reader.onload = function(e) { + try { + var importData = JSON.parse(e.target.result); + + // Validate file format + if (!importData.rules || !Array.isArray(importData.rules)) { + $scope.$apply(function() { + $scope.actionFailed = false; + $scope.actionSuccess = true; + $scope.errorMessage = "Invalid import file format. Please select a valid firewall rules export file."; + }); + return; + } + + // Upload file to server + uploadImportFile(file); + } catch (error) { + $scope.$apply(function() { + $scope.actionFailed = false; + $scope.actionSuccess = true; + $scope.errorMessage = "Invalid JSON file. Please select a valid firewall rules export file."; + }); + } + }; + reader.readAsText(file); + } + }; + + document.body.appendChild(input); + input.click(); + document.body.removeChild(input); + }; + + function uploadImportFile(file) { + $scope.rulesLoading = false; + $scope.actionFailed = true; + $scope.actionSuccess = true; + + var formData = new FormData(); + formData.append('import_file', file); + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken'), + 'Content-Type': undefined + }, + transformRequest: angular.identity + }; + + $http.post("/firewall/importFirewallRules", formData, config).then(importSuccess, importError); + + function importSuccess(response) { + $scope.rulesLoading = true; + + if (response.data.importStatus === 1) { + $scope.actionFailed = true; + $scope.actionSuccess = false; + + // Refresh rules list + populateCurrentRecords(); + + // Show import summary + var summary = `Import completed successfully!\n` + + `Imported: ${response.data.imported_count} rules\n` + + `Skipped: ${response.data.skipped_count} rules\n` + + `Errors: ${response.data.error_count} rules`; + + if (response.data.errors && response.data.errors.length > 0) { + summary += `\n\nErrors:\n${response.data.errors.join('\n')}`; + } + + alert(summary); + } else { + $scope.actionFailed = false; + $scope.actionSuccess = true; + $scope.errorMessage = response.data.error_message; + } + } + + function importError(response) { + $scope.rulesLoading = true; + $scope.actionFailed = false; + $scope.actionSuccess = true; + $scope.errorMessage = "Could not connect to server. Please refresh this page."; + } + } + + // Edit Rule Functions + $scope.editRule = function(rule) { + $scope.editingRule = { + id: rule.id, + name: rule.name, + proto: rule.proto, + port: rule.port, + ipAddress: rule.ipAddress + }; + $scope.showEditModal = true; + }; + + $scope.closeEditModal = function() { + $scope.showEditModal = false; + $scope.editingRule = {}; + }; + + $scope.saveEditedRule = function() { + // Basic validation + if (!$scope.editingRule.name || !$scope.editingRule.port || !$scope.editingRule.ipAddress) { + $scope.actionFailed = false; + $scope.actionSuccess = true; + $scope.errorMessage = "Please fill in all required fields."; + return; + } + + $scope.rulesLoading = false; + $scope.actionFailed = true; + $scope.actionSuccess = true; + + url = "/firewall/editRule"; + + var data = { + id: $scope.editingRule.id, + ruleName: $scope.editingRule.name, + ruleProtocol: $scope.editingRule.proto, + rulePort: $scope.editingRule.port, + ruleIP: $scope.editingRule.ipAddress + }; + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data, config).then(editSuccess, editError); + + function editSuccess(response) { + $scope.rulesLoading = true; + + if (response.data.edit_status === 1) { + // Close modal and refresh rules + $scope.closeEditModal(); + populateCurrentRecords(); + + $scope.actionFailed = true; + $scope.actionSuccess = false; + } else { + $scope.actionFailed = false; + $scope.actionSuccess = true; + $scope.errorMessage = response.data.error_message; + } + } + + function editError(response) { + $scope.rulesLoading = true; + $scope.actionFailed = false; + $scope.actionSuccess = true; + $scope.errorMessage = "Could not connect to server. Please refresh this page."; + } + }; + + // Close modal when clicking outside or pressing Escape + $scope.$on('$locationChangeStart', function() { + $scope.closeEditModal(); + }); + + // Keyboard support for modal + $(document).on('keydown', function(e) { + if ($scope.showEditModal && e.keyCode === 27) { // Escape key + $scope.$apply(function() { + $scope.closeEditModal(); + }); + } + }); + + // Focus management for modal + $scope.$watch('showEditModal', function(newVal) { + if (newVal) { + setTimeout(function() { + $('#editRuleModal input[name="ruleName"]').focus(); + }, 100); + } + }); + }); diff --git a/firewall/templates/firewall/firewall.html b/firewall/templates/firewall/firewall.html index dace7774b..da14c3f41 100644 --- a/firewall/templates/firewall/firewall.html +++ b/firewall/templates/firewall/firewall.html @@ -421,6 +421,205 @@ transform: scale(1.1); } + .btn-edit { + background: var(--bg-info-light, #dbeafe); + color: var(--info-color, #3b82f6); + width: 32px; + height: 32px; + border-radius: 6px; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s ease; + font-weight: 700; + border: none; + } + + .btn-edit:hover { + background: var(--info-color, #3b82f6); + color: var(--text-light, white); + transform: scale(1.1); + } + + /* Export/Import Buttons */ + .export-import-buttons { + display: flex; + gap: 0.75rem; + align-items: center; + } + + .btn-export, .btn-import { + padding: 0.5rem 1rem; + border-radius: 8px; + font-weight: 500; + font-size: 0.875rem; + cursor: pointer; + transition: all 0.3s ease; + border: none; + display: inline-flex; + align-items: center; + gap: 0.5rem; + background: rgba(255, 255, 255, 0.2); + color: var(--text-light, white); + backdrop-filter: blur(10px); + } + + .btn-export:hover, .btn-import:hover { + background: rgba(255, 255, 255, 0.3); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + } + + .btn-export:disabled, .btn-import:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; + } + + /* Edit Modal Styles */ + .modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + animation: fadeIn 0.3s ease-out; + } + + @keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } + } + + .modal-content { + background: var(--bg-secondary, white); + border-radius: 16px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + max-width: 500px; + width: 90%; + max-height: 90vh; + overflow-y: auto; + animation: slideInUp 0.3s ease-out; + } + + @keyframes slideInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + .modal-header { + padding: 1.5rem 2rem; + border-bottom: 1px solid var(--border-color, #e8e9ff); + display: flex; + align-items: center; + justify-content: space-between; + background: linear-gradient(135deg, var(--firewall-gradient-start, #ef4444) 0%, var(--firewall-gradient-end, #dc2626) 100%); + color: var(--text-light, white); + border-radius: 16px 16px 0 0; + } + + .modal-title { + font-size: 1.25rem; + font-weight: 600; + display: flex; + align-items: center; + gap: 0.75rem; + margin: 0; + } + + .modal-close { + background: rgba(255, 255, 255, 0.2); + border: none; + color: var(--text-light, white); + width: 32px; + height: 32px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s ease; + } + + .modal-close:hover { + background: rgba(255, 255, 255, 0.3); + transform: scale(1.1); + } + + .modal-body { + padding: 2rem; + } + + .edit-rule-form { + display: grid; + grid-template-columns: 1fr; + gap: 1.5rem; + } + + .modal-footer { + padding: 1.5rem 2rem; + border-top: 1px solid var(--border-color, #e8e9ff); + display: flex; + gap: 1rem; + justify-content: flex-end; + background: var(--bg-tertiary, #f8f9ff); + border-radius: 0 0 16px 16px; + } + + .btn-cancel, .btn-save { + padding: 0.75rem 1.5rem; + border-radius: 8px; + font-weight: 500; + font-size: 0.875rem; + cursor: pointer; + transition: all 0.3s ease; + border: none; + display: inline-flex; + align-items: center; + gap: 0.5rem; + min-width: 120px; + justify-content: center; + } + + .btn-cancel { + background: var(--bg-muted, #f1f5f9); + color: var(--text-secondary, #475569); + } + + .btn-cancel:hover { + background: var(--bg-muted-hover, #e2e8f0); + transform: translateY(-2px); + } + + .btn-save { + background: var(--firewall-accent, #ef4444); + color: var(--text-light, white); + } + + .btn-save:hover { + background: var(--firewall-accent-dark, #dc2626); + transform: translateY(-2px); + box-shadow: 0 4px 12px var(--firewall-shadow, rgba(239, 68, 68, 0.3)); + } + + .btn-save:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; + } + .empty-state { text-align: center; padding: 4rem 2rem; @@ -600,7 +799,25 @@ {% trans "Firewall Rules" %} -
+
+
+
+ + +
+
@@ -680,12 +897,20 @@ {$ rule.port $} - +
+ + +
@@ -717,6 +942,75 @@ + + + {% endblock %} \ No newline at end of file diff --git a/firewall/urls.py b/firewall/urls.py index 56e9fc187..8cf877b4c 100644 --- a/firewall/urls.py +++ b/firewall/urls.py @@ -32,6 +32,13 @@ urlpatterns = [ path('modSecRulesPacks', views.modSecRulesPacks, name='modSecRulesPacks'), path('getOWASPAndComodoStatus', views.getOWASPAndComodoStatus, name='getOWASPAndComodoStatus'), path('installModSecRulesPack', views.installModSecRulesPack, name='installModSecRulesPack'), + + # Firewall Export/Import + path('exportFirewallRules', views.exportFirewallRules, name='exportFirewallRules'), + path('importFirewallRules', views.importFirewallRules, name='importFirewallRules'), + + # Firewall Rule Edit + path('editRule', views.editRule, name='editRule'), path('getRulesFiles', views.getRulesFiles, name='getRulesFiles'), path('enableDisableRuleFile', views.enableDisableRuleFile, name='enableDisableRuleFile'), diff --git a/firewall/views.py b/firewall/views.py index d7f91dd93..369f94602 100644 --- a/firewall/views.py +++ b/firewall/views.py @@ -648,3 +648,36 @@ def saveLitespeed_conf(request): return fm.saveLitespeed_conf(userID, json.loads(request.body)) except KeyError: return redirect(loadLoginPage) + + +def exportFirewallRules(request): + try: + userID = request.session['userID'] + fm = FirewallManager() + return fm.exportFirewallRules(userID) + except KeyError: + return redirect(loadLoginPage) + + +def importFirewallRules(request): + try: + userID = request.session['userID'] + fm = FirewallManager(request) + + # Handle file upload + if request.method == 'POST' and 'import_file' in request.FILES: + return fm.importFirewallRules(userID, None) + else: + # Handle JSON data + return fm.importFirewallRules(userID, json.loads(request.body)) + except KeyError: + return redirect(loadLoginPage) + + +def editRule(request): + try: + userID = request.session['userID'] + fm = FirewallManager() + return fm.editRule(userID, json.loads(request.body)) + except KeyError: + return redirect(loadLoginPage) \ No newline at end of file diff --git a/key.pem b/key.pem deleted file mode 100644 index b83a0a6bf..000000000 --- a/key.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDNDCCAhwCCQDEgz2Vkmv5NDANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJV -UzEPMA0GA1UECAwGRGVuaWFsMRQwEgYDVQQHDAtTcHJpbmdmaWVsZDEMMAoGA1UE -CgwDRGlzMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wHhcNMjIwODI2MTEwNzEy -WhcNMzIwODIzMTEwNzEyWjBcMQswCQYDVQQGEwJVUzEPMA0GA1UECAwGRGVuaWFs -MRQwEgYDVQQHDAtTcHJpbmdmaWVsZDEMMAoGA1UECgwDRGlzMRgwFgYDVQQDDA93 -d3cuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCq -BesL5DNNansmBYyn0DSqJyBOwGEIH/Ur4KFbKICrhy376gec2UyGG5lurgQa8Nz6 -Gt7Z1B9LbLVE3bXS1f82bJHyPFUP8WmQuC+/3ZMRPFiG7/4n//0QwMptkDvGb5E1 -0NXfJjuGHNfpVaHAs83v9mvUKc7oOjxwa+lbkhobD8HKByzAi/fpD90WQS9JRUJX -8lGquw+k5flL31AkqCyZOJw22VEoIpoF7RSh0xZvpsLze6G1thY5R27YWChcOR0E -m9/TUZ9/oCaP3WBvCldjr5OT6eZxJJzlt1jYndQKUyO5OtaJuNd4MkCNz9u9wwjE -CfZF7VQj8FABR9tqcVEfAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGCNMmGXem6k -iqvJG2eFvuSdIh+x7x8EnXpRi2lW0ZqLYZfZuiMQL1fedhVoix+rzo9/Qhj6BTnt -5cC5oexhj/s76gqehNatMC0HAbIcK+MpvmwNoA/7U/bQAlbNxR3aLKSV0/B5YlTP -9yUoMtBqGEiesqAVAD26jOG5Ch1fHHElPtp3rE6qxGsAL0Eu+2Gezq3OqW2ejlvY -hZFpB/ZEynmYDUjT02+2J+3bAhfGaeUXC75YsbyfRAdc0OHa9/r7RhK+tgzmUhJt -sKUDW2OIwOTumUOSDgh1ayeTBddRAcMyIoGFeHF9degfJFiSo4vQrmaqFr9/bUFp -pn+y1/A3gNY= ------END CERTIFICATE----- From 237cf93db0f4b743a9c183689617efe01aaefa5f Mon Sep 17 00:00:00 2001 From: Master3395 Date: Wed, 17 Sep 2025 01:05:48 +0200 Subject: [PATCH 11/11] Enhance OS detection and support for additional distributions - Updated OS detection logic to include CentOS Stream and Red Hat Enterprise Linux. - Added support for AlmaLinux 9 and 10, as well as Debian 11, 12, and 13. - Improved error messages to reflect the expanded list of supported operating systems. - Adjusted package management handling for Debian to treat it as Ubuntu for compatibility. --- CPScripts/mailscannerinstaller.sh | 46 +++++++++++++++++++++++++---- CPScripts/mailscanneruninstaller.sh | 23 +++++++++++---- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/CPScripts/mailscannerinstaller.sh b/CPScripts/mailscannerinstaller.sh index 780486854..a7b250751 100644 --- a/CPScripts/mailscannerinstaller.sh +++ b/CPScripts/mailscannerinstaller.sh @@ -47,21 +47,29 @@ fi ### OS Detection Server_OS="" Server_OS_Version="" -if grep -q -E "CentOS Linux 7|CentOS Linux 8" /etc/os-release ; then +if grep -q -E "CentOS Linux 7|CentOS Linux 8|CentOS Stream" /etc/os-release ; then Server_OS="CentOS" -elif grep -q -E "AlmaLinux-8|AlmaLinux-9|AlmaLinux-10" /etc/os-release ; then +elif grep -q "Red Hat Enterprise Linux" /etc/os-release ; then + Server_OS="RedHat" +elif grep -q "AlmaLinux-8" /etc/os-release ; then + Server_OS="AlmaLinux" +elif grep -q "AlmaLinux-9" /etc/os-release ; then + Server_OS="AlmaLinux" +elif grep -q "AlmaLinux-10" /etc/os-release ; then Server_OS="AlmaLinux" elif grep -q -E "CloudLinux 7|CloudLinux 8" /etc/os-release ; then Server_OS="CloudLinux" elif grep -q -E "Rocky Linux" /etc/os-release ; then Server_OS="RockyLinux" -elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04" /etc/os-release ; then +elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04|Ubuntu 24.04" /etc/os-release ; then Server_OS="Ubuntu" +elif grep -q -E "Debian GNU/Linux 11|Debian GNU/Linux 12|Debian GNU/Linux 13" /etc/os-release ; then + Server_OS="Debian" elif grep -q -E "openEuler 20.03|openEuler 22.03" /etc/os-release ; then Server_OS="openEuler" else echo -e "Unable to detect your system..." - echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, CentOS 7, CentOS 8, AlmaLinux 8, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n" + echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, Debian 11, Debian 12, Debian 13, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, RockyLinux 9, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n" exit fi @@ -69,10 +77,13 @@ Server_OS_Version=$(grep VERSION_ID /etc/os-release | awk -F[=,] '{print $2}' | echo -e "System: $Server_OS $Server_OS_Version detected...\n" -if [[ $Server_OS = "CloudLinux" ]] || [[ "$Server_OS" = "AlmaLinux" ]] || [[ "$Server_OS" = "RockyLinux" ]] ; then +if [[ $Server_OS = "CloudLinux" ]] || [[ "$Server_OS" = "AlmaLinux" ]] || [[ "$Server_OS" = "RockyLinux" ]] || [[ "$Server_OS" = "RedHat" ]] ; then Server_OS="CentOS" #CloudLinux gives version id like 7.8, 7.9, so cut it to show first number only - #treat CloudLinux, Rocky and Alma as CentOS + #treat CloudLinux, Rocky, Alma and RedHat as CentOS +elif [[ "$Server_OS" = "Debian" ]] ; then + Server_OS="Ubuntu" + #Treat Debian as Ubuntu for package management (both use apt-get) fi if [[ $Server_OS = "CentOS" ]] && [[ "$Server_OS_Version" = "7" ]] ; then @@ -114,6 +125,29 @@ elif [[ $Server_OS = "CentOS" ]] && [[ "$Server_OS_Version" = "8" ]] ; then freshclam -v +elif [[ $Server_OS = "CentOS" ]] && [[ "$Server_OS_Version" = "9" ]] ; then + + setenforce 0 + dnf install -y perl dnf-utils perl-CPAN + dnf --enablerepo=crb install -y perl-IO-stringy + dnf install -y gcc cpp perl bzip2 zip make patch automake rpm-build perl-Archive-Zip perl-Filesys-Df perl-OLE-Storage_Lite perl-Net-CIDR perl-DBI perl-MIME-tools perl-DBD-SQLite binutils glibc-devel perl-Filesys-Df zlib unzip zlib-devel wget mlocate clamav clamav-update "perl(DBD::mysql)" + + # Install unrar for AlmaLinux 9 (using EPEL) + dnf install -y unrar + + export PERL_MM_USE_DEFAULT=1 + curl -L https://cpanmin.us | perl - App::cpanminus + + perl -MCPAN -e 'install Encoding::FixLatin' + perl -MCPAN -e 'install Digest::SHA1' + perl -MCPAN -e 'install Geo::IP' + perl -MCPAN -e 'install Razor2::Client::Agent' + perl -MCPAN -e 'install Sys::Hostname::Long' + perl -MCPAN -e 'install Sys::SigAction' + perl -MCPAN -e 'install Net::Patricia' + + freshclam -v + elif [ "$CLNVERSION" = "ID=\"cloudlinux\"" ]; then setenforce 0 diff --git a/CPScripts/mailscanneruninstaller.sh b/CPScripts/mailscanneruninstaller.sh index 9eebad670..eed23ddbb 100644 --- a/CPScripts/mailscanneruninstaller.sh +++ b/CPScripts/mailscanneruninstaller.sh @@ -4,21 +4,29 @@ ### OS Detection Server_OS="" Server_OS_Version="" -if grep -q -E "CentOS Linux 7|CentOS Linux 8" /etc/os-release ; then +if grep -q -E "CentOS Linux 7|CentOS Linux 8|CentOS Stream" /etc/os-release ; then Server_OS="CentOS" -elif grep -q -E "AlmaLinux-8|AlmaLinux-9|AlmaLinux-10" /etc/os-release ; then +elif grep -q "Red Hat Enterprise Linux" /etc/os-release ; then + Server_OS="RedHat" +elif grep -q "AlmaLinux-8" /etc/os-release ; then + Server_OS="AlmaLinux" +elif grep -q "AlmaLinux-9" /etc/os-release ; then + Server_OS="AlmaLinux" +elif grep -q "AlmaLinux-10" /etc/os-release ; then Server_OS="AlmaLinux" elif grep -q -E "CloudLinux 7|CloudLinux 8" /etc/os-release ; then Server_OS="CloudLinux" elif grep -q -E "Rocky Linux" /etc/os-release ; then Server_OS="RockyLinux" -elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04" /etc/os-release ; then +elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04|Ubuntu 24.04" /etc/os-release ; then Server_OS="Ubuntu" +elif grep -q -E "Debian GNU/Linux 11|Debian GNU/Linux 12|Debian GNU/Linux 13" /etc/os-release ; then + Server_OS="Debian" elif grep -q -E "openEuler 20.03|openEuler 22.03" /etc/os-release ; then Server_OS="openEuler" else echo -e "Unable to detect your system..." - echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, CentOS 7, CentOS 8, AlmaLinux 8, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n" + echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, Debian 11, Debian 12, Debian 13, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, RockyLinux 9, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n" exit fi @@ -26,10 +34,13 @@ Server_OS_Version=$(grep VERSION_ID /etc/os-release | awk -F[=,] '{print $2}' | echo -e "System: $Server_OS $Server_OS_Version detected...\n" -if [[ $Server_OS = "CloudLinux" ]] || [[ "$Server_OS" = "AlmaLinux" ]] || [[ "$Server_OS" = "RockyLinux" ]] ; then +if [[ $Server_OS = "CloudLinux" ]] || [[ "$Server_OS" = "AlmaLinux" ]] || [[ "$Server_OS" = "RockyLinux" ]] || [[ "$Server_OS" = "RedHat" ]] ; then Server_OS="CentOS" #CloudLinux gives version id like 7.8, 7.9, so cut it to show first number only - #treat CloudLinux, Rocky and Alma as CentOS + #treat CloudLinux, Rocky, Alma and RedHat as CentOS +elif [[ "$Server_OS" = "Debian" ]] ; then + Server_OS="Ubuntu" + #Treat Debian as Ubuntu for package management (both use apt-get) fi systemctl stop mailscanner