Merge pull request #1628 from master3395/v2.5.5-dev
Implement Catch-All Email Configuration Management https://github.com/usmannasir/cyberpanel/issues/1626 https://github.com/usmannasir/cyberpanel/issues/1627
This commit is contained in:
commit
47dc442ace
File diff suppressed because it is too large
Load Diff
|
|
@ -1088,24 +1088,25 @@ class preFlightsChecks:
|
|||
self.stdOut(f"Detected platform: {platform}", 1)
|
||||
|
||||
# Platform-specific URLs and checksums (OpenLiteSpeed v1.8.4.1 - v2.0.5 Static Build)
|
||||
# Module Build Date: December 28, 2025 - v2.2.0 Brute Force with Progressive Throttle
|
||||
BINARY_CONFIGS = {
|
||||
'rhel8': {
|
||||
'url': 'https://cyberpanel.net/openlitespeed-phpconfig-x86_64-rhel8-static',
|
||||
'sha256': '6ce688a237615102cc1603ee1999b3cede0ff3482d31e1f65705e92396d34b3a',
|
||||
'module_url': None, # RHEL 8 doesn't have module (use RHEL 9 if needed)
|
||||
'module_sha256': None
|
||||
'module_url': 'https://cyberpanel.net/binaries/rhel8/cyberpanel_ols.so',
|
||||
'module_sha256': '7c33d89c7fbcd3ed7b0422fee3f49b5e041713c2c2b7316a5774f6defa147572'
|
||||
},
|
||||
'rhel9': {
|
||||
'url': 'https://cyberpanel.net/openlitespeed-phpconfig-x86_64-rhel9-static',
|
||||
'sha256': '90468fb38767505185013024678d9144ae13100d2355097657f58719d98fbbc4',
|
||||
'module_url': 'https://cyberpanel.net/cyberpanel_ols_x86_64_rhel.so',
|
||||
'module_sha256': '127227db81bcbebf80b225fc747b69cfcd4ad2f01cea486aa02d5c9ba6c18109'
|
||||
'sha256': '709093d99d5d3e789134c131893614968e17eefd9ade2200f811d9b076b2f02e',
|
||||
'module_url': 'https://cyberpanel.net/binaries/rhel9/cyberpanel_ols.so',
|
||||
'module_sha256': 'ae65337e2d13babc0c675bb4264d469daffa2efb7627c9bf39ac59e42e3ebede'
|
||||
},
|
||||
'ubuntu': {
|
||||
'url': 'https://cyberpanel.net/openlitespeed-phpconfig-x86_64-ubuntu-static',
|
||||
'sha256': '89aaf66474e78cb3c1666784e0e7a417550bd317e6ab148201bdc318d36710cb',
|
||||
'module_url': 'https://cyberpanel.net/cyberpanel_ols_x86_64_ubuntu.so',
|
||||
'module_sha256': 'e7734f1e6226c2a0a8e00c1f6534ea9f577df9081b046736a774b1c52c28e7e5'
|
||||
'module_url': 'https://cyberpanel.net/binaries/ubuntu/cyberpanel_ols.so',
|
||||
'module_sha256': '62978ede1f174dd2885e5227a3d9cc463d0c27acd77cfc23743d7309ee0c54ea'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1471,7 +1472,9 @@ module cyberpanel_ols {
|
|||
"""Change MySQL root password"""
|
||||
try:
|
||||
if self.remotemysql == 'OFF':
|
||||
passwordCMD = "use mysql;DROP DATABASE IF EXISTS test;DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%%';GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY '%s';flush privileges;" % (self.mysql_Root_password)
|
||||
# Use ALTER USER syntax (compatible with MariaDB 10.4+ and MySQL 5.7+)
|
||||
# GRANT ... IDENTIFIED BY is deprecated in MariaDB 10.4+ and removed in 10.11+
|
||||
passwordCMD = "use mysql;DROP DATABASE IF EXISTS test;DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%%';ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;flush privileges;" % (self.mysql_Root_password)
|
||||
|
||||
# Try socket authentication first (for fresh MariaDB installations)
|
||||
socket_commands = ['sudo mysql', 'sudo mariadb', 'sudo /usr/bin/mysql', 'sudo /usr/bin/mariadb']
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import _thread
|
|||
try:
|
||||
from dns.models import Domains as dnsDomains
|
||||
from dns.models import Records as dnsRecords
|
||||
from mailServer.models import Forwardings, Pipeprograms
|
||||
from mailServer.models import Forwardings, Pipeprograms, CatchAllEmail
|
||||
from plogical.acl import ACLManager
|
||||
from plogical.dnsUtilities import DNS
|
||||
from loginSystem.models import Administrator
|
||||
|
|
@ -2030,6 +2030,151 @@ protocol sieve {
|
|||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def fetchCatchAllConfig(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)
|
||||
domain = data['domain']
|
||||
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
if ACLManager.checkOwnership(domain, admin, currentACL) == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
try:
|
||||
domainObj = Domains.objects.get(domain=domain)
|
||||
catchAll = CatchAllEmail.objects.get(domain=domainObj)
|
||||
data_ret = {
|
||||
'status': 1,
|
||||
'fetchStatus': 1,
|
||||
'configured': 1,
|
||||
'destination': catchAll.destination,
|
||||
'enabled': catchAll.enabled
|
||||
}
|
||||
except CatchAllEmail.DoesNotExist:
|
||||
data_ret = {
|
||||
'status': 1,
|
||||
'fetchStatus': 1,
|
||||
'configured': 0
|
||||
}
|
||||
except Domains.DoesNotExist:
|
||||
data_ret = {
|
||||
'status': 0,
|
||||
'fetchStatus': 0,
|
||||
'error_message': 'Domain not found in email system'
|
||||
}
|
||||
|
||||
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 saveCatchAllConfig(self):
|
||||
try:
|
||||
userID = self.request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0:
|
||||
return ACLManager.loadErrorJson('saveStatus', 0)
|
||||
|
||||
data = json.loads(self.request.body)
|
||||
domain = data['domain']
|
||||
destination = data['destination']
|
||||
enabled = data.get('enabled', True)
|
||||
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
if ACLManager.checkOwnership(domain, admin, currentACL) == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
# Validate destination email
|
||||
if '@' not in destination:
|
||||
data_ret = {'status': 0, 'saveStatus': 0, 'error_message': 'Invalid destination email address'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
domainObj = Domains.objects.get(domain=domain)
|
||||
|
||||
# Create or update catch-all config
|
||||
catchAll, created = CatchAllEmail.objects.update_or_create(
|
||||
domain=domainObj,
|
||||
defaults={'destination': destination, 'enabled': enabled}
|
||||
)
|
||||
|
||||
# Also add/update entry in Forwardings table for Postfix
|
||||
catchAllSource = '@' + domain
|
||||
if enabled:
|
||||
# Remove existing catch-all forwarding if any
|
||||
Forwardings.objects.filter(source=catchAllSource).delete()
|
||||
# Add new forwarding
|
||||
forwarding = Forwardings(source=catchAllSource, destination=destination)
|
||||
forwarding.save()
|
||||
else:
|
||||
# Remove catch-all forwarding when disabled
|
||||
Forwardings.objects.filter(source=catchAllSource).delete()
|
||||
|
||||
data_ret = {
|
||||
'status': 1,
|
||||
'saveStatus': 1,
|
||||
'message': 'Catch-all email configured successfully'
|
||||
}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'saveStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def deleteCatchAllConfig(self):
|
||||
try:
|
||||
userID = self.request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0:
|
||||
return ACLManager.loadErrorJson('deleteStatus', 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()
|
||||
|
||||
domainObj = Domains.objects.get(domain=domain)
|
||||
|
||||
# Delete catch-all config
|
||||
CatchAllEmail.objects.filter(domain=domainObj).delete()
|
||||
|
||||
# Remove from Forwardings table
|
||||
catchAllSource = '@' + domain
|
||||
Forwardings.objects.filter(source=catchAllSource).delete()
|
||||
|
||||
data_ret = {
|
||||
'status': 1,
|
||||
'deleteStatus': 1,
|
||||
'message': 'Catch-all email removed successfully'
|
||||
}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'deleteStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def refreshEmailDiskUsage(self):
|
||||
"""Refresh disk usage for all email accounts in a domain"""
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -49,3 +49,17 @@ class Transport(models.Model):
|
|||
class Pipeprograms(models.Model):
|
||||
source = models.CharField(max_length=80)
|
||||
destination = models.TextField()
|
||||
|
||||
class Meta:
|
||||
db_table = 'e_pipeprograms'
|
||||
|
||||
|
||||
class CatchAllEmail(models.Model):
|
||||
"""Stores catch-all email configuration per domain"""
|
||||
domain = models.OneToOneField(Domains, on_delete=models.CASCADE, primary_key=True, db_column='domain_id')
|
||||
destination = models.CharField(max_length=255)
|
||||
enabled = models.BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
db_table = 'e_catchall'
|
||||
managed = False
|
||||
|
|
|
|||
|
|
@ -38,4 +38,9 @@ urlpatterns = [
|
|||
|
||||
### disk usage refresh
|
||||
re_path(r'^refreshEmailDiskUsage$', views.refreshEmailDiskUsage, name='refreshEmailDiskUsage'),
|
||||
|
||||
## Catch-All Email
|
||||
re_path(r'^fetchCatchAllConfig$', views.fetchCatchAllConfig, name='fetchCatchAllConfig'),
|
||||
re_path(r'^saveCatchAllConfig$', views.saveCatchAllConfig, name='saveCatchAllConfig'),
|
||||
re_path(r'^deleteCatchAllConfig$', views.deleteCatchAllConfig, name='deleteCatchAllConfig'),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -262,6 +262,33 @@ def SaveEmailLimitsNew(request):
|
|||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def fetchCatchAllConfig(request):
|
||||
try:
|
||||
msM = MailServerManager(request)
|
||||
return msM.fetchCatchAllConfig()
|
||||
except KeyError as msg:
|
||||
data_ret = {'fetchStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def saveCatchAllConfig(request):
|
||||
try:
|
||||
msM = MailServerManager(request)
|
||||
return msM.saveCatchAllConfig()
|
||||
except KeyError as msg:
|
||||
data_ret = {'saveStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def deleteCatchAllConfig(request):
|
||||
try:
|
||||
msM = MailServerManager(request)
|
||||
return msM.deleteCatchAllConfig()
|
||||
except KeyError as msg:
|
||||
data_ret = {'deleteStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def refreshEmailDiskUsage(request):
|
||||
try:
|
||||
msM = MailServerManager(request)
|
||||
|
|
|
|||
|
|
@ -291,24 +291,26 @@ extprocessor docker{port} {{
|
|||
|
||||
@staticmethod
|
||||
def SetupN8NVhost(domain, port):
|
||||
"""Setup n8n vhost with proper proxy configuration including Origin header"""
|
||||
"""Setup n8n vhost with proper proxy configuration for OpenLiteSpeed"""
|
||||
try:
|
||||
vhost_path = f'/usr/local/lsws/conf/vhosts/{domain}/vhost.conf'
|
||||
|
||||
|
||||
if not os.path.exists(vhost_path):
|
||||
logging.writeToFile(f"Error: Vhost file not found at {vhost_path}")
|
||||
return False
|
||||
|
||||
|
||||
# Read existing vhost configuration
|
||||
with open(vhost_path, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
|
||||
# Check if context already exists
|
||||
if 'context / {' in content:
|
||||
logging.writeToFile("Context already exists, skipping...")
|
||||
return True
|
||||
|
||||
|
||||
# Add proxy context with proper headers for n8n
|
||||
# NOTE: Do NOT include "RequestHeader set Origin" - OpenLiteSpeed cannot override
|
||||
# browser Origin headers, which is why NODE_ENV=development is required
|
||||
proxy_context = f'''
|
||||
|
||||
# N8N Proxy Configuration
|
||||
|
|
@ -322,7 +324,6 @@ context / {{
|
|||
RequestHeader set X-Forwarded-For $ip
|
||||
RequestHeader set X-Forwarded-Proto https
|
||||
RequestHeader set X-Forwarded-Host "{domain}"
|
||||
RequestHeader set Origin "{domain}, {domain}"
|
||||
RequestHeader set Host "{domain}"
|
||||
END_extraHeaders
|
||||
}}
|
||||
|
|
@ -1370,7 +1371,7 @@ services:
|
|||
'DB_POSTGRESDB_PASSWORD': self.data['MySQLPassword'],
|
||||
'N8N_HOST': '0.0.0.0',
|
||||
'N8N_PORT': '5678',
|
||||
'NODE_ENV': 'production',
|
||||
'NODE_ENV': 'development', # Required for OpenLiteSpeed compatibility - OLS cannot override browser Origin headers which n8n v1.87.0+ validates in production mode
|
||||
'N8N_EDITOR_BASE_URL': f"https://{self.data['finalURL']}",
|
||||
'WEBHOOK_URL': f"https://{self.data['finalURL']}",
|
||||
'WEBHOOK_TUNNEL_URL': f"https://{self.data['finalURL']}",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,102 @@ class modSec:
|
|||
tempRulesFile = "/home/cyberpanel/tempModSecRules"
|
||||
mirrorPath = "cyberpanel.net"
|
||||
|
||||
# Compatible ModSecurity binaries (built against custom OLS headers)
|
||||
# These prevent ABI incompatibility crashes (Signal 11/SIGSEGV)
|
||||
MODSEC_COMPATIBLE = {
|
||||
'rhel8': {
|
||||
'url': 'https://cyberpanel.net/mod_security-compatible-rhel8.so',
|
||||
'sha256': 'bbbf003bdc7979b98f09b640dffe2cbbe5f855427f41319e4c121403c05837b2'
|
||||
},
|
||||
'rhel9': {
|
||||
'url': 'https://cyberpanel.net/mod_security-compatible-rhel.so',
|
||||
'sha256': '19deb2ffbaf1334cf4ce4d46d53f747a75b29e835bf5a01f91ebcc0c78e98629'
|
||||
},
|
||||
'ubuntu': {
|
||||
'url': 'https://cyberpanel.net/mod_security-compatible-ubuntu.so',
|
||||
'sha256': 'ed02c813136720bd4b9de5925f6e41bdc8392e494d7740d035479aaca6d1e0cd'
|
||||
}
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def detectPlatform():
|
||||
"""Detect OS platform for compatible binary selection"""
|
||||
try:
|
||||
# Check for Ubuntu/Debian
|
||||
if os.path.exists('/etc/lsb-release'):
|
||||
with open('/etc/lsb-release', 'r') as f:
|
||||
content = f.read()
|
||||
if 'Ubuntu' in content or 'ubuntu' in content:
|
||||
return 'ubuntu'
|
||||
|
||||
# Check for Debian
|
||||
if os.path.exists('/etc/debian_version'):
|
||||
return 'ubuntu' # Use Ubuntu binary for Debian
|
||||
|
||||
# Check for RHEL-based distributions
|
||||
if os.path.exists('/etc/os-release'):
|
||||
with open('/etc/os-release', 'r') as f:
|
||||
content = f.read().lower()
|
||||
|
||||
# Check for version 8.x
|
||||
if 'version="8.' in content or 'version_id="8' in content:
|
||||
return 'rhel8'
|
||||
|
||||
# Check for version 9.x
|
||||
if 'version="9.' in content or 'version_id="9' in content:
|
||||
return 'rhel9'
|
||||
|
||||
return 'rhel9' # Default to rhel9
|
||||
except:
|
||||
return 'rhel9'
|
||||
|
||||
@staticmethod
|
||||
def downloadCompatibleModSec(platform):
|
||||
"""Download and install compatible ModSecurity binary"""
|
||||
try:
|
||||
config = modSec.MODSEC_COMPATIBLE.get(platform)
|
||||
if not config:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"No compatible ModSecurity for platform {platform}")
|
||||
return False
|
||||
|
||||
modsec_path = "/usr/local/lsws/modules/mod_security.so"
|
||||
tmp_path = "/tmp/mod_security-compatible.so"
|
||||
|
||||
# Download compatible binary
|
||||
command = f"wget -q {config['url']} -O {tmp_path}"
|
||||
result = subprocess.call(shlex.split(command))
|
||||
if result != 0:
|
||||
logging.CyberCPLogFileWriter.writeToFile("Failed to download compatible ModSecurity")
|
||||
return False
|
||||
|
||||
# Verify checksum
|
||||
import hashlib
|
||||
sha256_hash = hashlib.sha256()
|
||||
with open(tmp_path, "rb") as f:
|
||||
for byte_block in iter(lambda: f.read(4096), b""):
|
||||
sha256_hash.update(byte_block)
|
||||
actual_sha256 = sha256_hash.hexdigest()
|
||||
|
||||
if actual_sha256 != config['sha256']:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"ModSecurity checksum mismatch: expected {config['sha256']}, got {actual_sha256}")
|
||||
os.remove(tmp_path)
|
||||
return False
|
||||
|
||||
# Backup original if exists
|
||||
if os.path.exists(modsec_path):
|
||||
shutil.copy2(modsec_path, f"{modsec_path}.stock")
|
||||
|
||||
# Install compatible version
|
||||
shutil.move(tmp_path, modsec_path)
|
||||
os.chmod(modsec_path, 0o644)
|
||||
|
||||
logging.CyberCPLogFileWriter.writeToFile("Installed compatible ModSecurity binary")
|
||||
return True
|
||||
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [downloadCompatibleModSec]")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def installModSec():
|
||||
try:
|
||||
|
|
@ -45,6 +141,23 @@ class modSec:
|
|||
writeToFile.writelines("ModSecurity Installed.[200]\n")
|
||||
writeToFile.close()
|
||||
|
||||
# Check if custom OLS binary is installed - if so, replace with compatible ModSecurity
|
||||
custom_ols_marker = "/usr/local/lsws/modules/cyberpanel_ols.so"
|
||||
if os.path.exists(custom_ols_marker):
|
||||
writeToFile = open(modSec.installLogPath, 'a')
|
||||
writeToFile.writelines("Custom OLS detected, installing compatible ModSecurity...\n")
|
||||
writeToFile.close()
|
||||
|
||||
platform = modSec.detectPlatform()
|
||||
if modSec.downloadCompatibleModSec(platform):
|
||||
writeToFile = open(modSec.installLogPath, 'a')
|
||||
writeToFile.writelines("Compatible ModSecurity installed successfully.\n")
|
||||
writeToFile.close()
|
||||
else:
|
||||
writeToFile = open(modSec.installLogPath, 'a')
|
||||
writeToFile.writelines("WARNING: Could not install compatible ModSecurity. May experience crashes.\n")
|
||||
writeToFile.close()
|
||||
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.writeToFile(str(msg) + "[installModSec]")
|
||||
|
|
|
|||
|
|
@ -938,25 +938,32 @@ class Upgrade:
|
|||
platform = Upgrade.detectPlatform()
|
||||
Upgrade.stdOut(f"Detected platform: {platform}", 0)
|
||||
|
||||
# Platform-specific URLs and checksums (OpenLiteSpeed v1.8.4.1 - v2.0.5 Static Build)
|
||||
# Platform-specific URLs and checksums (OpenLiteSpeed v1.8.4.1 with PHPConfig + Header unset fix + Static Linking)
|
||||
# Module Build Date: December 28, 2025 - v2.2.0 Brute Force with Progressive Throttle
|
||||
BINARY_CONFIGS = {
|
||||
'rhel8': {
|
||||
'url': 'https://cyberpanel.net/openlitespeed-phpconfig-x86_64-rhel8-static',
|
||||
'sha256': '6ce688a237615102cc1603ee1999b3cede0ff3482d31e1f65705e92396d34b3a',
|
||||
'module_url': None, # RHEL 8 doesn't have module (use RHEL 9 if needed)
|
||||
'module_sha256': None
|
||||
'module_url': 'https://cyberpanel.net/binaries/rhel8/cyberpanel_ols.so',
|
||||
'module_sha256': '7c33d89c7fbcd3ed7b0422fee3f49b5e041713c2c2b7316a5774f6defa147572',
|
||||
'modsec_url': 'https://cyberpanel.net/mod_security-compatible-rhel8.so',
|
||||
'modsec_sha256': 'bbbf003bdc7979b98f09b640dffe2cbbe5f855427f41319e4c121403c05837b2'
|
||||
},
|
||||
'rhel9': {
|
||||
'url': 'https://cyberpanel.net/openlitespeed-phpconfig-x86_64-rhel9-static',
|
||||
'sha256': '90468fb38767505185013024678d9144ae13100d2355097657f58719d98fbbc4',
|
||||
'module_url': 'https://cyberpanel.net/cyberpanel_ols_x86_64_rhel.so',
|
||||
'module_sha256': '127227db81bcbebf80b225fc747b69cfcd4ad2f01cea486aa02d5c9ba6c18109'
|
||||
'sha256': '709093d99d5d3e789134c131893614968e17eefd9ade2200f811d9b076b2f02e',
|
||||
'module_url': 'https://cyberpanel.net/binaries/rhel9/cyberpanel_ols.so',
|
||||
'module_sha256': 'ae65337e2d13babc0c675bb4264d469daffa2efb7627c9bf39ac59e42e3ebede',
|
||||
'modsec_url': 'https://cyberpanel.net/mod_security-compatible-rhel.so',
|
||||
'modsec_sha256': '19deb2ffbaf1334cf4ce4d46d53f747a75b29e835bf5a01f91ebcc0c78e98629'
|
||||
},
|
||||
'ubuntu': {
|
||||
'url': 'https://cyberpanel.net/openlitespeed-phpconfig-x86_64-ubuntu-static',
|
||||
'sha256': '89aaf66474e78cb3c1666784e0e7a417550bd317e6ab148201bdc318d36710cb',
|
||||
'module_url': 'https://cyberpanel.net/cyberpanel_ols_x86_64_ubuntu.so',
|
||||
'module_sha256': 'e7734f1e6226c2a0a8e00c1f6534ea9f577df9081b046736a774b1c52c28e7e5'
|
||||
'module_url': 'https://cyberpanel.net/binaries/ubuntu/cyberpanel_ols.so',
|
||||
'module_sha256': '62978ede1f174dd2885e5227a3d9cc463d0c27acd77cfc23743d7309ee0c54ea',
|
||||
'modsec_url': 'https://cyberpanel.net/mod_security-compatible-ubuntu.so',
|
||||
'modsec_sha256': 'ed02c813136720bd4b9de5925f6e41bdc8392e494d7740d035479aaca6d1e0cd'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -970,8 +977,11 @@ class Upgrade:
|
|||
OLS_BINARY_SHA256 = config['sha256']
|
||||
MODULE_URL = config['module_url']
|
||||
MODULE_SHA256 = config['module_sha256']
|
||||
MODSEC_URL = config.get('modsec_url')
|
||||
MODSEC_SHA256 = config.get('modsec_sha256')
|
||||
OLS_BINARY_PATH = "/usr/local/lsws/bin/openlitespeed"
|
||||
MODULE_PATH = "/usr/local/lsws/modules/cyberpanel_ols.so"
|
||||
MODSEC_PATH = "/usr/local/lsws/modules/mod_security.so"
|
||||
|
||||
# Create backup
|
||||
from datetime import datetime
|
||||
|
|
@ -983,12 +993,16 @@ class Upgrade:
|
|||
if os.path.exists(OLS_BINARY_PATH):
|
||||
shutil.copy2(OLS_BINARY_PATH, f"{backup_dir}/openlitespeed.backup")
|
||||
Upgrade.stdOut(f"Backup created at: {backup_dir}", 0)
|
||||
# Also backup existing ModSecurity if it exists
|
||||
if os.path.exists(MODSEC_PATH):
|
||||
shutil.copy2(MODSEC_PATH, f"{backup_dir}/mod_security.so.backup")
|
||||
except Exception as e:
|
||||
Upgrade.stdOut(f"WARNING: Could not create backup: {e}", 0)
|
||||
|
||||
# Download binaries to temp location
|
||||
tmp_binary = "/tmp/openlitespeed-custom"
|
||||
tmp_module = "/tmp/cyberpanel_ols.so"
|
||||
tmp_modsec = "/tmp/mod_security.so"
|
||||
|
||||
Upgrade.stdOut("Downloading custom binaries...", 0)
|
||||
|
||||
|
|
@ -1026,6 +1040,18 @@ class Upgrade:
|
|||
else:
|
||||
Upgrade.stdOut("Note: No CyberPanel module for this platform", 0)
|
||||
|
||||
# Download compatible ModSecurity if existing ModSecurity is installed
|
||||
# This prevents ABI incompatibility crashes (Signal 11/SIGSEGV)
|
||||
modsec_downloaded = False
|
||||
if os.path.exists(MODSEC_PATH) and MODSEC_URL and MODSEC_SHA256:
|
||||
Upgrade.stdOut("Existing ModSecurity detected - downloading compatible version...", 0)
|
||||
if Upgrade.downloadCustomBinary(MODSEC_URL, tmp_modsec, MODSEC_SHA256):
|
||||
modsec_downloaded = True
|
||||
else:
|
||||
Upgrade.stdOut("WARNING: Failed to download compatible ModSecurity", 0)
|
||||
Upgrade.stdOut("ModSecurity may crash due to ABI incompatibility", 0)
|
||||
Upgrade.stdOut("Consider manually updating ModSecurity after upgrade", 0)
|
||||
|
||||
# Install OpenLiteSpeed binary
|
||||
Upgrade.stdOut("Installing custom binaries...", 0)
|
||||
|
||||
|
|
@ -1068,6 +1094,16 @@ class Upgrade:
|
|||
Upgrade.stdOut(f"ERROR: Failed to install module: {e}", 0)
|
||||
return False
|
||||
|
||||
# Install compatible ModSecurity (if downloaded)
|
||||
if modsec_downloaded:
|
||||
try:
|
||||
shutil.move(tmp_modsec, MODSEC_PATH)
|
||||
os.chmod(MODSEC_PATH, 0o644)
|
||||
Upgrade.stdOut("Installed compatible ModSecurity module", 0)
|
||||
except Exception as e:
|
||||
Upgrade.stdOut(f"WARNING: Failed to install ModSecurity: {e}", 0)
|
||||
# Non-fatal, continue
|
||||
|
||||
# Verify installation
|
||||
if os.path.exists(OLS_BINARY_PATH):
|
||||
if not module_downloaded or os.path.exists(MODULE_PATH):
|
||||
|
|
@ -2495,6 +2531,19 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
|
|||
except:
|
||||
pass
|
||||
|
||||
# Email Filtering Tables - Catch-All, Plus-Addressing, Pattern Forwarding
|
||||
query = """CREATE TABLE IF NOT EXISTS `e_catchall` (
|
||||
`domain_id` varchar(50) NOT NULL,
|
||||
`destination` varchar(255) NOT NULL,
|
||||
`enabled` tinyint(1) NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`domain_id`),
|
||||
CONSTRAINT `fk_catchall_domain` FOREIGN KEY (`domain_id`) REFERENCES `e_domains` (`domain`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"""
|
||||
try:
|
||||
cursor.execute(query)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
connection.close()
|
||||
except:
|
||||
|
|
|
|||
Loading…
Reference in New Issue