conflict fix

This commit is contained in:
usmannasir
2025-11-09 19:34:57 +05:00
8 changed files with 1368 additions and 84 deletions

BIN
ModuleDeveloperGuide.pdf Normal file

Binary file not shown.

View File

@@ -805,10 +805,195 @@
.notification-shown.ai-scanner-shown #main-content { .notification-shown.ai-scanner-shown #main-content {
padding-top: 220px; padding-top: 220px;
} }
.notification-shown .ai-scanner-banner { .notification-shown .ai-scanner-banner {
top: 130px; top: 130px;
} }
/* .htaccess Feature Banner */
.htaccess-feature-banner {
position: fixed;
top: 80px;
left: 260px;
right: 0;
background: linear-gradient(135deg, #10b981 0%, #059669 50%, #047857 100%);
border-bottom: 2px solid #065f46;
padding: 18px 30px;
z-index: 997;
box-shadow: 0 6px 25px rgba(16, 185, 129, 0.3);
animation: slideDown 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
display: none;
}
.htaccess-feature-banner.show {
display: block;
}
.htaccess-content {
display: flex;
align-items: center;
gap: 1.5rem;
max-width: 1400px;
margin: 0 auto;
}
.htaccess-icon {
background: rgba(255, 255, 255, 0.25);
border-radius: 14px;
padding: 14px;
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.4);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.htaccess-icon i {
color: white;
font-size: 1.75rem;
display: block;
}
.htaccess-text {
flex: 1;
display: flex;
flex-direction: column;
gap: 5px;
}
.htaccess-main-text {
color: white;
font-size: 1.1rem;
font-weight: 700;
line-height: 1.4;
letter-spacing: 0.3px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.htaccess-sub-text {
color: rgba(255, 255, 255, 0.9);
font-size: 0.875rem;
font-weight: 500;
display: flex;
align-items: center;
gap: 1rem;
}
.htaccess-sub-text span {
display: flex;
align-items: center;
gap: 0.4rem;
}
.htaccess-sub-text i {
font-size: 0.75rem;
}
.htaccess-actions {
display: flex;
gap: 12px;
}
.htaccess-btn {
background: white;
color: #047857;
padding: 14px 28px;
border-radius: 10px;
text-decoration: none;
font-weight: 700;
font-size: 0.9rem;
display: flex;
align-items: center;
gap: 10px;
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
position: relative;
overflow: hidden;
border: 2px solid transparent;
}
.htaccess-btn:hover {
background: #f0fdf4;
color: #065f46;
transform: translateY(-2px) scale(1.02);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
text-decoration: none;
border-color: white;
}
.htaccess-btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(16, 185, 129, 0.1);
transform: translate(-50%, -50%);
transition: width 0.6s ease, height 0.6s ease;
}
.htaccess-btn:hover::before {
width: 300px;
height: 300px;
}
.htaccess-btn span {
position: relative;
z-index: 1;
}
.htaccess-btn i {
font-size: 1rem;
position: relative;
z-index: 1;
}
.htaccess-close {
background: rgba(255, 255, 255, 0.15);
border: 1px solid rgba(255, 255, 255, 0.3);
color: white;
font-size: 1.1rem;
cursor: pointer;
padding: 10px;
border-radius: 8px;
transition: all 0.3s ease;
backdrop-filter: blur(12px);
}
.htaccess-close:hover {
background: rgba(255, 255, 255, 0.25);
transform: scale(1.1) rotate(90deg);
}
/* Adjust main content when .htaccess banner is shown */
.htaccess-shown #main-content {
padding-top: 190px;
}
/* Multiple banners adjustments */
.notification-shown.htaccess-shown #main-content {
padding-top: 240px;
}
.ai-scanner-shown.htaccess-shown #main-content {
padding-top: 270px;
}
.notification-shown.ai-scanner-shown.htaccess-shown #main-content {
padding-top: 320px;
}
.notification-shown .htaccess-feature-banner {
top: 130px;
}
.ai-scanner-shown .htaccess-feature-banner {
top: 160px;
}
.notification-shown.ai-scanner-shown .htaccess-feature-banner {
top: 210px;
}
/* Mobile responsive styles for AI Scanner banner */ /* Mobile responsive styles for AI Scanner banner */
@media (max-width: 768px) { @media (max-width: 768px) {
@@ -816,54 +1001,110 @@
left: 0; left: 0;
padding: 12px 20px; padding: 12px 20px;
} }
.ai-scanner-content { .ai-scanner-content {
gap: 1rem; gap: 1rem;
flex-wrap: wrap; flex-wrap: wrap;
} }
.ai-scanner-text { .ai-scanner-text {
min-width: 200px; min-width: 200px;
} }
.ai-scanner-main-text { .ai-scanner-main-text {
font-size: 0.9rem; font-size: 0.9rem;
} }
.ai-scanner-sub-text { .ai-scanner-sub-text {
font-size: 0.8rem; font-size: 0.8rem;
} }
.ai-scanner-btn { .ai-scanner-btn {
padding: 10px 20px; padding: 10px 20px;
font-size: 0.8rem; font-size: 0.8rem;
} }
.ai-scanner-icon { .ai-scanner-icon {
padding: 10px; padding: 10px;
} }
.ai-scanner-icon i { .ai-scanner-icon i {
font-size: 1.25rem; font-size: 1.25rem;
} }
/* .htaccess banner mobile styles */
.htaccess-feature-banner {
left: 0;
padding: 14px 20px;
}
.htaccess-content {
gap: 1rem;
flex-wrap: wrap;
}
.htaccess-text {
min-width: 200px;
}
.htaccess-main-text {
font-size: 0.95rem;
}
.htaccess-sub-text {
font-size: 0.8rem;
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
.htaccess-btn {
padding: 12px 22px;
font-size: 0.85rem;
}
.htaccess-icon {
padding: 12px;
}
.htaccess-icon i {
font-size: 1.4rem;
}
} }
@media (max-width: 480px) { @media (max-width: 480px) {
.ai-scanner-content { .ai-scanner-content {
flex-direction: column; flex-direction: column;
align-items: stretch; align-items: stretch;
gap: 12px; gap: 12px;
} }
.ai-scanner-actions { .ai-scanner-actions {
justify-content: center; justify-content: center;
} }
.ai-scanner-close { .ai-scanner-close {
position: absolute; position: absolute;
top: 8px; top: 8px;
right: 8px; right: 8px;
} }
/* .htaccess banner small mobile styles */
.htaccess-content {
flex-direction: column;
align-items: stretch;
gap: 12px;
}
.htaccess-actions {
justify-content: center;
}
.htaccess-close {
position: absolute;
top: 10px;
right: 10px;
}
} }
/* Scrollbar */ /* Scrollbar */
@@ -1760,7 +2001,33 @@
</button> </button>
</div> </div>
</div> </div>
<!-- .htaccess Feature Announcement -->
<div id="htaccess-notification" class="htaccess-feature-banner">
<div class="htaccess-content">
<div class="htaccess-icon">
<i class="fas fa-magic"></i>
</div>
<div class="htaccess-text">
<span class="htaccess-main-text">✨ Revolutionary .htaccess Support Now Live!</span>
<span class="htaccess-sub-text">
<span><i class="fas fa-check-circle"></i> Full .htaccess support</span>
<span><i class="fas fa-check-circle"></i> PHP configuration now works</span>
<span><i class="fas fa-check-circle"></i> Zero rule rewrites needed</span>
</span>
</div>
<div class="htaccess-actions">
<a href="https://cyberpanel.net/cyberpanel-htaccess-module" target="_blank" rel="noopener" class="htaccess-btn">
<i class="fas fa-book-open"></i>
<span>Learn More</span>
</a>
</div>
<button class="htaccess-close" onclick="dismissHtaccessNotification()">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<!-- Main Content --> <!-- Main Content -->
<div id="main-content"> <div id="main-content">
{% block content %}{% endblock %} {% block content %}{% endblock %}
@@ -1841,10 +2108,10 @@
// Backup notification banner logic // Backup notification banner logic
function checkBackupStatus() { function checkBackupStatus() {
// Check if user has dismissed the notification permanently (from server-side context) // Check if user has dismissed the notification in this session
{% if backup_notification_dismissed %} if (sessionStorage.getItem('backupNotificationDismissed') === 'true') {
return; // Notification already dismissed permanently return;
{% endif %} }
// Check if user has backup configured (you'll need to implement this API) // 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 // For now, we'll show it by default unless they have a backup plan
@@ -1871,81 +2138,83 @@
const body = document.body; const body = document.body;
banner.classList.remove('show'); banner.classList.remove('show');
body.classList.remove('notification-shown'); body.classList.remove('notification-shown');
// Remember dismissal for this session
// Dismiss permanently via API sessionStorage.setItem('backupNotificationDismissed', 'true');
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 // AI Scanner Notification Functions
function checkAIScannerStatus() { function checkAIScannerStatus() {
// Check if user has dismissed the notification permanently (from server-side context) // Check if user has dismissed the notification in this session
{% if ai_scanner_notification_dismissed %} if (sessionStorage.getItem('aiScannerNotificationDismissed') === 'true') {
return; // Notification already dismissed permanently return;
{% endif %} }
// Check if we're not already on the AI Scanner page // Check if we're not already on the AI Scanner page
if (!window.location.href.includes('aiscanner')) { if (!window.location.href.includes('aiscanner')) {
showAIScannerNotification(); showAIScannerNotification();
} }
} }
function showAIScannerNotification() { function showAIScannerNotification() {
const banner = document.getElementById('ai-scanner-notification'); const banner = document.getElementById('ai-scanner-notification');
const body = document.body; const body = document.body;
banner.classList.add('show'); banner.classList.add('show');
body.classList.add('ai-scanner-shown'); body.classList.add('ai-scanner-shown');
} }
function dismissAIScannerNotification() { function dismissAIScannerNotification() {
const banner = document.getElementById('ai-scanner-notification'); const banner = document.getElementById('ai-scanner-notification');
const body = document.body; const body = document.body;
banner.classList.remove('show'); banner.classList.remove('show');
body.classList.remove('ai-scanner-shown'); body.classList.remove('ai-scanner-shown');
// Remember dismissal for this session
// Dismiss permanently via API sessionStorage.setItem('aiScannerNotificationDismissed', 'true');
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 // .htaccess Feature Notification Functions
function checkHtaccessStatus() {
// Check if user has dismissed the notification permanently (localStorage for longer persistence)
if (localStorage.getItem('htaccessNotificationDismissed') === 'true') {
return;
}
// Check if notification has been shown today
const lastShown = localStorage.getItem('htaccessNotificationLastShown');
const today = new Date().toDateString();
if (lastShown === today) {
return;
}
// Show the notification
showHtaccessNotification();
localStorage.setItem('htaccessNotificationLastShown', today);
}
function showHtaccessNotification() {
const banner = document.getElementById('htaccess-notification');
const body = document.body;
banner.classList.add('show');
body.classList.add('htaccess-shown');
}
function dismissHtaccessNotification() {
const banner = document.getElementById('htaccess-notification');
const body = document.body;
banner.classList.remove('show');
body.classList.remove('htaccess-shown');
// Remember dismissal permanently
localStorage.setItem('htaccessNotificationDismissed', 'true');
}
// Check all notification statuses when page loads
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
checkBackupStatus(); checkBackupStatus();
// Show AI Scanner notification with a slight delay for better UX // Show AI Scanner notification with a slight delay for better UX
setTimeout(checkAIScannerStatus, 1000); setTimeout(checkAIScannerStatus, 1000);
// Show .htaccess notification with additional delay for staggered effect
setTimeout(checkHtaccessStatus, 1500);
// Set active menu state based on current URL // Set active menu state based on current URL
setActiveMenuState(); setActiveMenuState();
}); });

View File

@@ -1986,9 +1986,30 @@ Current_Dir="$(pwd)"
rm -f /usr/local/lsws/cyberpanel-tmp rm -f /usr/local/lsws/cyberpanel-tmp
mkdir /usr/local/lsws/cyberpanel-tmp mkdir /usr/local/lsws/cyberpanel-tmp
cd /usr/local/lsws/cyberpanel-tmp || exit cd /usr/local/lsws/cyberpanel-tmp || exit
# Try to download timezonedb, but continue if it fails
wget -O timezonedb.tgz https://cyberpanel.sh/pecl.php.net/get/timezonedb wget -O timezonedb.tgz https://cyberpanel.sh/pecl.php.net/get/timezonedb
if [ ! -f timezonedb.tgz ] || [ ! -s timezonedb.tgz ]; then
log_info "WARNING: Failed to download timezonedb, skipping installation"
cd "$Current_Dir" || exit
rm -rf /usr/local/lsws/cyberpanel-tmp
return 0
fi
tar xzvf timezonedb.tgz tar xzvf timezonedb.tgz
cd timezonedb-* || exit if [ ! -d timezonedb-* ]; then
log_info "WARNING: Failed to extract timezonedb, skipping installation"
cd "$Current_Dir" || exit
rm -rf /usr/local/lsws/cyberpanel-tmp
return 0
fi
cd timezonedb-* || {
log_info "WARNING: Cannot enter timezonedb directory, skipping installation"
cd "$Current_Dir" || exit
rm -rf /usr/local/lsws/cyberpanel-tmp
return 0
}
# Install required packages for building PHP extensions # Install required packages for building PHP extensions
if [[ "$Server_OS" = "Ubuntu" ]] ; then if [[ "$Server_OS" = "Ubuntu" ]] ; then

View File

@@ -1471,8 +1471,8 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
var owaspInstalled = false; var owaspInstalled = false;
var comodoInstalled = false; var comodoInstalled = false;
var counterOWASP = 0; var owaspInitialized = false;
var counterComodo = 0; var comodoInitialized = false;
$('#owaspInstalled').change(function () { $('#owaspInstalled').change(function () {
@@ -1480,15 +1480,13 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
owaspInstalled = $(this).prop('checked'); owaspInstalled = $(this).prop('checked');
$scope.ruleFiles = true; $scope.ruleFiles = true;
if (counterOWASP !== 0) { if (owaspInitialized) {
if (owaspInstalled === true) { if (owaspInstalled === true) {
installModSecRulesPack('installOWASP'); installModSecRulesPack('installOWASP');
} else { } else {
installModSecRulesPack('disableOWASP') installModSecRulesPack('disableOWASP')
} }
} }
counterOWASP = counterOWASP + 1;
}); });
$('#comodoInstalled').change(function () { $('#comodoInstalled').change(function () {
@@ -1496,7 +1494,7 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
$scope.ruleFiles = true; $scope.ruleFiles = true;
comodoInstalled = $(this).prop('checked'); comodoInstalled = $(this).prop('checked');
if (counterComodo !== 0) { if (comodoInitialized) {
if (comodoInstalled === true) { if (comodoInstalled === true) {
installModSecRulesPack('installComodo'); installModSecRulesPack('installComodo');
@@ -1505,8 +1503,6 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
} }
} }
counterComodo = counterComodo + 1;
}); });
@@ -1545,6 +1541,9 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
$('#owaspInstalled').prop('checked', false); $('#owaspInstalled').prop('checked', false);
$scope.owaspDisable = true; $scope.owaspDisable = true;
} }
// Mark as initialized after setting initial state
owaspInitialized = true;
if (response.data.comodoInstalled === 1) { if (response.data.comodoInstalled === 1) {
$('#comodoInstalled').prop('checked', true); $('#comodoInstalled').prop('checked', true);
$scope.comodoDisable = false; $scope.comodoDisable = false;
@@ -1552,6 +1551,8 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
$('#comodoInstalled').prop('checked', false); $('#comodoInstalled').prop('checked', false);
$scope.comodoDisable = true; $scope.comodoDisable = true;
} }
// Mark as initialized after setting initial state
comodoInitialized = true;
} else { } else {
if (response.data.owaspInstalled === 1) { if (response.data.owaspInstalled === 1) {

View File

@@ -270,10 +270,346 @@ class InstallCyberPanel:
# Fallback to known latest version # Fallback to known latest version
return "6.3.4" return "6.3.4"
def detectArchitecture(self):
"""Detect system architecture - custom binaries only for x86_64"""
try:
import platform
arch = platform.machine()
return arch == "x86_64"
except Exception as msg:
logging.InstallLog.writeToFile(str(msg) + " [detectArchitecture]")
return False
def detectBinarySuffix(self):
"""Detect which binary suffix to use based on OS distribution
Returns 'ubuntu' for Ubuntu/Debian systems
Returns 'rhel8' for RHEL/AlmaLinux/Rocky 8.x systems
Returns 'rhel9' for RHEL/AlmaLinux/Rocky 9.x systems
"""
try:
# Check /etc/os-release first for more accurate detection
if os.path.exists('/etc/os-release'):
with open('/etc/os-release', 'r') as f:
os_release = f.read().lower()
# Check for Ubuntu/Debian FIRST
if 'ubuntu' in os_release or 'debian' in os_release:
return 'ubuntu'
# Check for RHEL-based distributions and extract version
if any(x in os_release for x in ['almalinux', 'rocky', 'rhel', 'centos stream']):
# Extract version number
for line in os_release.split('\n'):
if 'version_id' in line:
version = line.split('=')[1].strip('"').split('.')[0]
if version == '9':
return 'rhel9'
elif version == '8':
return 'rhel8'
# Default to rhel9 if version extraction fails
return 'rhel9'
# Fallback: Use distro variable
# Ubuntu/Debian → ubuntu suffix
if self.distro == ubuntu:
return 'ubuntu'
# CentOS 8+/AlmaLinux/Rocky/OpenEuler → rhel9 by default
elif self.distro == cent8 or self.distro == openeuler:
return 'rhel9'
# CentOS 7 → ubuntu suffix (uses libcrypt.so.1)
elif self.distro == centos:
return 'ubuntu'
# Default to ubuntu for unknown distros
else:
InstallCyberPanel.stdOut("Unknown OS distribution, defaulting to Ubuntu binaries", 1)
return 'ubuntu'
except Exception as msg:
logging.InstallLog.writeToFile(str(msg) + " [detectBinarySuffix]")
InstallCyberPanel.stdOut("Error detecting OS, defaulting to Ubuntu binaries", 1)
return 'ubuntu'
def downloadCustomBinary(self, url, destination):
"""Download custom binary file"""
try:
InstallCyberPanel.stdOut(f"Downloading {os.path.basename(destination)}...", 1)
# Use wget for better progress display
command = f'wget -q --show-progress {url} -O {destination}'
install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Check if file was downloaded successfully by verifying it exists and has reasonable size
if os.path.exists(destination):
file_size = os.path.getsize(destination)
# Verify file size is reasonable (at least 10KB to avoid error pages/empty files)
if file_size > 10240: # 10KB
if file_size > 1048576: # 1MB
InstallCyberPanel.stdOut(f"Downloaded successfully ({file_size / (1024*1024):.2f} MB)", 1)
else:
InstallCyberPanel.stdOut(f"Downloaded successfully ({file_size / 1024:.2f} KB)", 1)
return True
else:
InstallCyberPanel.stdOut(f"ERROR: Downloaded file too small ({file_size} bytes)", 1)
return False
else:
InstallCyberPanel.stdOut("ERROR: Download failed - file not found", 1)
return False
except Exception as msg:
logging.InstallLog.writeToFile(str(msg) + " [downloadCustomBinary]")
InstallCyberPanel.stdOut(f"ERROR: {msg}", 1)
return False
def verifyCustomBinary(self, binary_path):
"""Verify custom binary has correct dependencies and can run"""
try:
InstallCyberPanel.stdOut("Verifying custom binary compatibility...", 1)
# Check library dependencies
command = f'ldd {binary_path}'
result = subprocess.run(command, shell=True, capture_output=True, text=True)
if result.returncode != 0:
InstallCyberPanel.stdOut("ERROR: Failed to check binary dependencies", 1)
return False
# Check for missing libraries
if 'not found' in result.stdout:
InstallCyberPanel.stdOut("ERROR: Binary has missing library dependencies:", 1)
for line in result.stdout.split('\n'):
if 'not found' in line:
InstallCyberPanel.stdOut(f" {line.strip()}", 1)
return False
# Try to run the binary with -v to check if it can execute
command = f'{binary_path} -v'
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=5)
if result.returncode != 0:
InstallCyberPanel.stdOut("ERROR: Binary failed to execute", 1)
if result.stderr:
InstallCyberPanel.stdOut(f" Error: {result.stderr.strip()}", 1)
return False
InstallCyberPanel.stdOut("Binary verification successful", 1)
return True
except subprocess.TimeoutExpired:
InstallCyberPanel.stdOut("ERROR: Binary verification timed out", 1)
return False
except Exception as msg:
logging.InstallLog.writeToFile(str(msg) + " [verifyCustomBinary]")
InstallCyberPanel.stdOut(f"ERROR: Verification failed: {msg}", 1)
return False
def rollbackCustomBinary(self, backup_dir, binary_path, module_path):
"""Rollback to original binary if custom binary fails"""
try:
InstallCyberPanel.stdOut("Rolling back to original binary...", 1)
backup_binary = f"{backup_dir}/openlitespeed.backup"
# Restore original binary if backup exists
if os.path.exists(backup_binary):
shutil.copy2(backup_binary, binary_path)
os.chmod(binary_path, 0o755)
InstallCyberPanel.stdOut("Original binary restored successfully", 1)
else:
InstallCyberPanel.stdOut("WARNING: No backup found, cannot restore", 1)
# Remove failed custom module
if os.path.exists(module_path):
os.remove(module_path)
InstallCyberPanel.stdOut("Custom module removed", 1)
InstallCyberPanel.stdOut("Rollback completed", 1)
return True
except Exception as msg:
logging.InstallLog.writeToFile(str(msg) + " [rollbackCustomBinary]")
InstallCyberPanel.stdOut(f"ERROR: Rollback failed: {msg}", 1)
return False
def installCustomOLSBinaries(self):
"""Install custom OpenLiteSpeed binaries with PHP config support"""
try:
InstallCyberPanel.stdOut("Installing Custom OpenLiteSpeed Binaries", 1)
InstallCyberPanel.stdOut("=" * 50, 1)
# Check architecture
if not self.detectArchitecture():
InstallCyberPanel.stdOut("WARNING: Custom binaries only available for x86_64", 1)
InstallCyberPanel.stdOut("Skipping custom binary installation", 1)
InstallCyberPanel.stdOut("Standard OLS will be used", 1)
return True # Not a failure, just skip
# Detect OS and select appropriate binary suffix
binary_suffix = self.detectBinarySuffix()
InstallCyberPanel.stdOut(f"Detected OS type: using '{binary_suffix}' binaries", 1)
# URLs for custom binaries with OS-specific paths
BASE_URL = "https://cyberpanel.net/binaries"
# Set URLs based on OS type
if binary_suffix == 'rhel8':
OLS_BINARY_URL = f"{BASE_URL}/rhel8/openlitespeed-phpconfig-x86_64-rhel8"
MODULE_URL = f"{BASE_URL}/rhel8/cyberpanel_ols_x86_64_rhel8.so"
elif binary_suffix == 'rhel9':
OLS_BINARY_URL = f"{BASE_URL}/rhel9/openlitespeed-phpconfig-x86_64-rhel"
MODULE_URL = f"{BASE_URL}/rhel9/cyberpanel_ols_x86_64_rhel.so"
else: # ubuntu
OLS_BINARY_URL = f"{BASE_URL}/ubuntu/openlitespeed-phpconfig-x86_64-ubuntu"
MODULE_URL = f"{BASE_URL}/ubuntu/cyberpanel_ols_x86_64_ubuntu.so"
OLS_BINARY_PATH = "/usr/local/lsws/bin/openlitespeed"
MODULE_PATH = "/usr/local/lsws/modules/cyberpanel_ols.so"
# Create backup
from datetime import datetime
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
backup_dir = f"/usr/local/lsws/backup-{timestamp}"
try:
os.makedirs(backup_dir, exist_ok=True)
if os.path.exists(OLS_BINARY_PATH):
shutil.copy2(OLS_BINARY_PATH, f"{backup_dir}/openlitespeed.backup")
InstallCyberPanel.stdOut(f"Backup created at: {backup_dir}", 1)
except Exception as e:
InstallCyberPanel.stdOut(f"WARNING: Could not create backup: {e}", 1)
# Download binaries to temp location
tmp_binary = "/tmp/openlitespeed-custom"
tmp_module = "/tmp/cyberpanel_ols.so"
InstallCyberPanel.stdOut("Downloading custom binaries...", 1)
# Download OpenLiteSpeed binary
if not self.downloadCustomBinary(OLS_BINARY_URL, tmp_binary):
InstallCyberPanel.stdOut("ERROR: Failed to download OLS binary", 1)
InstallCyberPanel.stdOut("Continuing with standard OLS", 1)
return True # Not fatal, continue with standard OLS
# Download module
if not self.downloadCustomBinary(MODULE_URL, tmp_module):
InstallCyberPanel.stdOut("ERROR: Failed to download module", 1)
InstallCyberPanel.stdOut("Continuing with standard OLS", 1)
return True # Not fatal, continue with standard OLS
# Install OpenLiteSpeed binary
InstallCyberPanel.stdOut("Installing custom binaries...", 1)
try:
shutil.move(tmp_binary, OLS_BINARY_PATH)
os.chmod(OLS_BINARY_PATH, 0o755)
InstallCyberPanel.stdOut("Installed OpenLiteSpeed binary", 1)
except Exception as e:
InstallCyberPanel.stdOut(f"ERROR: Failed to install binary: {e}", 1)
logging.InstallLog.writeToFile(str(e) + " [installCustomOLSBinaries - binary install]")
return False
# Install module
try:
os.makedirs(os.path.dirname(MODULE_PATH), exist_ok=True)
shutil.move(tmp_module, MODULE_PATH)
os.chmod(MODULE_PATH, 0o644)
InstallCyberPanel.stdOut("Installed CyberPanel module", 1)
except Exception as e:
InstallCyberPanel.stdOut(f"ERROR: Failed to install module: {e}", 1)
logging.InstallLog.writeToFile(str(e) + " [installCustomOLSBinaries - module install]")
return False
# Verify installation files exist
if not (os.path.exists(OLS_BINARY_PATH) and os.path.exists(MODULE_PATH)):
InstallCyberPanel.stdOut("ERROR: Installation verification failed - files not found", 1)
return False
# Verify binary compatibility
if not self.verifyCustomBinary(OLS_BINARY_PATH):
InstallCyberPanel.stdOut("ERROR: Custom binary verification failed", 1)
InstallCyberPanel.stdOut("This usually means wrong binary type for your OS", 1)
# Rollback to original binary
if os.path.exists(backup_dir):
self.rollbackCustomBinary(backup_dir, OLS_BINARY_PATH, MODULE_PATH)
InstallCyberPanel.stdOut("Continuing with standard OLS", 1)
else:
InstallCyberPanel.stdOut("WARNING: Cannot rollback, no backup found", 1)
return True # Non-fatal, continue with standard OLS
# Success!
InstallCyberPanel.stdOut("=" * 50, 1)
InstallCyberPanel.stdOut("Custom Binaries Installed Successfully", 1)
InstallCyberPanel.stdOut("Features enabled:", 1)
InstallCyberPanel.stdOut(" - Apache-style .htaccess support", 1)
InstallCyberPanel.stdOut(" - php_value/php_flag directives", 1)
InstallCyberPanel.stdOut(" - Enhanced header control", 1)
InstallCyberPanel.stdOut(f"Backup: {backup_dir}", 1)
InstallCyberPanel.stdOut("=" * 50, 1)
return True
except Exception as msg:
logging.InstallLog.writeToFile(str(msg) + " [installCustomOLSBinaries]")
InstallCyberPanel.stdOut(f"ERROR: {msg}", 1)
InstallCyberPanel.stdOut("Continuing with standard OLS", 1)
return True # Non-fatal error, continue
def configureCustomModule(self):
"""Configure CyberPanel module in OpenLiteSpeed config"""
try:
InstallCyberPanel.stdOut("Configuring CyberPanel module...", 1)
CONFIG_FILE = "/usr/local/lsws/conf/httpd_config.conf"
if not os.path.exists(CONFIG_FILE):
InstallCyberPanel.stdOut("WARNING: Config file not found", 1)
InstallCyberPanel.stdOut("Module will be auto-loaded", 1)
return True
# Check if module is already configured
with open(CONFIG_FILE, 'r') as f:
content = f.read()
if 'cyberpanel_ols' in content:
InstallCyberPanel.stdOut("Module already configured", 1)
return True
# Add module configuration
module_config = """
module cyberpanel_ols {
ls_enabled 1
}
"""
# Backup config
shutil.copy2(CONFIG_FILE, f"{CONFIG_FILE}.backup")
# Append module config
with open(CONFIG_FILE, 'a') as f:
f.write(module_config)
InstallCyberPanel.stdOut("Module configured successfully", 1)
return True
except Exception as msg:
logging.InstallLog.writeToFile(str(msg) + " [configureCustomModule]")
InstallCyberPanel.stdOut(f"WARNING: Module configuration failed: {msg}", 1)
InstallCyberPanel.stdOut("Module may still work via auto-load", 1)
return True # Non-fatal
def installLiteSpeed(self): def installLiteSpeed(self):
if self.ent == 0: if self.ent == 0:
# Install standard OpenLiteSpeed package
self.install_package('openlitespeed') self.install_package('openlitespeed')
# Install custom binaries with PHP config support
# This replaces the standard binary with enhanced version
self.installCustomOLSBinaries()
# Configure the custom module
self.configureCustomModule()
else: else:
try: try:
try: try:

View File

@@ -308,7 +308,9 @@ extprocessor docker{port} {{
logging.writeToFile("Context already exists, skipping...") logging.writeToFile("Context already exists, skipping...")
return True return True
# Add proxy context with proper headers for n8n # Add proxy context with Origin header for n8n
# Note: OLS proxy automatically adds X-Forwarded-* headers
# Only Origin header needs explicit configuration
proxy_context = f''' proxy_context = f'''
# N8N Proxy Configuration # N8N Proxy Configuration
@@ -319,11 +321,7 @@ context / {{
websocket 1 websocket 1
extraHeaders <<<END_extraHeaders extraHeaders <<<END_extraHeaders
RequestHeader set X-Forwarded-For $ip RequestHeader set Origin "https://{domain}"
RequestHeader set X-Forwarded-Proto https
RequestHeader set X-Forwarded-Host "{domain}"
RequestHeader set Origin "{domain}, {domain}"
RequestHeader set Host "{domain}"
END_extraHeaders END_extraHeaders
}} }}
''' '''
@@ -1380,7 +1378,8 @@ services:
'N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS': 'true', 'N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS': 'true',
'DB_POSTGRESDB_SCHEMA': 'public', 'DB_POSTGRESDB_SCHEMA': 'public',
'N8N_PROTOCOL': 'https', 'N8N_PROTOCOL': 'https',
'N8N_SECURE_COOKIE': 'true' 'N8N_SECURE_COOKIE': 'true',
'N8N_PROXY_HOPS': '1'
} }
} }

View File

@@ -8,6 +8,7 @@ from plogical.virtualHostUtilities import virtualHostUtilities
import os import os
import tarfile import tarfile
import shutil import shutil
import time
from plogical.mailUtilities import mailUtilities from plogical.mailUtilities import mailUtilities
from plogical.processUtilities import ProcessUtilities from plogical.processUtilities import ProcessUtilities
from plogical.installUtilities import installUtilities from plogical.installUtilities import installUtilities
@@ -19,11 +20,184 @@ class modSec:
mirrorPath = "cyberpanel.net" mirrorPath = "cyberpanel.net"
@staticmethod @staticmethod
def installModSec(): def isCustomOLSBinaryInstalled():
"""Detect if custom OpenLiteSpeed binary is installed"""
try: try:
OLS_BINARY_PATH = "/usr/local/lsws/bin/openlitespeed"
if not os.path.exists(OLS_BINARY_PATH):
return False
# Check for PHPConfig function signature in binary
command = f'strings {OLS_BINARY_PATH}'
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=10)
if result.returncode == 0:
# Look for custom binary markers
return 'set_php_config_value' in result.stdout or 'PHPConfig LSIAPI' in result.stdout
return False
except Exception as msg:
logging.CyberCPLogFileWriter.writeToFile(f"WARNING: Could not detect OLS binary type: {msg}")
return False
@staticmethod
def detectBinarySuffix():
"""Detect which binary suffix to use based on OS distribution
Returns 'ubuntu' for Ubuntu/Debian systems
Returns 'rhel8' for RHEL/AlmaLinux/Rocky 8.x systems
Returns 'rhel9' for RHEL/AlmaLinux/Rocky 9.x systems
"""
try:
# Check if we're on RHEL/CentOS/AlmaLinux or Ubuntu/Debian
if os.path.exists('/etc/os-release'):
with open('/etc/os-release', 'r') as f:
os_release = f.read().lower()
# Check for Ubuntu/Debian FIRST
if 'ubuntu' in os_release or 'debian' in os_release:
return 'ubuntu'
# Check for RHEL-based distributions
if any(x in os_release for x in ['almalinux', 'rocky', 'rhel', 'centos stream']):
# Extract version number
for line in os_release.split('\n'):
if 'version_id' in line:
version = line.split('=')[1].strip('"').split('.')[0]
if version == '9':
return 'rhel9'
elif version == '8':
return 'rhel8'
# Check CentOS/RHEL path (legacy method)
if os.path.exists('/etc/redhat-release'):
data = open('/etc/redhat-release', 'r').read()
if 'release 9' in data:
return 'rhel9'
elif 'release 8' in data:
return 'rhel8'
# Default to ubuntu
return 'ubuntu'
except Exception as msg:
logging.CyberCPLogFileWriter.writeToFile(f"Error detecting OS: {msg}, defaulting to Ubuntu binaries")
return 'ubuntu'
@staticmethod
def installCompatibleModSecurity():
"""Install ModSecurity compatible with custom OpenLiteSpeed binary"""
try:
mailUtilities.checkHome() mailUtilities.checkHome()
with open(modSec.installLogPath, 'w') as f:
f.write("Installing ModSecurity compatible with custom OpenLiteSpeed binary...\n")
MODSEC_PATH = "/usr/local/lsws/modules/mod_security.so"
# Detect OS and select appropriate ModSecurity binary
binary_suffix = modSec.detectBinarySuffix()
BASE_URL = "https://cyberpanel.net/binaries"
if binary_suffix == 'rhel8':
MODSEC_URL = f"{BASE_URL}/rhel8/mod_security-compatible-rhel8.so"
EXPECTED_SHA256 = "8c769dfb42711851ec539e9b6ea649616c14b0e85a53eb18755d200ce29bc442"
elif binary_suffix == 'rhel9':
MODSEC_URL = f"{BASE_URL}/rhel9/mod_security-compatible-rhel.so"
EXPECTED_SHA256 = "db580afc431fda40d46bdae2249ac74690d9175ff6d8b1843f2837d86f8d602f"
else: # ubuntu
MODSEC_URL = f"{BASE_URL}/ubuntu/mod_security-compatible-ubuntu.so"
EXPECTED_SHA256 = "115971fcd44b74bc7c7b097b9cec33ddcfb0fb07bb9b562ec9f4f0691c388a6b"
# Download to temp location
tmp_modsec = "/tmp/mod_security_custom.so"
with open(modSec.installLogPath, 'a') as f:
f.write(f"Downloading compatible ModSecurity for {binary_suffix}...\n")
command = f'wget -q --show-progress {MODSEC_URL} -O {tmp_modsec}'
result = subprocess.call(shlex.split(command))
if result != 0 or not os.path.exists(tmp_modsec):
with open(modSec.installLogPath, 'a') as f:
f.write("ERROR: Failed to download ModSecurity\n")
f.write("Can not be installed.[404]\n")
logging.CyberCPLogFileWriter.writeToFile("[Could not download compatible ModSecurity]")
return 0
# Verify checksum
with open(modSec.installLogPath, 'a') as f:
f.write("Verifying checksum...\n")
result = subprocess.run(f'sha256sum {tmp_modsec}', shell=True, capture_output=True, text=True)
actual_sha256 = result.stdout.split()[0]
if actual_sha256 != EXPECTED_SHA256:
with open(modSec.installLogPath, 'a') as f:
f.write(f"ERROR: Checksum verification failed\n")
f.write(f" Expected: {EXPECTED_SHA256}\n")
f.write(f" Got: {actual_sha256}\n")
f.write("Can not be installed.[404]\n")
os.remove(tmp_modsec)
logging.CyberCPLogFileWriter.writeToFile("[ModSecurity checksum verification failed]")
return 0
# Backup existing ModSecurity if present
if os.path.exists(MODSEC_PATH):
backup_path = f"{MODSEC_PATH}.backup.{int(time.time())}"
shutil.copy2(MODSEC_PATH, backup_path)
with open(modSec.installLogPath, 'a') as f:
f.write(f"Backed up existing ModSecurity to: {backup_path}\n")
# Stop OpenLiteSpeed
subprocess.run(['/usr/local/lsws/bin/lswsctrl', 'stop'], timeout=30)
time.sleep(2)
# Install compatible ModSecurity
os.makedirs(os.path.dirname(MODSEC_PATH), exist_ok=True)
shutil.copy2(tmp_modsec, MODSEC_PATH)
os.chmod(MODSEC_PATH, 0o755)
os.remove(tmp_modsec)
# Start OpenLiteSpeed
subprocess.run(['/usr/local/lsws/bin/lswsctrl', 'start'], timeout=30)
with open(modSec.installLogPath, 'a') as f:
f.write("Compatible ModSecurity installed successfully\n")
f.write("ModSecurity Installed (ABI-compatible version).[200]\n")
logging.CyberCPLogFileWriter.writeToFile("[Compatible ModSecurity installed successfully]")
return 1
except subprocess.TimeoutExpired:
with open(modSec.installLogPath, 'a') as f:
f.write("ERROR: Timeout during OpenLiteSpeed restart\n")
f.write("Can not be installed.[404]\n")
logging.CyberCPLogFileWriter.writeToFile("[Timeout during ModSecurity installation]")
return 0
except Exception as msg:
with open(modSec.installLogPath, 'a') as f:
f.write(f"ERROR: {str(msg)}\n")
f.write("Can not be installed.[404]\n")
logging.CyberCPLogFileWriter.writeToFile(str(msg) + "[installCompatibleModSecurity]")
return 0
@staticmethod
def installModSec():
try:
mailUtilities.checkHome()
# Check if custom OLS binary is installed
if modSec.isCustomOLSBinaryInstalled():
# Install compatible ModSecurity for custom OLS
with open(modSec.installLogPath, 'w') as f:
f.write("Detected custom OpenLiteSpeed binary\n")
f.write("Installing ABI-compatible ModSecurity...\n")
return modSec.installCompatibleModSecurity()
# Stock OLS binary - use package manager as usual
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8: if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
command = 'sudo yum install ols-modsecurity -y' command = 'sudo yum install ols-modsecurity -y'
else: else:

View File

@@ -619,6 +619,471 @@ class Upgrade:
except IOError as err: except IOError as err:
pass pass
@staticmethod
def detectArchitecture():
"""Detect system architecture - custom binaries only for x86_64"""
try:
import platform
arch = platform.machine()
return arch == "x86_64"
except Exception as msg:
Upgrade.stdOut(str(msg) + " [detectArchitecture]", 0)
return False
@staticmethod
def detectBinarySuffix():
"""Detect which binary suffix to use based on OS distribution
Returns 'ubuntu' for Ubuntu/Debian systems
Returns 'rhel8' for RHEL/AlmaLinux/Rocky 8.x systems
Returns 'rhel9' for RHEL/AlmaLinux/Rocky 9.x systems
"""
try:
# Check if we're on RHEL/CentOS/AlmaLinux or Ubuntu/Debian
if os.path.exists('/etc/os-release'):
with open('/etc/os-release', 'r') as f:
os_release = f.read().lower()
# Check for Ubuntu/Debian FIRST
if 'ubuntu' in os_release or 'debian' in os_release:
return 'ubuntu'
# Check for RHEL-based distributions
if any(x in os_release for x in ['almalinux', 'rocky', 'rhel', 'centos stream']):
# Extract version number
for line in os_release.split('\n'):
if 'version_id' in line:
version = line.split('=')[1].strip('"').split('.')[0]
if version == '9':
return 'rhel9'
elif version == '8':
return 'rhel8'
# Check CentOS/RHEL path (legacy method)
if os.path.exists(Upgrade.CentOSPath):
data = open(Upgrade.CentOSPath, 'r').read()
if 'release 9' in data:
return 'rhel9'
elif 'release 8' in data:
return 'rhel8'
# CentOS 7 → ubuntu suffix (uses libcrypt.so.1)
else:
return 'ubuntu'
# OpenEuler → rhel9 suffix (assuming latest version)
if os.path.exists(Upgrade.openEulerPath):
return 'rhel9'
# Ubuntu/Debian → ubuntu suffix (default for unknown)
return 'ubuntu'
except Exception as msg:
Upgrade.stdOut(f"Error detecting OS: {msg}, defaulting to Ubuntu binaries", 0)
return 'ubuntu'
@staticmethod
def downloadCustomBinary(url, destination):
"""Download custom binary file"""
try:
Upgrade.stdOut(f"Downloading {os.path.basename(destination)}...", 0)
# Use wget for better progress display
command = f'wget -q --show-progress {url} -O {destination}'
res = subprocess.call(shlex.split(command))
# Check if file was downloaded successfully by verifying it exists and has reasonable size
if os.path.exists(destination):
file_size = os.path.getsize(destination)
# Verify file size is reasonable (at least 10KB to avoid error pages/empty files)
if file_size > 10240: # 10KB
if file_size > 1048576: # 1MB
Upgrade.stdOut(f"Downloaded successfully ({file_size / (1024*1024):.2f} MB)", 0)
else:
Upgrade.stdOut(f"Downloaded successfully ({file_size / 1024:.2f} KB)", 0)
return True
else:
Upgrade.stdOut(f"ERROR: Downloaded file too small ({file_size} bytes)", 0)
return False
else:
Upgrade.stdOut("ERROR: Download failed - file not found", 0)
return False
except Exception as msg:
Upgrade.stdOut(f"ERROR: {msg} [downloadCustomBinary]", 0)
return False
@staticmethod
def verifyCustomBinary(binary_path):
"""Verify custom binary has correct dependencies and can run"""
try:
Upgrade.stdOut("Verifying custom binary compatibility...", 0)
# Check library dependencies
command = f'ldd {binary_path}'
result = subprocess.run(command, shell=True, capture_output=True, text=True)
if result.returncode != 0:
Upgrade.stdOut("ERROR: Failed to check binary dependencies", 0)
return False
# Check for missing libraries
if 'not found' in result.stdout:
Upgrade.stdOut("ERROR: Binary has missing library dependencies:", 0)
for line in result.stdout.split('\n'):
if 'not found' in line:
Upgrade.stdOut(f" {line.strip()}", 0)
return False
# Try to run the binary with -v to check if it can execute
command = f'{binary_path} -v'
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=5)
if result.returncode != 0:
Upgrade.stdOut("ERROR: Binary failed to execute", 0)
if result.stderr:
Upgrade.stdOut(f" Error: {result.stderr.strip()}", 0)
return False
Upgrade.stdOut("Binary verification successful", 0)
return True
except subprocess.TimeoutExpired:
Upgrade.stdOut("ERROR: Binary verification timed out", 0)
return False
except Exception as msg:
Upgrade.stdOut(f"ERROR: Verification failed: {msg}", 0)
return False
@staticmethod
def rollbackCustomBinary(backup_dir, binary_path, module_path):
"""Rollback to original binary if custom binary fails"""
try:
Upgrade.stdOut("Rolling back to original binary...", 0)
backup_binary = f"{backup_dir}/openlitespeed.backup"
# Restore original binary if backup exists
if os.path.exists(backup_binary):
shutil.copy2(backup_binary, binary_path)
os.chmod(binary_path, 0o755)
Upgrade.stdOut("Original binary restored successfully", 0)
else:
Upgrade.stdOut("WARNING: No backup found, cannot restore", 0)
# Remove failed custom module
if os.path.exists(module_path):
os.remove(module_path)
Upgrade.stdOut("Custom module removed", 0)
Upgrade.stdOut("Rollback completed", 0)
return True
except Exception as msg:
Upgrade.stdOut(f"ERROR: Rollback failed: {msg}", 0)
return False
@staticmethod
def installCustomOLSBinaries():
"""Install custom OpenLiteSpeed binaries with PHP config support"""
try:
Upgrade.stdOut("Installing Custom OpenLiteSpeed Binaries", 0)
Upgrade.stdOut("=" * 50, 0)
# Check architecture
if not Upgrade.detectArchitecture():
Upgrade.stdOut("WARNING: Custom binaries only available for x86_64", 0)
Upgrade.stdOut("Skipping custom binary installation", 0)
Upgrade.stdOut("Standard OLS will be used", 0)
return True # Not a failure, just skip
# Detect OS and select appropriate binary suffix
binary_suffix = Upgrade.detectBinarySuffix()
Upgrade.stdOut(f"Detected OS type: using '{binary_suffix}' binaries", 0)
# URLs for custom binaries with OS-specific paths
BASE_URL = "https://cyberpanel.net/binaries"
# Set URLs based on OS type
if binary_suffix == 'rhel8':
OLS_BINARY_URL = f"{BASE_URL}/rhel8/openlitespeed-phpconfig-x86_64-rhel8"
MODULE_URL = f"{BASE_URL}/rhel8/cyberpanel_ols_x86_64_rhel8.so"
elif binary_suffix == 'rhel9':
OLS_BINARY_URL = f"{BASE_URL}/rhel9/openlitespeed-phpconfig-x86_64-rhel"
MODULE_URL = f"{BASE_URL}/rhel9/cyberpanel_ols_x86_64_rhel.so"
else: # ubuntu
OLS_BINARY_URL = f"{BASE_URL}/ubuntu/openlitespeed-phpconfig-x86_64-ubuntu"
MODULE_URL = f"{BASE_URL}/ubuntu/cyberpanel_ols_x86_64_ubuntu.so"
OLS_BINARY_PATH = "/usr/local/lsws/bin/openlitespeed"
MODULE_PATH = "/usr/local/lsws/modules/cyberpanel_ols.so"
# Create backup
from datetime import datetime
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
backup_dir = f"/usr/local/lsws/backup-{timestamp}"
try:
os.makedirs(backup_dir, exist_ok=True)
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)
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"
Upgrade.stdOut("Downloading custom binaries...", 0)
# Download OpenLiteSpeed binary
if not Upgrade.downloadCustomBinary(OLS_BINARY_URL, tmp_binary):
Upgrade.stdOut("ERROR: Failed to download OLS binary", 0)
Upgrade.stdOut("Continuing with standard OLS", 0)
return True # Not fatal, continue with standard OLS
# Download module
if not Upgrade.downloadCustomBinary(MODULE_URL, tmp_module):
Upgrade.stdOut("ERROR: Failed to download module", 0)
Upgrade.stdOut("Continuing with standard OLS", 0)
return True # Not fatal, continue with standard OLS
# Install OpenLiteSpeed binary
Upgrade.stdOut("Installing custom binaries...", 0)
try:
shutil.move(tmp_binary, OLS_BINARY_PATH)
os.chmod(OLS_BINARY_PATH, 0o755)
Upgrade.stdOut("Installed OpenLiteSpeed binary", 0)
except Exception as e:
Upgrade.stdOut(f"ERROR: Failed to install binary: {e}", 0)
return False
# Install module
try:
os.makedirs(os.path.dirname(MODULE_PATH), exist_ok=True)
shutil.move(tmp_module, MODULE_PATH)
os.chmod(MODULE_PATH, 0o644)
Upgrade.stdOut("Installed CyberPanel module", 0)
except Exception as e:
Upgrade.stdOut(f"ERROR: Failed to install module: {e}", 0)
return False
# Verify installation files exist
if not (os.path.exists(OLS_BINARY_PATH) and os.path.exists(MODULE_PATH)):
Upgrade.stdOut("ERROR: Installation verification failed - files not found", 0)
return False
# Verify binary compatibility
if not Upgrade.verifyCustomBinary(OLS_BINARY_PATH):
Upgrade.stdOut("ERROR: Custom binary verification failed", 0)
Upgrade.stdOut("This usually means wrong binary type for your OS", 0)
# Rollback to original binary
if os.path.exists(backup_dir):
Upgrade.rollbackCustomBinary(backup_dir, OLS_BINARY_PATH, MODULE_PATH)
Upgrade.stdOut("Continuing with standard OLS", 0)
else:
Upgrade.stdOut("WARNING: Cannot rollback, no backup found", 0)
return True # Non-fatal, continue with standard OLS
# Success!
Upgrade.stdOut("=" * 50, 0)
Upgrade.stdOut("Custom Binaries Installed Successfully", 0)
Upgrade.stdOut("Features enabled:", 0)
Upgrade.stdOut(" - Apache-style .htaccess support", 0)
Upgrade.stdOut(" - php_value/php_flag directives", 0)
Upgrade.stdOut(" - Enhanced header control", 0)
Upgrade.stdOut(f"Backup: {backup_dir}", 0)
Upgrade.stdOut("=" * 50, 0)
return True
except Exception as msg:
Upgrade.stdOut(f"ERROR: {msg} [installCustomOLSBinaries]", 0)
Upgrade.stdOut("Continuing with standard OLS", 0)
return True # Non-fatal error, continue
@staticmethod
def isCustomOLSBinaryInstalled():
"""Detect if custom OpenLiteSpeed binary is installed"""
try:
OLS_BINARY_PATH = "/usr/local/lsws/bin/openlitespeed"
if not os.path.exists(OLS_BINARY_PATH):
return False
# Check for PHPConfig function signature in binary
command = f'strings {OLS_BINARY_PATH}'
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=10)
if result.returncode == 0:
# Look for custom binary markers
return 'set_php_config_value' in result.stdout or 'PHPConfig LSIAPI' in result.stdout
return False
except Exception as msg:
Upgrade.stdOut(f"WARNING: Could not detect OLS binary type: {msg}", 0)
return False
@staticmethod
def installCompatibleModSecurity():
"""Install ModSecurity compatible with custom OpenLiteSpeed binary"""
try:
Upgrade.stdOut("Installing ModSecurity compatible with custom OpenLiteSpeed binary...", 0)
MODSEC_PATH = "/usr/local/lsws/modules/mod_security.so"
# Detect OS and select appropriate ModSecurity binary
binary_suffix = Upgrade.detectBinarySuffix()
BASE_URL = "https://cyberpanel.net/binaries"
if binary_suffix == 'rhel8':
MODSEC_URL = f"{BASE_URL}/rhel8/mod_security-compatible-rhel8.so"
EXPECTED_SHA256 = "8c769dfb42711851ec539e9b6ea649616c14b0e85a53eb18755d200ce29bc442"
EXPECTED_MD5 = "b7b9eb20de42b7f8c9c8f4c7a019d6ff"
elif binary_suffix == 'rhel9':
MODSEC_URL = f"{BASE_URL}/rhel9/mod_security-compatible-rhel.so"
EXPECTED_SHA256 = "db580afc431fda40d46bdae2249ac74690d9175ff6d8b1843f2837d86f8d602f"
EXPECTED_MD5 = "1efa1e442fe8eedf4705584ac194fc95"
else: # ubuntu
MODSEC_URL = f"{BASE_URL}/ubuntu/mod_security-compatible-ubuntu.so"
EXPECTED_SHA256 = "115971fcd44b74bc7c7b097b9cec33ddcfb0fb07bb9b562ec9f4f0691c388a6b"
EXPECTED_MD5 = "c3987c41182355c1290530b6553658db"
# Download to temp location
tmp_modsec = "/tmp/mod_security_custom.so"
Upgrade.stdOut(f"Downloading compatible ModSecurity for {binary_suffix}...", 0)
command = f'wget -q --show-progress {MODSEC_URL} -O {tmp_modsec}'
result = subprocess.call(shlex.split(command))
if result != 0 or not os.path.exists(tmp_modsec):
Upgrade.stdOut("ERROR: Failed to download ModSecurity", 0)
return False
# Verify checksum
Upgrade.stdOut("Verifying checksum...", 0)
result = subprocess.run(f'sha256sum {tmp_modsec}', shell=True, capture_output=True, text=True)
actual_sha256 = result.stdout.split()[0]
if actual_sha256 != EXPECTED_SHA256:
Upgrade.stdOut(f"ERROR: Checksum verification failed", 0)
Upgrade.stdOut(f" Expected: {EXPECTED_SHA256}", 0)
Upgrade.stdOut(f" Got: {actual_sha256}", 0)
os.remove(tmp_modsec)
return False
Upgrade.stdOut("Checksum verified successfully", 0)
# Backup existing ModSecurity if present
if os.path.exists(MODSEC_PATH):
backup_path = f"{MODSEC_PATH}.backup.{int(time.time())}"
shutil.copy2(MODSEC_PATH, backup_path)
Upgrade.stdOut(f"Backed up existing ModSecurity to: {backup_path}", 0)
# Stop OpenLiteSpeed
Upgrade.stdOut("Stopping OpenLiteSpeed...", 0)
subprocess.run(['/usr/local/lsws/bin/lswsctrl', 'stop'], timeout=30)
time.sleep(2)
# Install compatible ModSecurity
os.makedirs(os.path.dirname(MODSEC_PATH), exist_ok=True)
shutil.copy2(tmp_modsec, MODSEC_PATH)
os.chmod(MODSEC_PATH, 0o755)
os.remove(tmp_modsec)
Upgrade.stdOut("Compatible ModSecurity installed successfully", 0)
# Start OpenLiteSpeed
Upgrade.stdOut("Starting OpenLiteSpeed...", 0)
subprocess.run(['/usr/local/lsws/bin/lswsctrl', 'start'], timeout=30)
Upgrade.stdOut("✓ ModSecurity updated to compatible version", 0)
return True
except subprocess.TimeoutExpired:
Upgrade.stdOut("ERROR: Timeout during OpenLiteSpeed restart", 0)
return False
except Exception as msg:
Upgrade.stdOut(f"ERROR: ModSecurity installation failed: {msg}", 0)
return False
@staticmethod
def handleModSecurityCompatibility():
"""Check and update ModSecurity if custom OLS binary is installed"""
try:
MODSEC_PATH = "/usr/local/lsws/modules/mod_security.so"
# Check if ModSecurity is installed
if not os.path.exists(MODSEC_PATH):
Upgrade.stdOut("ModSecurity not installed, skipping compatibility check", 0)
return True
# Check if custom OLS binary is installed
if not Upgrade.isCustomOLSBinaryInstalled():
Upgrade.stdOut("Stock OLS binary detected, ModSecurity compatibility check not needed", 0)
return True
Upgrade.stdOut("=" * 50, 0)
Upgrade.stdOut("Detected ModSecurity with custom OpenLiteSpeed binary", 0)
Upgrade.stdOut("Updating to ABI-compatible ModSecurity version...", 0)
Upgrade.stdOut("=" * 50, 0)
# Install compatible version
if Upgrade.installCompatibleModSecurity():
Upgrade.stdOut("ModSecurity compatibility update completed", 0)
return True
else:
Upgrade.stdOut("WARNING: ModSecurity compatibility update failed", 0)
Upgrade.stdOut("Server may experience crashes. Please contact support.", 0)
return False
except Exception as msg:
Upgrade.stdOut(f"ERROR in ModSecurity compatibility check: {msg}", 0)
return False
@staticmethod
def configureCustomModule():
"""Configure CyberPanel module in OpenLiteSpeed config"""
try:
Upgrade.stdOut("Configuring CyberPanel module...", 0)
CONFIG_FILE = "/usr/local/lsws/conf/httpd_config.conf"
if not os.path.exists(CONFIG_FILE):
Upgrade.stdOut("WARNING: Config file not found", 0)
Upgrade.stdOut("Module will be auto-loaded", 0)
return True
# Check if module is already configured
with open(CONFIG_FILE, 'r') as f:
content = f.read()
if 'cyberpanel_ols' in content:
Upgrade.stdOut("Module already configured", 0)
return True
# Add module configuration
module_config = """
module cyberpanel_ols {
ls_enabled 1
}
"""
# Backup config
shutil.copy2(CONFIG_FILE, f"{CONFIG_FILE}.backup")
# Append module config
with open(CONFIG_FILE, 'a') as f:
f.write(module_config)
Upgrade.stdOut("Module configured successfully", 0)
return True
except Exception as msg:
Upgrade.stdOut(f"WARNING: Module configuration failed: {msg}", 0)
Upgrade.stdOut("Module may still work via auto-load", 0)
return True # Non-fatal
@staticmethod @staticmethod
def download_install_phpmyadmin(): def download_install_phpmyadmin():
try: try:
@@ -4287,7 +4752,7 @@ pm.max_spare_servers = 3
## Add LSPHP7.4 TO LSWS Ent configs ## Add LSPHP7.4 TO LSWS Ent configs
if not os.path.exists('/usr/local/lsws/bin/openlitespeed'): if not os.path.exists('/usr/local/lsws/bin/openlitespeed'):
# This is Enterprise LSWS
if os.path.exists('httpd_config.xml'): if os.path.exists('httpd_config.xml'):
os.remove('httpd_config.xml') os.remove('httpd_config.xml')
@@ -4295,6 +4760,25 @@ pm.max_spare_servers = 3
Upgrade.executioner(command, command, 0) Upgrade.executioner(command, command, 0)
# os.remove('/usr/local/lsws/conf/httpd_config.xml') # os.remove('/usr/local/lsws/conf/httpd_config.xml')
# shutil.copy('httpd_config.xml', '/usr/local/lsws/conf/httpd_config.xml') # shutil.copy('httpd_config.xml', '/usr/local/lsws/conf/httpd_config.xml')
else:
# This is OpenLiteSpeed - install/upgrade custom binaries
Upgrade.stdOut("Detected OpenLiteSpeed installation", 0)
Upgrade.stdOut("Installing/upgrading custom binaries with .htaccess PHP config support...", 0)
# Install custom binaries
if Upgrade.installCustomOLSBinaries():
# Configure the custom module
Upgrade.configureCustomModule()
# Check and update ModSecurity compatibility if needed
Upgrade.handleModSecurityCompatibility()
# Restart OpenLiteSpeed to apply changes
Upgrade.stdOut("Restarting OpenLiteSpeed...", 0)
command = '/usr/local/lsws/bin/lswsctrl restart'
Upgrade.executioner(command, 'Restart OpenLiteSpeed', 0)
else:
Upgrade.stdOut("Custom binary installation failed, continuing with upgrade...", 0)
Upgrade.updateRepoURL() Upgrade.updateRepoURL()