bug fix: csf issue

This commit is contained in:
usmannasir
2025-07-05 17:03:54 +05:00
parent b66fd7ce0f
commit 26d3968045
3 changed files with 192 additions and 3 deletions

View File

@@ -127,8 +127,38 @@ class secMiddleware:
logging.writeToFile(f'Value being scanned {str(value)}') logging.writeToFile(f'Value being scanned {str(value)}')
# Skip validation for ports key to allow port ranges with colons # Skip validation for ports key to allow port ranges with colons
if key == 'ports': # but only for CSF modifyPorts endpoint
if key == 'ports' and pathActual == '/firewall/modifyPorts':
# Validate that ports only contain numbers, commas, and colons
if type(value) == str:
import re
# Allow only: digits, commas, colons, and whitespace
if re.match(r'^[\d,:,\s]+$', value):
continue continue
else:
logging.writeToFile(f"Invalid port format in CSF configuration: {value}")
final_dic = {
'error_message': "Invalid port format. Only numbers, commas, and colons are allowed for port ranges.",
"errorMessage": "Invalid port format. Only numbers, commas, and colons are allowed for port ranges."}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
continue
elif key == 'ports':
# For other endpoints, ports key continues to skip validation
continue
# Allow protocol parameter for CSF modifyPorts endpoint
if key == 'protocol' and pathActual == '/firewall/modifyPorts':
# Validate protocol values
if value in ['TCP_IN', 'TCP_OUT', 'UDP_IN', 'UDP_OUT']:
continue
else:
logging.writeToFile(f"Invalid protocol in CSF configuration: {value}")
final_dic = {
'error_message': "Invalid protocol. Only TCP_IN, TCP_OUT, UDP_IN, UDP_OUT are allowed.",
"errorMessage": "Invalid protocol. Only TCP_IN, TCP_OUT, UDP_IN, UDP_OUT are allowed."}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
if type(value) == str or type(value) == bytes: if type(value) == str or type(value) == bytes:
pass pass

129
REFACTORING_NOTES.md Normal file
View File

@@ -0,0 +1,129 @@
# CyberPanel Installation Scripts Refactoring
## Date: January 5, 2025
## Overview
This document outlines the refactoring work performed on CyberPanel's installation scripts to consolidate common functionality and improve code maintainability.
## Files Modified
- `/install/install.py`
- `/install/installCyberPanel.py`
- `/install/install_utils.py` (newly created)
## Refactoring Summary
### 1. Created Shared Utility Module
Created `install_utils.py` in the `/install/` directory to house common functions used by both `install.py` and `installCyberPanel.py`.
### 2. Functions Moved to install_utils.py
#### System Detection Functions
- **`FetchCloudLinuxAlmaVersionVersion()`**
- Detects CloudLinux and AlmaLinux versions
- Previously duplicated in both files with identical implementations
- **`get_Ubuntu_release()`**
- Gets Ubuntu release version from `/etc/lsb-release`
- Added parameters to handle minor differences between implementations
- **`get_distro()`**
- Detects Linux distribution (Ubuntu, CentOS, CentOS 8, OpenEuler)
- Only existed in `install.py`, now shared
#### Output and Logging
- **`stdOut(message, log=0, do_exit=0, code=os.EX_OK)`**
- Standard output function with timestamps
- **Enhanced with color support**:
- 🔴 Red: Errors, failures, critical issues
- 🟡 Yellow: Warnings and alerts
- 🟢 Green: Success messages
- 🔵 Blue: Running/processing operations
- 🟣 Purple: Default/general messages
- Colors automatically disabled when output is piped
#### Package Management
- **`get_package_install_command(distro, package_name, options="")`**
- Returns appropriate install command for the distribution
- Handles apt-get, yum, and dnf package managers
- **`get_package_remove_command(distro, package_name)`**
- Returns appropriate remove command for the distribution
#### Command Execution
- **`resFailed(distro, res)`**
- Checks if command execution failed based on return code
- **`call(command, distro, bracket, message, log=0, do_exit=0, code=os.EX_OK, shell=False)`**
- Executes shell commands with retry logic (3 attempts)
- Provides consistent error handling and logging
#### Password Generation
- **`char_set`** dictionary
- Character sets for password generation (kept for backward compatibility)
- **`generate_pass(length=14)`**
- Generates cryptographically secure passwords
- Uses `secrets` module instead of `random` for better security
- **`generate_random_string(length=32, include_special=False)`**
- Flexible string generation with optional special characters
#### LiteSpeed Management
- **`format_restart_litespeed_command(server_root_path)`**
- Formats the LiteSpeed restart command
### 3. Distribution Constants
Moved distribution constants to `install_utils.py`:
```python
ubuntu = 0
centos = 1
cent8 = 2
openeuler = 3
```
### 4. Key Improvements
#### Security Enhancements
- Password generation now uses `secrets` module for cryptographic security
- Removed usage of `random.choice()` for password generation
#### Code Quality
- Eliminated code duplication across files
- Single source of truth for common functionality
- Consistent error handling and logging
#### Maintainability
- Centralized utility functions in one location
- Easier to update and maintain shared functionality
- Better organization of code
#### Visual Improvements
- Added color-coded output for better readability
- Automatic detection of terminal capabilities
- Clear visual distinction between error, warning, success, and info messages
### 5. Backward Compatibility
All changes maintain backward compatibility:
- Function signatures preserved where possible
- Wrapper functions created in original locations when needed
- `installCyberPanel.py` continues to use `install.preFlightsChecks.call` through import
### 6. Testing
Created test scripts to verify functionality:
- Color output testing
- Password generation verification
- Ensured all functions work as expected
## Benefits
1. **Reduced Code Duplication**: Common functions now exist in single location
2. **Improved Security**: Cryptographically secure password generation
3. **Better User Experience**: Color-coded output for easier reading
4. **Easier Maintenance**: Changes to common functions only need to be made once
5. **Consistent Behavior**: Both installation scripts now use identical implementations
## Future Recommendations
1. Consider moving more shared functionality to `install_utils.py`
2. Add unit tests for utility functions
3. Consider creating additional utility modules for specific domains (e.g., `network_utils.py`, `database_utils.py`)
4. Document function parameters and return values more thoroughly
5. Consider adding configuration file support for installation parameters

View File

@@ -701,7 +701,8 @@ context /.well-known/acme-challenge {
command = acmePath + " --issue" + domain_list \ command = acmePath + " --issue" + domain_list \
+ ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \ + ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \
+ ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --staging' + ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --staging' \
+ ' --webroot-path /usr/local/lsws/Example/html'
if ProcessUtilities.decideServer() == ProcessUtilities.OLS: if ProcessUtilities.decideServer() == ProcessUtilities.OLS:
result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True)
@@ -711,7 +712,8 @@ context /.well-known/acme-challenge {
if result.returncode == 0: if result.returncode == 0:
command = acmePath + " --issue" + domain_list \ command = acmePath + " --issue" + domain_list \
+ ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \ + ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \
+ ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --server letsencrypt' + ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --server letsencrypt' \
+ ' --webroot-path /usr/local/lsws/Example/html'
result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True)
@@ -765,6 +767,34 @@ context /.well-known/acme-challenge {
def issueSSLForDomain(domain, adminEmail, sslpath, aliasDomain=None): def issueSSLForDomain(domain, adminEmail, sslpath, aliasDomain=None):
try: try:
# Check if certificate already exists and try to renew it first
existingCertPath = '/etc/letsencrypt/live/' + domain + '/fullchain.pem'
if os.path.exists(existingCertPath):
logging.CyberCPLogFileWriter.writeToFile(f"Certificate exists for {domain}, attempting renewal...")
# Try to renew using acme.sh
acmePath = '/root/.acme.sh/acme.sh'
if os.path.exists(acmePath):
# First set the webroot path for the domain
command = f'{acmePath} --update-account --accountemail {adminEmail}'
subprocess.call(command, shell=True)
# Build domain list for renewal
renewal_domains = f'-d {domain}'
if sslUtilities.checkDNSRecords(f'www.{domain}'):
renewal_domains += f' -d www.{domain}'
# Try to renew with explicit webroot
command = f'{acmePath} --renew {renewal_domains} --webroot /usr/local/lsws/Example/html --force'
result = subprocess.run(command, capture_output=True, text=True, shell=True)
if result.returncode == 0:
logging.CyberCPLogFileWriter.writeToFile(f"Successfully renewed SSL for {domain}")
if sslUtilities.installSSLForDomain(domain, adminEmail) == 1:
return [1, "None"]
else:
logging.CyberCPLogFileWriter.writeToFile(f"Renewal failed for {domain}, falling back to new issuance")
if sslUtilities.obtainSSLForADomain(domain, adminEmail, sslpath, aliasDomain) == 1: if sslUtilities.obtainSSLForADomain(domain, adminEmail, sslpath, aliasDomain) == 1:
if sslUtilities.installSSLForDomain(domain, adminEmail) == 1: if sslUtilities.installSSLForDomain(domain, adminEmail) == 1:
return [1, "None"] return [1, "None"]