mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-12-16 05:19:43 +01:00
Merge pull request #1510 from master3395/v2.5.5-dev
V2.5.5 dev - Firewall ban button, and management
This commit is contained in:
@@ -1152,6 +1152,12 @@ Automatic backup failed for %s on %s.
|
||||
#
|
||||
# command = 'chattr -R -i /home/%s/incbackup/' % (website.domain)
|
||||
# ProcessUtilities.executioner(command)
|
||||
#
|
||||
# command = 'chattr -R -i /home/%s/lscache/' % (website.domain)
|
||||
# ProcessUtilities.executioner(command)
|
||||
#
|
||||
# command = 'chattr -R -i /home/%s/.cagefs/' % (website.domain)
|
||||
# ProcessUtilities.executioner(command)
|
||||
# else:
|
||||
# command = 'chattr -R -i /home/%s/' % (website.domain)
|
||||
# ProcessUtilities.executioner(command)
|
||||
|
||||
@@ -139,6 +139,134 @@ class CronUtil:
|
||||
command = 'chmod 1730 /var/spool/cron/crontabs'
|
||||
ProcessUtilities.outputExecutioner(command)
|
||||
|
||||
@staticmethod
|
||||
def suspendWebsiteCrons(externalApp):
|
||||
"""
|
||||
Suspend all cron jobs for a website by backing up and clearing the cron file.
|
||||
This prevents cron jobs from running when a website is suspended.
|
||||
"""
|
||||
try:
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
cronPath = "/var/spool/cron/" + externalApp
|
||||
backupPath = "/var/spool/cron/" + externalApp + ".suspended"
|
||||
else:
|
||||
cronPath = "/var/spool/cron/crontabs/" + externalApp
|
||||
backupPath = "/var/spool/cron/crontabs/" + externalApp + ".suspended"
|
||||
|
||||
# Check if cron file exists
|
||||
if not os.path.exists(cronPath):
|
||||
print("1,None") # No cron file to suspend
|
||||
return
|
||||
|
||||
# Create backup of current cron jobs
|
||||
try:
|
||||
command = f'cp {cronPath} {backupPath}'
|
||||
ProcessUtilities.executioner(command, 'root')
|
||||
except Exception as e:
|
||||
print(f"0,Warning: Could not backup cron file: {str(e)}")
|
||||
|
||||
# Clear the cron file to suspend all jobs
|
||||
try:
|
||||
CronUtil.CronPrem(1) # Enable permissions
|
||||
|
||||
# Create empty cron file or clear existing one
|
||||
with open(cronPath, 'w') as f:
|
||||
f.write('') # Empty file to disable all cron jobs
|
||||
|
||||
# Set proper ownership
|
||||
command = f'chown {externalApp}:{externalApp} {cronPath}'
|
||||
ProcessUtilities.executioner(command, 'root')
|
||||
|
||||
CronUtil.CronPrem(0) # Restore permissions
|
||||
|
||||
print("1,Cron jobs suspended successfully")
|
||||
|
||||
except Exception as e:
|
||||
CronUtil.CronPrem(0) # Ensure permissions are restored
|
||||
print(f"0,Failed to suspend cron jobs: {str(e)}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"0,Error suspending cron jobs: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
def restoreWebsiteCrons(externalApp):
|
||||
"""
|
||||
Restore cron jobs for a website by restoring from backup file.
|
||||
This restores cron jobs when a website is unsuspended.
|
||||
"""
|
||||
try:
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
cronPath = "/var/spool/cron/" + externalApp
|
||||
backupPath = "/var/spool/cron/" + externalApp + ".suspended"
|
||||
else:
|
||||
cronPath = "/var/spool/cron/crontabs/" + externalApp
|
||||
backupPath = "/var/spool/cron/crontabs/" + externalApp + ".suspended"
|
||||
|
||||
# Check if backup file exists
|
||||
if not os.path.exists(backupPath):
|
||||
print("1,No suspended cron jobs to restore")
|
||||
return
|
||||
|
||||
try:
|
||||
CronUtil.CronPrem(1) # Enable permissions
|
||||
|
||||
# Restore cron jobs from backup
|
||||
command = f'cp {backupPath} {cronPath}'
|
||||
ProcessUtilities.executioner(command, 'root')
|
||||
|
||||
# Set proper ownership
|
||||
command = f'chown {externalApp}:{externalApp} {cronPath}'
|
||||
ProcessUtilities.executioner(command, 'root')
|
||||
|
||||
# Remove backup file
|
||||
os.remove(backupPath)
|
||||
|
||||
CronUtil.CronPrem(0) # Restore permissions
|
||||
|
||||
print("1,Cron jobs restored successfully")
|
||||
|
||||
except Exception as e:
|
||||
CronUtil.CronPrem(0) # Ensure permissions are restored
|
||||
print(f"0,Failed to restore cron jobs: {str(e)}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"0,Error restoring cron jobs: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
def getCronSuspensionStatus(externalApp):
|
||||
"""
|
||||
Check if cron jobs are currently suspended for a website.
|
||||
Returns 1 if suspended, 0 if active, -1 if error.
|
||||
"""
|
||||
try:
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
cronPath = "/var/spool/cron/" + externalApp
|
||||
backupPath = "/var/spool/cron/" + externalApp + ".suspended"
|
||||
else:
|
||||
cronPath = "/var/spool/cron/crontabs/" + externalApp
|
||||
backupPath = "/var/spool/cron/crontabs/" + externalApp + ".suspended"
|
||||
|
||||
# Check if backup file exists (indicates suspension)
|
||||
if os.path.exists(backupPath):
|
||||
print("1,Cron jobs are suspended")
|
||||
return
|
||||
elif os.path.exists(cronPath):
|
||||
# Check if cron file is empty (also indicates suspension)
|
||||
try:
|
||||
with open(cronPath, 'r') as f:
|
||||
content = f.read().strip()
|
||||
if not content:
|
||||
print("1,Cron jobs are suspended (empty file)")
|
||||
else:
|
||||
print("0,Cron jobs are active")
|
||||
except Exception as e:
|
||||
print(f"-1,Error reading cron file: {str(e)}")
|
||||
else:
|
||||
print("0,No cron jobs configured")
|
||||
|
||||
except Exception as e:
|
||||
print(f"-1,Error checking cron status: {str(e)}")
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
@@ -162,6 +290,12 @@ def main():
|
||||
CronUtil.remCronbyLine(args.externalApp, int(args.line))
|
||||
elif args.function == "addNewCron":
|
||||
CronUtil.addNewCron(args.externalApp, args.finalCron)
|
||||
elif args.function == "suspendWebsiteCrons":
|
||||
CronUtil.suspendWebsiteCrons(args.externalApp)
|
||||
elif args.function == "restoreWebsiteCrons":
|
||||
CronUtil.restoreWebsiteCrons(args.externalApp)
|
||||
elif args.function == "getCronSuspensionStatus":
|
||||
CronUtil.getCronSuspensionStatus(args.externalApp)
|
||||
|
||||
|
||||
|
||||
|
||||
273
plogical/errorSanitizer.py
Normal file
273
plogical/errorSanitizer.py
Normal file
@@ -0,0 +1,273 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
CyberPanel Error Sanitization Utility
|
||||
=====================================
|
||||
|
||||
This module provides secure error handling and sanitization to prevent
|
||||
information disclosure vulnerabilities while maintaining useful error
|
||||
reporting for debugging purposes.
|
||||
|
||||
Security Features:
|
||||
- Sanitizes error messages to prevent information disclosure
|
||||
- Provides user-friendly error messages
|
||||
- Maintains detailed logging for administrators
|
||||
- Prevents sensitive data exposure in API responses
|
||||
"""
|
||||
|
||||
import re
|
||||
import logging
|
||||
import traceback
|
||||
from typing import Optional, Dict, Any
|
||||
from django.conf import settings
|
||||
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||
|
||||
class ErrorSanitizer:
|
||||
"""
|
||||
Centralized error sanitization and handling utility
|
||||
"""
|
||||
|
||||
# Sensitive patterns that should be masked in error messages
|
||||
SENSITIVE_PATTERNS = [
|
||||
# File paths
|
||||
r'/home/[^/]+/',
|
||||
r'/usr/local/[^/]+/',
|
||||
r'/var/[^/]+/',
|
||||
r'/etc/[^/]+/',
|
||||
# Database credentials
|
||||
r'password[=\s]*[^\s]+',
|
||||
r'passwd[=\s]*[^\s]+',
|
||||
r'pwd[=\s]*[^\s]+',
|
||||
# API keys and tokens
|
||||
r'api[_-]?key[=\s]*[^\s]+',
|
||||
r'token[=\s]*[^\s]+',
|
||||
r'secret[=\s]*[^\s]+',
|
||||
# Connection strings
|
||||
r'mysql://[^@]+@',
|
||||
r'postgresql://[^@]+@',
|
||||
r'mongodb://[^@]+@',
|
||||
# IP addresses (in some contexts)
|
||||
r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b',
|
||||
# Email addresses
|
||||
r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
|
||||
]
|
||||
|
||||
# Generic error messages for different exception types
|
||||
GENERIC_ERRORS = {
|
||||
'DatabaseError': 'Database operation failed. Please try again.',
|
||||
'ConnectionError': 'Unable to connect to the service. Please check your connection.',
|
||||
'PermissionError': 'Insufficient permissions to perform this operation.',
|
||||
'FileNotFoundError': 'Required file not found. Please contact support.',
|
||||
'OSError': 'System operation failed. Please try again.',
|
||||
'ValueError': 'Invalid input provided. Please check your data.',
|
||||
'KeyError': 'Required information is missing. Please try again.',
|
||||
'TypeError': 'Invalid data type provided. Please check your input.',
|
||||
'AttributeError': 'System configuration error. Please contact support.',
|
||||
'ImportError': 'System module error. Please contact support.',
|
||||
'TimeoutError': 'Operation timed out. Please try again.',
|
||||
'BaseException': 'An unexpected error occurred. Please try again.',
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def sanitize_error_message(error_message: str, exception_type: str = None) -> str:
|
||||
"""
|
||||
Sanitize error message by removing sensitive information
|
||||
|
||||
Args:
|
||||
error_message: The original error message
|
||||
exception_type: The type of exception that occurred
|
||||
|
||||
Returns:
|
||||
Sanitized error message safe for user display
|
||||
"""
|
||||
if not error_message:
|
||||
return "An error occurred. Please try again."
|
||||
|
||||
# Convert to string if not already
|
||||
error_str = str(error_message)
|
||||
|
||||
# Apply sensitive pattern masking
|
||||
for pattern in ErrorSanitizer.SENSITIVE_PATTERNS:
|
||||
error_str = re.sub(pattern, '[REDACTED]', error_str, flags=re.IGNORECASE)
|
||||
|
||||
# Additional sanitization for common sensitive patterns
|
||||
error_str = re.sub(r'[^\x00-\x7F]+', '[NON-ASCII]', error_str) # Remove non-ASCII chars
|
||||
error_str = re.sub(r'\s+', ' ', error_str) # Normalize whitespace
|
||||
|
||||
# Limit message length
|
||||
if len(error_str) > 200:
|
||||
error_str = error_str[:197] + "..."
|
||||
|
||||
return error_str.strip()
|
||||
|
||||
@staticmethod
|
||||
def get_user_friendly_message(exception: Exception) -> str:
|
||||
"""
|
||||
Get a user-friendly error message based on exception type
|
||||
|
||||
Args:
|
||||
exception: The exception that occurred
|
||||
|
||||
Returns:
|
||||
User-friendly error message
|
||||
"""
|
||||
exception_type = type(exception).__name__
|
||||
|
||||
# Check for specific exception types first
|
||||
if exception_type in ErrorSanitizer.GENERIC_ERRORS:
|
||||
return ErrorSanitizer.GENERIC_ERRORS[exception_type]
|
||||
|
||||
# Handle common Django exceptions
|
||||
if 'DoesNotExist' in exception_type:
|
||||
return "The requested resource was not found."
|
||||
elif 'ValidationError' in exception_type:
|
||||
return "Invalid data provided. Please check your input."
|
||||
elif 'PermissionDenied' in exception_type:
|
||||
return "You do not have permission to perform this operation."
|
||||
|
||||
# Default generic message
|
||||
return "An unexpected error occurred. Please try again."
|
||||
|
||||
@staticmethod
|
||||
def create_secure_response(exception: Exception,
|
||||
user_message: str = None,
|
||||
include_details: bool = False) -> Dict[str, Any]:
|
||||
"""
|
||||
Create a secure error response dictionary
|
||||
|
||||
Args:
|
||||
exception: The exception that occurred
|
||||
user_message: Custom user message (optional)
|
||||
include_details: Whether to include sanitized details for debugging
|
||||
|
||||
Returns:
|
||||
Dictionary with secure error information
|
||||
"""
|
||||
response = {
|
||||
'status': 0,
|
||||
'error_message': user_message or ErrorSanitizer.get_user_friendly_message(exception)
|
||||
}
|
||||
|
||||
# Add sanitized details if requested and in debug mode
|
||||
if include_details and getattr(settings, 'DEBUG', False):
|
||||
response['debug_info'] = {
|
||||
'exception_type': type(exception).__name__,
|
||||
'sanitized_message': ErrorSanitizer.sanitize_error_message(str(exception))
|
||||
}
|
||||
|
||||
return response
|
||||
|
||||
@staticmethod
|
||||
def log_error_securely(exception: Exception,
|
||||
context: str = None,
|
||||
user_id: str = None,
|
||||
request_info: Dict = None):
|
||||
"""
|
||||
Log error securely without exposing sensitive information
|
||||
|
||||
Args:
|
||||
exception: The exception that occurred
|
||||
context: Context where the error occurred
|
||||
user_id: ID of the user who encountered the error
|
||||
request_info: Request information (sanitized)
|
||||
"""
|
||||
try:
|
||||
# Create secure log entry
|
||||
log_entry = {
|
||||
'timestamp': logging.get_current_timestamp(),
|
||||
'exception_type': type(exception).__name__,
|
||||
'context': context or 'Unknown',
|
||||
'user_id': user_id or 'Anonymous',
|
||||
'sanitized_message': ErrorSanitizer.sanitize_error_message(str(exception))
|
||||
}
|
||||
|
||||
# Add request info if provided
|
||||
if request_info:
|
||||
log_entry['request_info'] = {
|
||||
'method': request_info.get('method', 'Unknown'),
|
||||
'path': request_info.get('path', 'Unknown'),
|
||||
'ip': request_info.get('ip', 'Unknown')
|
||||
}
|
||||
|
||||
# Log the error
|
||||
logging.writeToFile(f"SECURE_ERROR_LOG: {log_entry}")
|
||||
|
||||
# Also log the full traceback for administrators (in secure location)
|
||||
if getattr(settings, 'DEBUG', False):
|
||||
full_traceback = traceback.format_exc()
|
||||
sanitized_traceback = ErrorSanitizer.sanitize_error_message(full_traceback)
|
||||
logging.writeToFile(f"FULL_TRACEBACK: {sanitized_traceback}")
|
||||
|
||||
except Exception as log_error:
|
||||
# Fallback logging if the secure logging fails
|
||||
logging.writeToFile(f"LOGGING_ERROR: Failed to log error - {str(log_error)}")
|
||||
|
||||
@staticmethod
|
||||
def handle_exception(exception: Exception,
|
||||
context: str = None,
|
||||
user_id: str = None,
|
||||
request_info: Dict = None,
|
||||
return_response: bool = True) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Comprehensive exception handling with secure logging and response
|
||||
|
||||
Args:
|
||||
exception: The exception to handle
|
||||
context: Context where the error occurred
|
||||
user_id: ID of the user who encountered the error
|
||||
request_info: Request information
|
||||
return_response: Whether to return a response dictionary
|
||||
|
||||
Returns:
|
||||
Secure error response dictionary if return_response is True
|
||||
"""
|
||||
# Log the error securely
|
||||
ErrorSanitizer.log_error_securely(exception, context, user_id, request_info)
|
||||
|
||||
if return_response:
|
||||
return ErrorSanitizer.create_secure_response(exception)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class SecureExceptionHandler:
|
||||
"""
|
||||
Context manager for secure exception handling
|
||||
"""
|
||||
|
||||
def __init__(self, context: str = None, user_id: str = None, request_info: Dict = None):
|
||||
self.context = context
|
||||
self.user_id = user_id
|
||||
self.request_info = request_info
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if exc_type is not None:
|
||||
ErrorSanitizer.handle_exception(
|
||||
exc_val,
|
||||
self.context,
|
||||
self.user_id,
|
||||
self.request_info,
|
||||
return_response=False
|
||||
)
|
||||
# Return True to suppress the exception (we've handled it)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# Convenience functions for common use cases
|
||||
def secure_error_response(exception: Exception, user_message: str = None) -> Dict[str, Any]:
|
||||
"""Create a secure error response for API endpoints"""
|
||||
return ErrorSanitizer.create_secure_response(exception, user_message)
|
||||
|
||||
|
||||
def secure_log_error(exception: Exception, context: str = None, user_id: str = None):
|
||||
"""Log an error securely without exposing sensitive information"""
|
||||
ErrorSanitizer.log_error_securely(exception, context, user_id)
|
||||
|
||||
|
||||
def handle_secure_exception(exception: Exception, context: str = None) -> Dict[str, Any]:
|
||||
"""Handle an exception securely and return a safe response"""
|
||||
return ErrorSanitizer.handle_exception(exception, context, return_response=True)
|
||||
@@ -89,11 +89,35 @@ class FTPUtilities:
|
||||
@staticmethod
|
||||
def ftpFunctions(path,externalApp):
|
||||
try:
|
||||
|
||||
command = 'mkdir %s' % (path)
|
||||
ProcessUtilities.executioner(command, externalApp)
|
||||
|
||||
return 1,'None'
|
||||
# Enhanced path validation and creation
|
||||
import os
|
||||
|
||||
# Check if path already exists
|
||||
if os.path.exists(path):
|
||||
# Path exists, ensure it's a directory
|
||||
if not os.path.isdir(path):
|
||||
return 0, "Specified path exists but is not a directory"
|
||||
# Set proper permissions
|
||||
command = 'chown -R %s:%s %s' % (externalApp, externalApp, path)
|
||||
ProcessUtilities.executioner(command, externalApp)
|
||||
return 1, 'None'
|
||||
else:
|
||||
# Create the directory with proper permissions
|
||||
command = 'mkdir -p %s' % (path)
|
||||
result = ProcessUtilities.executioner(command, externalApp)
|
||||
|
||||
if result == 0:
|
||||
# Set proper ownership
|
||||
command = 'chown -R %s:%s %s' % (externalApp, externalApp, path)
|
||||
ProcessUtilities.executioner(command, externalApp)
|
||||
|
||||
# Set proper permissions (755)
|
||||
command = 'chmod 755 %s' % (path)
|
||||
ProcessUtilities.executioner(command, externalApp)
|
||||
|
||||
return 1, 'None'
|
||||
else:
|
||||
return 0, "Failed to create directory: %s" % path
|
||||
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.writeToFile(
|
||||
@@ -118,30 +142,43 @@ class FTPUtilities:
|
||||
|
||||
## gid , uid ends
|
||||
|
||||
path = path.lstrip("/")
|
||||
# Enhanced path validation and handling
|
||||
if path and path.strip() and path != 'None':
|
||||
# Clean the path
|
||||
path = path.strip().lstrip("/")
|
||||
|
||||
# Additional security checks
|
||||
if path.find("..") > -1 or path.find("~") > -1 or path.startswith("/"):
|
||||
raise BaseException("Invalid path: Path must be relative and not contain '..' or '~' or start with '/'")
|
||||
|
||||
# Check for dangerous characters
|
||||
dangerous_chars = [';', '|', '&', '$', '`', '\'', '"', '<', '>', '*', '?']
|
||||
if any(char in path for char in dangerous_chars):
|
||||
raise BaseException("Invalid path: Path contains dangerous characters")
|
||||
|
||||
# Construct full path
|
||||
full_path = "/home/" + domainName + "/" + path
|
||||
|
||||
# Additional security: ensure path is within domain directory
|
||||
domain_home = "/home/" + domainName
|
||||
if not os.path.abspath(full_path).startswith(os.path.abspath(domain_home)):
|
||||
raise BaseException("Security violation: Path must be within domain directory")
|
||||
|
||||
if path != 'None':
|
||||
path = "/home/" + domainName + "/" + path
|
||||
|
||||
## Security Check
|
||||
|
||||
if path.find("..") > -1:
|
||||
raise BaseException("Specified path must be inside virtual host home!")
|
||||
|
||||
|
||||
result = FTPUtilities.ftpFunctions(path, externalApp)
|
||||
result = FTPUtilities.ftpFunctions(full_path, externalApp)
|
||||
|
||||
if result[0] == 1:
|
||||
pass
|
||||
path = full_path
|
||||
else:
|
||||
raise BaseException(result[1])
|
||||
raise BaseException("Path validation failed: " + result[1])
|
||||
|
||||
else:
|
||||
path = "/home/" + domainName
|
||||
|
||||
# Enhanced symlink handling
|
||||
if os.path.islink(path):
|
||||
print("0, %s file is symlinked." % (path))
|
||||
return 0
|
||||
logging.CyberCPLogFileWriter.writeToFile(
|
||||
"FTP path is symlinked: %s" % path)
|
||||
raise BaseException("Cannot create FTP account: Path is a symbolic link")
|
||||
|
||||
ProcessUtilities.decideDistro()
|
||||
|
||||
@@ -266,6 +303,9 @@ class FTPUtilities:
|
||||
|
||||
ftp.save()
|
||||
|
||||
# Apply quota to filesystem if needed
|
||||
FTPUtilities.applyQuotaToFilesystem(ftp)
|
||||
|
||||
return 1, "FTP quota updated successfully"
|
||||
|
||||
except Users.DoesNotExist:
|
||||
@@ -274,6 +314,107 @@ class FTPUtilities:
|
||||
logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [updateFTPQuota]")
|
||||
return 0, str(msg)
|
||||
|
||||
@staticmethod
|
||||
def applyQuotaToFilesystem(ftp_user):
|
||||
"""
|
||||
Apply quota settings to the filesystem level
|
||||
"""
|
||||
try:
|
||||
import subprocess
|
||||
|
||||
# Get the user's directory
|
||||
user_dir = ftp_user.dir
|
||||
if not user_dir or not os.path.exists(user_dir):
|
||||
return False, "User directory not found"
|
||||
|
||||
# Convert quota from MB to KB for setquota command
|
||||
quota_kb = ftp_user.quotasize * 1024
|
||||
|
||||
# Apply quota using setquota command
|
||||
# Note: This requires quota tools to be installed
|
||||
try:
|
||||
# Set both soft and hard limits to the same value
|
||||
subprocess.run([
|
||||
'setquota', '-u', str(ftp_user.uid),
|
||||
f'{quota_kb}K', f'{quota_kb}K',
|
||||
'0', '0', # inode limits (unlimited)
|
||||
user_dir
|
||||
], check=True, capture_output=True)
|
||||
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"Applied quota {quota_kb}KB to user {ftp_user.user} in {user_dir}")
|
||||
return True, "Quota applied successfully"
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"Failed to apply quota: {e}")
|
||||
return False, f"Failed to apply quota: {e}"
|
||||
except FileNotFoundError:
|
||||
# setquota command not found, quota tools not installed
|
||||
logging.CyberCPLogFileWriter.writeToFile("setquota command not found - quota tools may not be installed")
|
||||
return False, "Quota tools not installed"
|
||||
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"Error applying quota to filesystem: {str(e)}")
|
||||
return False, str(e)
|
||||
|
||||
@staticmethod
|
||||
def getFTPQuotaUsage(ftpUsername):
|
||||
"""
|
||||
Get current quota usage for an FTP user
|
||||
"""
|
||||
try:
|
||||
ftp = Users.objects.get(user=ftpUsername)
|
||||
user_dir = ftp.dir
|
||||
|
||||
if not user_dir or not os.path.exists(user_dir):
|
||||
return 0, "User directory not found"
|
||||
|
||||
# Get directory size in MB
|
||||
import subprocess
|
||||
result = subprocess.run(['du', '-sm', user_dir], capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
usage_mb = int(result.stdout.split()[0])
|
||||
quota_mb = ftp.quotasize
|
||||
usage_percent = (usage_mb / quota_mb * 100) if quota_mb > 0 else 0
|
||||
|
||||
return {
|
||||
'usage_mb': usage_mb,
|
||||
'quota_mb': quota_mb,
|
||||
'usage_percent': round(usage_percent, 2),
|
||||
'remaining_mb': max(0, quota_mb - usage_mb)
|
||||
}
|
||||
else:
|
||||
return 0, "Failed to get directory size"
|
||||
|
||||
except Users.DoesNotExist:
|
||||
return 0, "FTP user not found"
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"Error getting quota usage: {str(e)}")
|
||||
return 0, str(e)
|
||||
|
||||
@staticmethod
|
||||
def migrateExistingFTPUsers():
|
||||
"""
|
||||
Migrate existing FTP users to use the new quota system
|
||||
"""
|
||||
try:
|
||||
migrated_count = 0
|
||||
|
||||
for ftp_user in Users.objects.all():
|
||||
# If custom_quota_enabled is not set, set it to False and use package default
|
||||
if not hasattr(ftp_user, 'custom_quota_enabled') or ftp_user.custom_quota_enabled is None:
|
||||
ftp_user.custom_quota_enabled = False
|
||||
ftp_user.custom_quota_size = 0
|
||||
ftp_user.quotasize = ftp_user.domain.package.diskSpace
|
||||
ftp_user.save()
|
||||
migrated_count += 1
|
||||
|
||||
return 1, f"Migrated {migrated_count} FTP users to new quota system"
|
||||
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"Error migrating FTP users: {str(e)}")
|
||||
return 0, str(e)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
|
||||
@@ -400,11 +400,12 @@ modsecurity_rules_file /usr/local/lsws/conf/modsec/rules.conf
|
||||
def setupOWASPRules():
|
||||
try:
|
||||
pathTOOWASPFolder = os.path.join(virtualHostUtilities.Server_root, "conf/modsec/owasp")
|
||||
pathToOWASFolderNew = '%s/modsec/owasp-modsecurity-crs-3.0-master' % (virtualHostUtilities.vhostConfPath)
|
||||
pathToOWASFolderNew = '%s/modsec/owasp-modsecurity-crs-4.18.0' % (virtualHostUtilities.vhostConfPath)
|
||||
|
||||
command = 'mkdir -p /usr/local/lsws/conf/modsec'
|
||||
result = subprocess.call(shlex.split(command))
|
||||
if result != 0:
|
||||
logging.CyberCPLogFileWriter.writeToFile("Failed to create modsec directory: " + str(result) + " [setupOWASPRules]")
|
||||
return 0
|
||||
|
||||
if os.path.exists(pathToOWASFolderNew):
|
||||
@@ -416,22 +417,32 @@ modsecurity_rules_file /usr/local/lsws/conf/modsec/rules.conf
|
||||
if os.path.exists('owasp.tar.gz'):
|
||||
os.remove('owasp.tar.gz')
|
||||
|
||||
command = "wget https://github.com/coreruleset/coreruleset/archive/v3.3.2/master.zip -O /usr/local/lsws/conf/modsec/owasp.zip"
|
||||
# Clean up any existing zip file
|
||||
if os.path.exists('/usr/local/lsws/conf/modsec/owasp.zip'):
|
||||
os.remove('/usr/local/lsws/conf/modsec/owasp.zip')
|
||||
|
||||
command = "wget https://github.com/coreruleset/coreruleset/archive/refs/tags/v4.18.0.zip -O /usr/local/lsws/conf/modsec/owasp.zip"
|
||||
logging.CyberCPLogFileWriter.writeToFile("Downloading OWASP rules: " + command + " [setupOWASPRules]")
|
||||
result = subprocess.call(shlex.split(command))
|
||||
|
||||
if result != 0:
|
||||
logging.CyberCPLogFileWriter.writeToFile("Failed to download OWASP rules: " + str(result) + " [setupOWASPRules]")
|
||||
return 0
|
||||
|
||||
command = "unzip -o /usr/local/lsws/conf/modsec/owasp.zip -d /usr/local/lsws/conf/modsec/"
|
||||
logging.CyberCPLogFileWriter.writeToFile("Extracting OWASP rules: " + command + " [setupOWASPRules]")
|
||||
result = subprocess.call(shlex.split(command))
|
||||
|
||||
if result != 0:
|
||||
logging.CyberCPLogFileWriter.writeToFile("Failed to extract OWASP rules: " + str(result) + " [setupOWASPRules]")
|
||||
return 0
|
||||
|
||||
command = 'mv /usr/local/lsws/conf/modsec/coreruleset-3.3.2 /usr/local/lsws/conf/modsec/owasp-modsecurity-crs-3.0-master'
|
||||
command = 'mv /usr/local/lsws/conf/modsec/coreruleset-4.18.0 /usr/local/lsws/conf/modsec/owasp-modsecurity-crs-4.18.0'
|
||||
logging.CyberCPLogFileWriter.writeToFile("Moving OWASP rules: " + command + " [setupOWASPRules]")
|
||||
result = subprocess.call(shlex.split(command))
|
||||
|
||||
if result != 0:
|
||||
logging.CyberCPLogFileWriter.writeToFile("Failed to move OWASP rules: " + str(result) + " [setupOWASPRules]")
|
||||
return 0
|
||||
|
||||
command = 'mv %s/crs-setup.conf.example %s/crs-setup.conf' % (pathToOWASFolderNew, pathToOWASFolderNew)
|
||||
@@ -453,32 +464,8 @@ modsecurity_rules_file /usr/local/lsws/conf/modsec/rules.conf
|
||||
if result != 0:
|
||||
return 0
|
||||
|
||||
content = """include {pathToOWASFolderNew}/crs-setup.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-901-INITIALIZATION.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-905-COMMON-EXCEPTIONS.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-910-IP-REPUTATION.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-911-METHOD-ENFORCEMENT.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-912-DOS-PROTECTION.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-913-SCANNER-DETECTION.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-921-PROTOCOL-ATTACK.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
|
||||
include {pathToOWASFolderNew}/rules/REQUEST-949-BLOCKING-EVALUATION.conf
|
||||
include {pathToOWASFolderNew}/rules/RESPONSE-950-DATA-LEAKAGES.conf
|
||||
include {pathToOWASFolderNew}/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
|
||||
include {pathToOWASFolderNew}/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
|
||||
include {pathToOWASFolderNew}/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
|
||||
include {pathToOWASFolderNew}/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
|
||||
include {pathToOWASFolderNew}/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
|
||||
include {pathToOWASFolderNew}/rules/RESPONSE-980-CORRELATION.conf
|
||||
include {pathToOWASFolderNew}/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
|
||||
# CRS v4.0.0 uses a different structure - it has a main crs.conf file
|
||||
content = """include {pathToOWASFolderNew}/crs.conf
|
||||
"""
|
||||
writeToFile = open('%s/owasp-master.conf' % (pathToOWASFolderNew), 'w')
|
||||
writeToFile.write(content.replace('{pathToOWASFolderNew}', pathToOWASFolderNew))
|
||||
@@ -501,7 +488,7 @@ include {pathToOWASFolderNew}/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
|
||||
|
||||
if ProcessUtilities.decideServer() == ProcessUtilities.OLS:
|
||||
owaspRulesConf = """
|
||||
modsecurity_rules_file /usr/local/lsws/conf/modsec/owasp-modsecurity-crs-3.0-master/owasp-master.conf
|
||||
modsecurity_rules_file /usr/local/lsws/conf/modsec/owasp-modsecurity-crs-4.18.0/owasp-master.conf
|
||||
"""
|
||||
|
||||
confFile = os.path.join(virtualHostUtilities.Server_root, "conf/httpd_config.conf")
|
||||
@@ -519,6 +506,14 @@ modsecurity_rules_file /usr/local/lsws/conf/modsec/owasp-modsecurity-crs-3.0-mas
|
||||
conf.writelines(items)
|
||||
|
||||
conf.close()
|
||||
|
||||
# Verify the installation
|
||||
owaspPath = os.path.join(virtualHostUtilities.Server_root, "conf/modsec/owasp-modsecurity-crs-4.18.0")
|
||||
if not os.path.exists(owaspPath) or not os.path.exists(os.path.join(owaspPath, "owasp-master.conf")):
|
||||
logging.CyberCPLogFileWriter.writeToFile("OWASP installation verification failed - files not found [installOWASP]")
|
||||
print("0, OWASP installation verification failed")
|
||||
return
|
||||
|
||||
else:
|
||||
confFile = os.path.join('/usr/local/lsws/conf/modsec.conf')
|
||||
confData = open(confFile).readlines()
|
||||
@@ -528,7 +523,7 @@ modsecurity_rules_file /usr/local/lsws/conf/modsec/owasp-modsecurity-crs-3.0-mas
|
||||
for items in confData:
|
||||
if items.find('/conf/comodo_litespeed/') > -1:
|
||||
conf.writelines(items)
|
||||
conf.write('Include /usr/local/lsws/conf/modsec/owasp-modsecurity-crs-3.0-master/*.conf\n')
|
||||
conf.write('Include /usr/local/lsws/conf/modsec/owasp-modsecurity-crs-4.18.0/*.conf\n')
|
||||
continue
|
||||
else:
|
||||
conf.writelines(items)
|
||||
@@ -536,7 +531,8 @@ modsecurity_rules_file /usr/local/lsws/conf/modsec/owasp-modsecurity-crs-3.0-mas
|
||||
conf.close()
|
||||
|
||||
installUtilities.reStartLiteSpeed()
|
||||
|
||||
|
||||
logging.CyberCPLogFileWriter.writeToFile("OWASP ModSecurity rules installed successfully [installOWASP]")
|
||||
print("1,None")
|
||||
|
||||
except BaseException as msg:
|
||||
|
||||
@@ -150,10 +150,10 @@ class remoteBackup:
|
||||
return [0, msg]
|
||||
|
||||
@staticmethod
|
||||
def postRemoteTransfer(ipAddress, ownIP ,password, sshkey):
|
||||
def postRemoteTransfer(ipAddress, ownIP ,password, sshkey, cyberPanelPort=8090):
|
||||
try:
|
||||
finalData = json.dumps({'username': "admin", "ipAddress": ownIP, "password": password})
|
||||
url = "https://" + ipAddress + ":8090/api/remoteTransfer"
|
||||
url = "https://" + ipAddress + ":" + str(cyberPanelPort) + "/api/remoteTransfer"
|
||||
r = requests.post(url, data=finalData, verify=False)
|
||||
data = json.loads(r.text)
|
||||
|
||||
|
||||
@@ -78,6 +78,9 @@ class Renew:
|
||||
try:
|
||||
logging.writeToFile('Restarting mail services for them to see new SSL.', 0)
|
||||
|
||||
# Update mail SSL configuration for all domains
|
||||
self._update_all_mail_ssl_configs()
|
||||
|
||||
commands = [
|
||||
'postmap -F hash:/etc/postfix/vmail_ssl.map',
|
||||
'systemctl restart postfix',
|
||||
@@ -93,6 +96,22 @@ class Renew:
|
||||
except Exception as e:
|
||||
logging.writeToFile(f'Error restarting services: {str(e)}', 1)
|
||||
|
||||
def _update_all_mail_ssl_configs(self) -> None:
|
||||
"""Update mail SSL configuration for all domains after renewal"""
|
||||
try:
|
||||
logging.writeToFile('Updating mail SSL configurations for all domains.', 0)
|
||||
|
||||
# Update mail SSL config for all websites
|
||||
for website in Websites.objects.filter(state=1):
|
||||
virtualHostUtilities.updateMailSSLConfig(website.domain)
|
||||
|
||||
# Update mail SSL config for all child domains
|
||||
for child in ChildDomains.objects.all():
|
||||
virtualHostUtilities.updateMailSSLConfig(child.domain)
|
||||
|
||||
except Exception as e:
|
||||
logging.writeToFile(f'Error updating mail SSL configs: {str(e)}', 1)
|
||||
|
||||
def SSLObtainer(self):
|
||||
try:
|
||||
logging.writeToFile('Running SSL Renew Utility')
|
||||
|
||||
@@ -9,6 +9,7 @@ import re
|
||||
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
from plogical.errorSanitizer import ErrorSanitizer
|
||||
import shlex
|
||||
import subprocess
|
||||
import shutil
|
||||
@@ -567,8 +568,9 @@ class Upgrade:
|
||||
writeToFile.writelines(varTmp)
|
||||
writeToFile.close()
|
||||
|
||||
except BaseException as msg:
|
||||
Upgrade.stdOut(str(msg) + " [mountTemp]", 0)
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'mountTemp')
|
||||
Upgrade.stdOut("Failed to mount temporary filesystem [mountTemp]", 0)
|
||||
|
||||
@staticmethod
|
||||
def dockerUsers():
|
||||
@@ -738,8 +740,9 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout';
|
||||
|
||||
os.chdir(cwd)
|
||||
|
||||
except BaseException as msg:
|
||||
Upgrade.stdOut(str(msg) + " [download_install_phpmyadmin]", 0)
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'download_install_phpmyadmin')
|
||||
Upgrade.stdOut("Failed to download and install phpMyAdmin [download_install_phpmyadmin]", 0)
|
||||
|
||||
@staticmethod
|
||||
def setupComposer():
|
||||
@@ -1028,8 +1031,9 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout';
|
||||
|
||||
Upgrade.stdOut("SnappyMail installation completed.", 0)
|
||||
|
||||
except BaseException as msg:
|
||||
Upgrade.stdOut(str(msg) + " [downoad_and_install_raindloop]", 0)
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'downoad_and_install_raindloop')
|
||||
Upgrade.stdOut("Failed to download and install Rainloop [downoad_and_install_raindloop]", 0)
|
||||
|
||||
return 1
|
||||
|
||||
@@ -1049,8 +1053,9 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout';
|
||||
pass
|
||||
|
||||
return (version_number + "." + version_build + ".tar.gz")
|
||||
except BaseException as msg:
|
||||
Upgrade.stdOut(str(msg) + ' [downloadLink]')
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'downloadLink')
|
||||
Upgrade.stdOut("Failed to download required files [downloadLink]")
|
||||
os._exit(0)
|
||||
|
||||
@staticmethod
|
||||
@@ -1063,8 +1068,9 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout';
|
||||
command = "chmod +x /usr/local/CyberCP/cli/cyberPanel.py"
|
||||
Upgrade.executioner(command, 'CLI Permissions', 0)
|
||||
|
||||
except OSError as msg:
|
||||
Upgrade.stdOut(str(msg) + " [setupCLI]")
|
||||
except OSError as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'setupCLI')
|
||||
Upgrade.stdOut("Failed to setup CLI [setupCLI]")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
@@ -1136,8 +1142,9 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout';
|
||||
cursor = conn.cursor()
|
||||
return conn, cursor
|
||||
|
||||
except BaseException as msg:
|
||||
Upgrade.stdOut(str(msg))
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'database_connection')
|
||||
Upgrade.stdOut("Failed to establish database connection")
|
||||
return 0, 0
|
||||
|
||||
@staticmethod
|
||||
@@ -1381,8 +1388,8 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout';
|
||||
|
||||
try:
|
||||
cursor.execute("UPDATE loginSystem_acl SET config = '%s' where name = 'admin'" % (Upgrade.AdminACL))
|
||||
except BaseException as msg:
|
||||
print(str(msg))
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'applyLoginSystemMigrations')
|
||||
try:
|
||||
import sleep
|
||||
except:
|
||||
@@ -2197,6 +2204,22 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
|
||||
except:
|
||||
pass
|
||||
|
||||
# Add new fields for network configuration and extra options
|
||||
try:
|
||||
cursor.execute('ALTER TABLE dockerManager_containers ADD network VARCHAR(100) DEFAULT "bridge"')
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
cursor.execute('ALTER TABLE dockerManager_containers ADD network_mode VARCHAR(50) DEFAULT "bridge"')
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
cursor.execute('ALTER TABLE dockerManager_containers ADD extra_options LONGTEXT DEFAULT "{}"')
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
connection.close()
|
||||
except:
|
||||
@@ -2983,8 +3006,9 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
|
||||
|
||||
return 1, None
|
||||
|
||||
except BaseException as msg:
|
||||
return 0, str(msg)
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'installLSCPD')
|
||||
return 0, "Failed to install LSCPD"
|
||||
|
||||
@staticmethod
|
||||
def installLSCPD(branch):
|
||||
@@ -3074,8 +3098,9 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
|
||||
|
||||
Upgrade.stdOut("LSCPD successfully installed!")
|
||||
|
||||
except BaseException as msg:
|
||||
Upgrade.stdOut(str(msg) + " [installLSCPD]")
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'installLSCPD')
|
||||
Upgrade.stdOut("Failed to install LSCPD [installLSCPD]")
|
||||
|
||||
### disable dkim signing in rspamd in ref to https://github.com/usmannasir/cyberpanel/issues/1176
|
||||
@staticmethod
|
||||
@@ -3363,8 +3388,9 @@ echo $oConfig->Save() ? 'Done' : 'Error';
|
||||
|
||||
Upgrade.stdOut("Permissions updated.")
|
||||
|
||||
except BaseException as msg:
|
||||
Upgrade.stdOut(str(msg) + " [fixPermissions]")
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'fixPermissions')
|
||||
Upgrade.stdOut("Failed to fix permissions [fixPermissions]")
|
||||
|
||||
@staticmethod
|
||||
def AutoUpgradeAcme():
|
||||
@@ -3807,8 +3833,9 @@ echo $oConfig->Save() ? 'Done' : 'Error';
|
||||
|
||||
Upgrade.stdOut("Dovecot upgraded.")
|
||||
|
||||
except BaseException as msg:
|
||||
Upgrade.stdOut(str(msg) + " [upgradeDovecot]")
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'upgradeDovecot')
|
||||
Upgrade.stdOut("Failed to upgrade Dovecot [upgradeDovecot]")
|
||||
|
||||
@staticmethod
|
||||
def installRestic():
|
||||
@@ -4052,317 +4079,181 @@ vmail
|
||||
|
||||
@staticmethod
|
||||
def CreateMissingPoolsforFPM():
|
||||
##### apache configs
|
||||
"""
|
||||
Create missing PHP-FPM pool configurations for all PHP versions.
|
||||
This function ensures all PHP versions have proper pool configurations
|
||||
to prevent ImunifyAV/Imunify360 installation failures.
|
||||
"""
|
||||
try:
|
||||
# Detect OS and set paths
|
||||
CentOSPath = '/etc/redhat-release'
|
||||
|
||||
if os.path.exists(CentOSPath):
|
||||
# CentOS/RHEL/CloudLinux paths
|
||||
serverRootPath = '/etc/httpd'
|
||||
configBasePath = '/etc/httpd/conf.d/'
|
||||
sockPath = '/var/run/php-fpm/'
|
||||
runAsUser = 'apache'
|
||||
group = 'nobody'
|
||||
|
||||
# Define PHP pool paths for CentOS
|
||||
php_paths = {
|
||||
'5.4': '/opt/remi/php54/root/etc/php-fpm.d/',
|
||||
'5.5': '/opt/remi/php55/root/etc/php-fpm.d/',
|
||||
'5.6': '/etc/opt/remi/php56/php-fpm.d/',
|
||||
'7.0': '/etc/opt/remi/php70/php-fpm.d/',
|
||||
'7.1': '/etc/opt/remi/php71/php-fpm.d/',
|
||||
'7.2': '/etc/opt/remi/php72/php-fpm.d/',
|
||||
'7.3': '/etc/opt/remi/php73/php-fpm.d/',
|
||||
'7.4': '/etc/opt/remi/php74/php-fpm.d/',
|
||||
'8.0': '/etc/opt/remi/php80/php-fpm.d/',
|
||||
'8.1': '/etc/opt/remi/php81/php-fpm.d/',
|
||||
'8.2': '/etc/opt/remi/php82/php-fpm.d/',
|
||||
'8.3': '/etc/opt/remi/php83/php-fpm.d/',
|
||||
'8.4': '/etc/opt/remi/php84/php-fpm.d/',
|
||||
'8.5': '/etc/opt/remi/php85/php-fpm.d/'
|
||||
}
|
||||
else:
|
||||
# Ubuntu/Debian paths
|
||||
serverRootPath = '/etc/apache2'
|
||||
configBasePath = '/etc/apache2/sites-enabled/'
|
||||
sockPath = '/var/run/php/'
|
||||
runAsUser = 'www-data'
|
||||
group = 'nogroup'
|
||||
|
||||
# Define PHP pool paths for Ubuntu
|
||||
php_paths = {
|
||||
'5.4': '/etc/php/5.4/fpm/pool.d/',
|
||||
'5.5': '/etc/php/5.5/fpm/pool.d/',
|
||||
'5.6': '/etc/php/5.6/fpm/pool.d/',
|
||||
'7.0': '/etc/php/7.0/fpm/pool.d/',
|
||||
'7.1': '/etc/php/7.1/fpm/pool.d/',
|
||||
'7.2': '/etc/php/7.2/fpm/pool.d/',
|
||||
'7.3': '/etc/php/7.3/fpm/pool.d/',
|
||||
'7.4': '/etc/php/7.4/fpm/pool.d/',
|
||||
'8.0': '/etc/php/8.0/fpm/pool.d/',
|
||||
'8.1': '/etc/php/8.1/fpm/pool.d/',
|
||||
'8.2': '/etc/php/8.2/fpm/pool.d/',
|
||||
'8.3': '/etc/php/8.3/fpm/pool.d/',
|
||||
'8.4': '/etc/php/8.4/fpm/pool.d/',
|
||||
'8.5': '/etc/php/8.5/fpm/pool.d/'
|
||||
}
|
||||
|
||||
CentOSPath = '/etc/redhat-release'
|
||||
# Check if server root exists
|
||||
if not os.path.exists(serverRootPath):
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Server root path not found: {serverRootPath}')
|
||||
return 1
|
||||
|
||||
if os.path.exists(CentOSPath):
|
||||
# Create pool configurations for all PHP versions
|
||||
for version, pool_path in php_paths.items():
|
||||
if os.path.exists(pool_path):
|
||||
www_conf = os.path.join(pool_path, 'www.conf')
|
||||
|
||||
# Skip if www.conf already exists
|
||||
if os.path.exists(www_conf):
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'PHP {version} pool config already exists: {www_conf}')
|
||||
continue
|
||||
|
||||
# Create the pool configuration
|
||||
pool_name = f'php{version.replace(".", "")}default'
|
||||
sock_name = f'php{version}-fpm.sock'
|
||||
|
||||
content = f'''[{pool_name}]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen = {sockPath}{sock_name}
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {group}
|
||||
listen.mode = 0660
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
pm.max_requests = 1000
|
||||
pm.status_path = /status
|
||||
ping.path = /ping
|
||||
ping.response = pong
|
||||
request_terminate_timeout = 300
|
||||
request_slowlog_timeout = 10
|
||||
slowlog = /var/log/php{version}-fpm-slow.log
|
||||
'''
|
||||
|
||||
try:
|
||||
# Write the configuration file
|
||||
with open(www_conf, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
# Set proper permissions
|
||||
os.chown(www_conf, 0, 0) # root:root
|
||||
os.chmod(www_conf, 0o644)
|
||||
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Created PHP {version} pool config: {www_conf}')
|
||||
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Error creating PHP {version} pool config: {str(e)}')
|
||||
else:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'PHP {version} pool directory not found: {pool_path}')
|
||||
|
||||
serverRootPath = '/etc/httpd'
|
||||
configBasePath = '/etc/httpd/conf.d/'
|
||||
php54Path = '/opt/remi/php54/root/etc/php-fpm.d/'
|
||||
php55Path = '/opt/remi/php55/root/etc/php-fpm.d/'
|
||||
php56Path = '/etc/opt/remi/php56/php-fpm.d/'
|
||||
php70Path = '/etc/opt/remi/php70/php-fpm.d/'
|
||||
php71Path = '/etc/opt/remi/php71/php-fpm.d/'
|
||||
php72Path = '/etc/opt/remi/php72/php-fpm.d/'
|
||||
php73Path = '/etc/opt/remi/php73/php-fpm.d/'
|
||||
|
||||
php74Path = '/etc/opt/remi/php74/php-fpm.d/'
|
||||
|
||||
php80Path = '/etc/opt/remi/php80/php-fpm.d/'
|
||||
php81Path = '/etc/opt/remi/php81/php-fpm.d/'
|
||||
php82Path = '/etc/opt/remi/php82/php-fpm.d/'
|
||||
|
||||
php83Path = '/etc/opt/remi/php83/php-fpm.d/'
|
||||
php84Path = '/etc/opt/remi/php84/php-fpm.d/'
|
||||
php85Path = '/etc/opt/remi/php85/php-fpm.d/'
|
||||
|
||||
serviceName = 'httpd'
|
||||
sockPath = '/var/run/php-fpm/'
|
||||
runAsUser = 'apache'
|
||||
else:
|
||||
serverRootPath = '/etc/apache2'
|
||||
configBasePath = '/etc/apache2/sites-enabled/'
|
||||
|
||||
php54Path = '/etc/php/5.4/fpm/pool.d/'
|
||||
php55Path = '/etc/php/5.5/fpm/pool.d/'
|
||||
php56Path = '/etc/php/5.6/fpm/pool.d/'
|
||||
php70Path = '/etc/php/7.0/fpm/pool.d/'
|
||||
php71Path = '/etc/php/7.1/fpm/pool.d/'
|
||||
php72Path = '/etc/php/7.2/fpm/pool.d/'
|
||||
php73Path = '/etc/php/7.3/fpm/pool.d/'
|
||||
|
||||
php74Path = '/etc/php/7.4/fpm/pool.d/'
|
||||
php80Path = '/etc/php/8.0/fpm/pool.d/'
|
||||
php81Path = '/etc/php/8.1/fpm/pool.d/'
|
||||
php82Path = '/etc/php/8.2/fpm/pool.d/'
|
||||
php83Path = '/etc/php/8.3/fpm/pool.d/'
|
||||
php84Path = '/etc/php/8.4/fpm/pool.d/'
|
||||
php85Path = '/etc/php/8.5/fpm/pool.d/'
|
||||
|
||||
serviceName = 'apache2'
|
||||
sockPath = '/var/run/php/'
|
||||
runAsUser = 'www-data'
|
||||
|
||||
#####
|
||||
|
||||
if not os.path.exists(serverRootPath):
|
||||
# Restart PHP-FPM services to apply configurations
|
||||
Upgrade.restartPHPFPMServices()
|
||||
|
||||
return 0
|
||||
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Error in CreateMissingPoolsforFPM: {str(e)}')
|
||||
return 1
|
||||
|
||||
if os.path.exists(php54Path):
|
||||
content = f"""
|
||||
[php54default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php5.4-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
"""
|
||||
WriteToFile = open(f'{php54Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php55Path):
|
||||
content = f'''
|
||||
[php55default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php5.5-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
'''
|
||||
WriteToFile = open(f'{php55Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php56Path):
|
||||
content = f'''
|
||||
[php56default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php5.6-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
'''
|
||||
WriteToFile = open(f'{php56Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php70Path):
|
||||
content = f'''
|
||||
[php70default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php7.0-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
'''
|
||||
WriteToFile = open(f'{php70Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php71Path):
|
||||
content = f'''
|
||||
[php71default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php7.1-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
'''
|
||||
WriteToFile = open(f'{php71Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php72Path):
|
||||
content = f'''
|
||||
[php72default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php7.2-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
'''
|
||||
WriteToFile = open(f'{php72Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php73Path):
|
||||
content = f'''
|
||||
[php73default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php7.3-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
'''
|
||||
WriteToFile = open(f'{php73Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php74Path):
|
||||
content = f'''
|
||||
[php74default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php7.4-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
'''
|
||||
WriteToFile = open(f'{php74Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php80Path):
|
||||
content = f'''
|
||||
[php80default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php8.0-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
|
||||
'''
|
||||
WriteToFile = open(f'{php80Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php81Path):
|
||||
content = f'''
|
||||
[php81default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php8.1-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
|
||||
'''
|
||||
WriteToFile = open(f'{php81Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
if os.path.exists(php82Path):
|
||||
content = f'''
|
||||
[php82default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php8.2-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
@staticmethod
|
||||
def restartPHPFPMServices():
|
||||
"""
|
||||
Restart all PHP-FPM services to apply new pool configurations.
|
||||
This ensures that ImunifyAV/Imunify360 installation will work properly.
|
||||
"""
|
||||
try:
|
||||
# Define all possible PHP versions
|
||||
php_versions = ['5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
|
||||
|
||||
'''
|
||||
WriteToFile = open(f'{php82Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php83Path):
|
||||
content = f'''
|
||||
[php83default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php8.3-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
'''
|
||||
WriteToFile = open(f'{php83Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php84Path):
|
||||
content = f'''
|
||||
[php84default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php8.4-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
'''
|
||||
WriteToFile = open(f'{php84Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
|
||||
if os.path.exists(php85Path):
|
||||
content = f'''
|
||||
[php85default]
|
||||
user = {runAsUser}
|
||||
group = {runAsUser}
|
||||
listen ={sockPath}php8.5-fpm.sock
|
||||
listen.owner = {runAsUser}
|
||||
listen.group = {runAsUser}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
'''
|
||||
WriteToFile = open(f'{php85Path}www.conf', 'w')
|
||||
WriteToFile.write(content)
|
||||
WriteToFile.close()
|
||||
restarted_count = 0
|
||||
total_count = 0
|
||||
|
||||
for version in php_versions:
|
||||
service_name = f'php{version}-fpm'
|
||||
|
||||
# Check if service exists
|
||||
try:
|
||||
result = subprocess.run(['systemctl', 'list-unit-files', service_name],
|
||||
capture_output=True, text=True, timeout=10)
|
||||
if result.returncode == 0 and service_name in result.stdout:
|
||||
total_count += 1
|
||||
|
||||
# Restart the service
|
||||
restart_result = subprocess.run(['systemctl', 'restart', service_name],
|
||||
capture_output=True, text=True, timeout=30)
|
||||
|
||||
if restart_result.returncode == 0:
|
||||
# Check if service is actually running
|
||||
status_result = subprocess.run(['systemctl', 'is-active', service_name],
|
||||
capture_output=True, text=True, timeout=10)
|
||||
if status_result.returncode == 0 and 'active' in status_result.stdout:
|
||||
restarted_count += 1
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Successfully restarted {service_name}')
|
||||
else:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Warning: {service_name} restarted but not active')
|
||||
else:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Failed to restart {service_name}: {restart_result.stderr}')
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Timeout restarting {service_name}')
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Error restarting {service_name}: {str(e)}')
|
||||
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'PHP-FPM restart summary: {restarted_count}/{total_count} services restarted successfully')
|
||||
return restarted_count, total_count
|
||||
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Error in restartPHPFPMServices: {str(e)}')
|
||||
return 0, 0
|
||||
|
||||
@staticmethod
|
||||
def setupPHPSymlink():
|
||||
@@ -4406,8 +4297,9 @@ pm.max_spare_servers = 3
|
||||
|
||||
Upgrade.stdOut(f"PHP symlink updated to PHP {selected_php} successfully.")
|
||||
|
||||
except BaseException as msg:
|
||||
Upgrade.stdOut('[ERROR] ' + str(msg) + " [setupPHPSymlink]")
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'setupPHPSymlink')
|
||||
Upgrade.stdOut('[ERROR] Failed to setup PHP symlink [setupPHPSymlink]')
|
||||
return 0
|
||||
|
||||
return 1
|
||||
@@ -5219,8 +5111,9 @@ extprocessor proxyApacheBackendSSL {
|
||||
|
||||
return 1
|
||||
|
||||
except BaseException as msg:
|
||||
print("[ERROR] installQuota. " + str(msg))
|
||||
except Exception as e:
|
||||
ErrorSanitizer.log_error_securely(e, 'installQuota')
|
||||
print("[ERROR] installQuota. Failed to install quota")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -196,9 +196,15 @@ class vhost:
|
||||
command = 'mkdir -p /usr/local/lsws/Example/html/.well-known/acme-challenge'
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
path = "/home/" + virtualHostName
|
||||
pathHTML = "/home/" + virtualHostName + "/public_html"
|
||||
pathLogs = "/home/" + virtualHostName + "/logs"
|
||||
# Get user's home directory dynamically
|
||||
from userManagment.homeDirectoryUtils import HomeDirectoryUtils
|
||||
home_path = HomeDirectoryUtils.getUserHomeDirectory(virtualHostUser)
|
||||
if not home_path:
|
||||
home_path = "/home" # Fallback to default
|
||||
|
||||
path = os.path.join(home_path, virtualHostName)
|
||||
pathHTML = os.path.join(home_path, virtualHostName, "public_html")
|
||||
pathLogs = os.path.join(home_path, virtualHostName, "logs")
|
||||
confPath = vhost.Server_root + "/conf/vhosts/"+virtualHostName
|
||||
completePathToConfigFile = confPath +"/vhost.conf"
|
||||
|
||||
|
||||
@@ -796,6 +796,12 @@ local_name %s {
|
||||
print("0," + parsed_error)
|
||||
return 0, parsed_error
|
||||
|
||||
# Update vhost SSL configuration with new certificate paths
|
||||
virtualHostUtilities.updateVhostSSLConfig(virtualHost)
|
||||
|
||||
# Update mail SSL configuration for this domain
|
||||
virtualHostUtilities.updateMailSSLConfig(virtualHost)
|
||||
|
||||
installUtilities.installUtilities.reStartLiteSpeed()
|
||||
|
||||
command = 'systemctl restart postfix'
|
||||
@@ -891,8 +897,50 @@ local_name %s {
|
||||
print("0, %s file is symlinked." % (fileName))
|
||||
return 0
|
||||
|
||||
numberOfTotalLines = int(
|
||||
ProcessUtilities.outputExecutioner('wc -l %s' % (fileName), externalApp).split(" ")[0])
|
||||
# Improved wc -l parsing with better error handling
|
||||
wc_output = ProcessUtilities.outputExecutioner('wc -l %s' % (fileName), externalApp)
|
||||
|
||||
# Handle different wc output formats and potential errors
|
||||
if wc_output and wc_output.strip():
|
||||
# Split by whitespace and take the first part that looks like a number
|
||||
wc_parts = wc_output.strip().split()
|
||||
numberOfTotalLines = 0
|
||||
|
||||
for part in wc_parts:
|
||||
try:
|
||||
numberOfTotalLines = int(part)
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
# If no valid number found, try to extract from common wc error formats
|
||||
if numberOfTotalLines == 0:
|
||||
# Handle cases like "wc: filename: No such file or directory"
|
||||
if "No such file or directory" in wc_output:
|
||||
print("1,None")
|
||||
return "1,None"
|
||||
# Handle cases where wc returns just "wc:" or similar
|
||||
if "wc:" in wc_output:
|
||||
# Try to get line count using alternative method
|
||||
try:
|
||||
alt_output = ProcessUtilities.outputExecutioner('cat %s | wc -l' % (fileName), externalApp)
|
||||
if alt_output and alt_output.strip():
|
||||
alt_parts = alt_output.strip().split()
|
||||
for part in alt_parts:
|
||||
try:
|
||||
numberOfTotalLines = int(part)
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
|
||||
if numberOfTotalLines == 0:
|
||||
print("1,None")
|
||||
return "1,None"
|
||||
else:
|
||||
print("1,None")
|
||||
return "1,None"
|
||||
|
||||
if numberOfTotalLines < 25:
|
||||
data = ProcessUtilities.outputExecutioner('cat %s' % (fileName), externalApp)
|
||||
@@ -1079,6 +1127,84 @@ local_name %s {
|
||||
print("0," + str(msg))
|
||||
return 0, str(msg)
|
||||
|
||||
@staticmethod
|
||||
def updateVhostSSLConfig(virtualHost):
|
||||
"""Update vhost SSL configuration with new certificate paths"""
|
||||
try:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"Updating vhost SSL configuration for {virtualHost}")
|
||||
|
||||
# Update vhost configuration file
|
||||
vhostConfPath = f'/usr/local/lsws/conf/vhosts/{virtualHost}/vhost.conf'
|
||||
if os.path.exists(vhostConfPath):
|
||||
with open(vhostConfPath, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Update SSL certificate paths in vhost configuration
|
||||
new_ssl_config = f"""vhssl {{
|
||||
keyFile /etc/letsencrypt/live/{virtualHost}/privkey.pem
|
||||
certFile /etc/letsencrypt/live/{virtualHost}/fullchain.pem
|
||||
certChain 1
|
||||
sslProtocol 24
|
||||
enableECDHE 1
|
||||
renegProtection 1
|
||||
sslSessionCache 1
|
||||
enableSpdy 15
|
||||
enableStapling 1
|
||||
ocspRespMaxAge 86400
|
||||
}}"""
|
||||
|
||||
# Replace existing vhssl block
|
||||
import re
|
||||
pattern = r'vhssl\s*\{[^}]*\}'
|
||||
if re.search(pattern, content, re.DOTALL):
|
||||
content = re.sub(pattern, new_ssl_config, content, flags=re.DOTALL)
|
||||
else:
|
||||
# Add vhssl block if it doesn't exist
|
||||
content += f"\n{new_ssl_config}\n"
|
||||
|
||||
with open(vhostConfPath, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"Updated vhost SSL configuration for {virtualHost}")
|
||||
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"Error updating vhost SSL config for {virtualHost}: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
def updateMailSSLConfig(virtualHost):
|
||||
"""Update mail SSL configuration with new certificate paths"""
|
||||
try:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"Updating mail SSL configuration for {virtualHost}")
|
||||
|
||||
# Update vmail_ssl.map file
|
||||
postfixMapFile = '/etc/postfix/vmail_ssl.map'
|
||||
if os.path.exists(postfixMapFile):
|
||||
with open(postfixMapFile, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Remove old entries for this domain
|
||||
lines = content.split('\n')
|
||||
new_lines = []
|
||||
for line in lines:
|
||||
if not line.startswith(f'{virtualHost} ') and not line.startswith(f'mail.{virtualHost} '):
|
||||
new_lines.append(line)
|
||||
|
||||
# Add new entries
|
||||
new_lines.append(f'{virtualHost} /etc/letsencrypt/live/{virtualHost}/privkey.pem /etc/letsencrypt/live/{virtualHost}/fullchain.pem')
|
||||
new_lines.append(f'mail.{virtualHost} /etc/letsencrypt/live/{virtualHost}/privkey.pem /etc/letsencrypt/live/{virtualHost}/fullchain.pem')
|
||||
|
||||
with open(postfixMapFile, 'w') as f:
|
||||
f.write('\n'.join(new_lines))
|
||||
|
||||
# Update postfix map database
|
||||
command = 'postmap -F hash:/etc/postfix/vmail_ssl.map'
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"Updated mail SSL configuration for {virtualHost}")
|
||||
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"Error updating mail SSL config for {virtualHost}: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
def issueSSLForMailServer(virtualHost, path):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user