diff --git a/firewall/firewallManager.py b/firewall/firewallManager.py
index 0427ac38d..d3127b586 100644
--- a/firewall/firewallManager.py
+++ b/firewall/firewallManager.py
@@ -2050,9 +2050,163 @@ class FirewallManager:
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
-
-
-
+ def exportFirewallRules(self, userID=None):
+ """
+ Export all custom firewall rules to a JSON file, excluding default CyberPanel rules
+ """
+ try:
+ currentACL = ACLManager.loadedACL(userID)
+
+ if currentACL['admin'] == 1:
+ pass
+ else:
+ return ACLManager.loadErrorJson('exportStatus', 0)
+
+ # Get all firewall rules
+ rules = FirewallRules.objects.all()
+
+ # Default CyberPanel rules to exclude
+ default_rules = ['CyberPanel Admin', 'SSHCustom']
+
+ # Filter out default rules
+ custom_rules = []
+ for rule in rules:
+ if rule.name not in default_rules:
+ custom_rules.append({
+ 'name': rule.name,
+ 'proto': rule.proto,
+ 'port': rule.port,
+ 'ipAddress': rule.ipAddress
+ })
+
+ # Create export data with metadata
+ export_data = {
+ 'version': '1.0',
+ 'exported_at': time.strftime('%Y-%m-%d %H:%M:%S'),
+ 'total_rules': len(custom_rules),
+ 'rules': custom_rules
+ }
+
+ # Create JSON response with file download
+ json_content = json.dumps(export_data, indent=2)
+
+ logging.CyberCPLogFileWriter.writeToFile(f"Firewall rules exported successfully. Total rules: {len(custom_rules)}")
+
+ # Return file as download
+ response = HttpResponse(json_content, content_type='application/json')
+ response['Content-Disposition'] = f'attachment; filename="firewall_rules_export_{int(time.time())}.json"'
+
+ return response
+
+ except BaseException as msg:
+ final_dic = {'exportStatus': 0, 'error_message': str(msg)}
+ final_json = json.dumps(final_dic)
+ return HttpResponse(final_json)
+
+ def importFirewallRules(self, userID=None, data=None):
+ """
+ Import firewall rules from a JSON file
+ """
+ try:
+ currentACL = ACLManager.loadedACL(userID)
+
+ if currentACL['admin'] == 1:
+ pass
+ else:
+ return ACLManager.loadErrorJson('importStatus', 0)
+
+ # Handle file upload
+ if hasattr(self.request, 'FILES') and 'import_file' in self.request.FILES:
+ import_file = self.request.FILES['import_file']
+
+ # Read file content
+ import_data = json.loads(import_file.read().decode('utf-8'))
+ else:
+ # Fallback to file path method
+ import_file_path = data.get('import_file_path', '')
+
+ if not import_file_path or not os.path.exists(import_file_path):
+ final_dic = {'importStatus': 0, 'error_message': 'Import file not found or invalid path'}
+ final_json = json.dumps(final_dic)
+ return HttpResponse(final_json)
+
+ # Read and parse the import file
+ with open(import_file_path, 'r') as f:
+ import_data = json.load(f)
+
+ # Validate the import data structure
+ if 'rules' not in import_data:
+ final_dic = {'importStatus': 0, 'error_message': 'Invalid import file format. Missing rules array.'}
+ final_json = json.dumps(final_dic)
+ return HttpResponse(final_json)
+
+ imported_count = 0
+ skipped_count = 0
+ error_count = 0
+ errors = []
+
+ # Default CyberPanel rules to exclude from import
+ default_rules = ['CyberPanel Admin', 'SSHCustom']
+
+ for rule_data in import_data['rules']:
+ try:
+ # Skip default rules
+ if rule_data.get('name', '') in default_rules:
+ skipped_count += 1
+ continue
+
+ # Check if rule already exists
+ existing_rule = FirewallRules.objects.filter(
+ name=rule_data['name'],
+ proto=rule_data['proto'],
+ port=rule_data['port'],
+ ipAddress=rule_data['ipAddress']
+ ).first()
+
+ if existing_rule:
+ skipped_count += 1
+ continue
+
+ # Add the rule to the system firewall
+ FirewallUtilities.addRule(
+ rule_data['proto'],
+ rule_data['port'],
+ rule_data['ipAddress']
+ )
+
+ # Add the rule to the database
+ new_rule = FirewallRules(
+ name=rule_data['name'],
+ proto=rule_data['proto'],
+ port=rule_data['port'],
+ ipAddress=rule_data['ipAddress']
+ )
+ new_rule.save()
+
+ imported_count += 1
+
+ except Exception as e:
+ error_count += 1
+ errors.append(f"Rule '{rule_data.get('name', 'Unknown')}': {str(e)}")
+ logging.CyberCPLogFileWriter.writeToFile(f"Error importing rule {rule_data.get('name', 'Unknown')}: {str(e)}")
+
+ logging.CyberCPLogFileWriter.writeToFile(f"Firewall rules import completed. Imported: {imported_count}, Skipped: {skipped_count}, Errors: {error_count}")
+
+ final_dic = {
+ 'importStatus': 1,
+ 'error_message': "None",
+ 'imported_count': imported_count,
+ 'skipped_count': skipped_count,
+ 'error_count': error_count,
+ 'errors': errors
+ }
+ final_json = json.dumps(final_dic)
+ return HttpResponse(final_json)
+
+ except BaseException as msg:
+ final_dic = {'importStatus': 0, 'error_message': str(msg)}
+ final_json = json.dumps(final_dic)
+ return HttpResponse(final_json)
diff --git a/firewall/static/firewall/firewall.js b/firewall/static/firewall/firewall.js
index b2f9b2326..495b88ec0 100644
--- a/firewall/static/firewall/firewall.js
+++ b/firewall/static/firewall/firewall.js
@@ -2549,4 +2549,151 @@ app.controller('litespeed_ent_conf', function ($scope, $http, $timeout, $window)
});
};
+ // Export/Import Firewall Rules Functions
+ $scope.exportRules = function () {
+ $scope.rulesLoading = false;
+ $scope.actionFailed = true;
+ $scope.actionSuccess = true;
+
+ url = "/firewall/exportFirewallRules";
+
+ var data = {};
+
+ var config = {
+ headers: {
+ 'X-CSRFToken': getCookie('csrftoken')
+ }
+ };
+
+ $http.post(url, data, config).then(exportSuccess, exportError);
+
+ function exportSuccess(response) {
+ $scope.rulesLoading = true;
+
+ // Check if response is JSON (error) or file download
+ if (typeof response.data === 'string' && response.data.includes('{')) {
+ try {
+ var errorData = JSON.parse(response.data);
+ if (errorData.exportStatus === 0) {
+ $scope.actionFailed = false;
+ $scope.actionSuccess = true;
+ $scope.errorMessage = errorData.error_message;
+ return;
+ }
+ } catch (e) {
+ // If not JSON, assume it's the file content
+ }
+ }
+
+ // If we get here, it's a successful file download
+ $scope.actionFailed = true;
+ $scope.actionSuccess = false;
+ }
+
+ function exportError(response) {
+ $scope.rulesLoading = true;
+ $scope.actionFailed = false;
+ $scope.actionSuccess = true;
+ $scope.errorMessage = "Could not connect to server. Please refresh this page.";
+ }
+ };
+
+ $scope.importRules = function () {
+ // Create file input element
+ var input = document.createElement('input');
+ input.type = 'file';
+ input.accept = '.json';
+ input.style.display = 'none';
+
+ input.onchange = function(event) {
+ var file = event.target.files[0];
+ if (file) {
+ var reader = new FileReader();
+ reader.onload = function(e) {
+ try {
+ var importData = JSON.parse(e.target.result);
+
+ // Validate file format
+ if (!importData.rules || !Array.isArray(importData.rules)) {
+ $scope.$apply(function() {
+ $scope.actionFailed = false;
+ $scope.actionSuccess = true;
+ $scope.errorMessage = "Invalid import file format. Please select a valid firewall rules export file.";
+ });
+ return;
+ }
+
+ // Upload file to server
+ uploadImportFile(file);
+ } catch (error) {
+ $scope.$apply(function() {
+ $scope.actionFailed = false;
+ $scope.actionSuccess = true;
+ $scope.errorMessage = "Invalid JSON file. Please select a valid firewall rules export file.";
+ });
+ }
+ };
+ reader.readAsText(file);
+ }
+ };
+
+ document.body.appendChild(input);
+ input.click();
+ document.body.removeChild(input);
+ };
+
+ function uploadImportFile(file) {
+ $scope.rulesLoading = false;
+ $scope.actionFailed = true;
+ $scope.actionSuccess = true;
+
+ var formData = new FormData();
+ formData.append('import_file', file);
+
+ var config = {
+ headers: {
+ 'X-CSRFToken': getCookie('csrftoken'),
+ 'Content-Type': undefined
+ },
+ transformRequest: angular.identity
+ };
+
+ $http.post("/firewall/importFirewallRules", formData, config).then(importSuccess, importError);
+
+ function importSuccess(response) {
+ $scope.rulesLoading = true;
+
+ if (response.data.importStatus === 1) {
+ $scope.actionFailed = true;
+ $scope.actionSuccess = false;
+
+ // Refresh rules list
+ populateCurrentRecords();
+
+ // Show import summary
+ var summary = `Import completed successfully!\n` +
+ `Imported: ${response.data.imported_count} rules\n` +
+ `Skipped: ${response.data.skipped_count} rules\n` +
+ `Errors: ${response.data.error_count} rules`;
+
+ if (response.data.errors && response.data.errors.length > 0) {
+ summary += `\n\nErrors:\n${response.data.errors.join('\n')}`;
+ }
+
+ alert(summary);
+ } else {
+ $scope.actionFailed = false;
+ $scope.actionSuccess = true;
+ $scope.errorMessage = response.data.error_message;
+ }
+ }
+
+ function importError(response) {
+ $scope.rulesLoading = true;
+ $scope.actionFailed = false;
+ $scope.actionSuccess = true;
+ $scope.errorMessage = "Could not connect to server. Please refresh this page.";
+ }
+ }
+
});
\ No newline at end of file
diff --git a/firewall/templates/firewall/firewall.html b/firewall/templates/firewall/firewall.html
index 2081c94ed..711043857 100644
--- a/firewall/templates/firewall/firewall.html
+++ b/firewall/templates/firewall/firewall.html
@@ -445,6 +445,41 @@
color: var(--text-muted, #64748b);
}
+ /* Export/Import Buttons */
+ .export-import-buttons {
+ display: flex;
+ gap: 0.75rem;
+ align-items: center;
+ }
+
+ .btn-export, .btn-import {
+ padding: 0.5rem 1rem;
+ border-radius: 8px;
+ font-weight: 500;
+ font-size: 0.875rem;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ border: none;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ background: rgba(255, 255, 255, 0.2);
+ color: var(--text-light, white);
+ backdrop-filter: blur(10px);
+ }
+
+ .btn-export:hover, .btn-import:hover {
+ background: rgba(255, 255, 255, 0.3);
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+ }
+
+ .btn-export:disabled, .btn-import:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ transform: none;
+ }
+
.alert {
padding: 1rem 1.5rem;
border-radius: 8px;
@@ -877,7 +912,25 @@
{% trans "Firewall Rules" %}
-
+
+
+
+
+
+
+
diff --git a/firewall/urls.py b/firewall/urls.py
index b866bbbe7..eee5da43d 100644
--- a/firewall/urls.py
+++ b/firewall/urls.py
@@ -63,4 +63,8 @@ urlpatterns = [
path('litespeed_ent_conf', views.litespeed_ent_conf, name='litespeed_ent_conf'),
path('fetchlitespeed_conf', views.fetchlitespeed_conf, name='fetchlitespeed_conf'),
path('saveLitespeed_conf', views.saveLitespeed_conf, name='saveLitespeed_conf'),
+
+ # Firewall Export/Import
+ path('exportFirewallRules', views.exportFirewallRules, name='exportFirewallRules'),
+ path('importFirewallRules', views.importFirewallRules, name='importFirewallRules'),
]
diff --git a/firewall/views.py b/firewall/views.py
index 609af0adf..15f894bd2 100644
--- a/firewall/views.py
+++ b/firewall/views.py
@@ -679,5 +679,29 @@ def deleteBannedIP(request):
userID = request.session['userID']
fm = FirewallManager()
return fm.deleteBannedIP(userID, json.loads(request.body))
+ except KeyError:
+ return redirect(loadLoginPage)
+
+
+def exportFirewallRules(request):
+ try:
+ userID = request.session['userID']
+ fm = FirewallManager()
+ return fm.exportFirewallRules(userID)
+ except KeyError:
+ return redirect(loadLoginPage)
+
+
+def importFirewallRules(request):
+ try:
+ userID = request.session['userID']
+ fm = FirewallManager(request)
+
+ # Handle file upload
+ if request.method == 'POST' and 'import_file' in request.FILES:
+ return fm.importFirewallRules(userID, None)
+ else:
+ # Handle JSON data
+ return fm.importFirewallRules(userID, json.loads(request.body))
except KeyError:
return redirect(loadLoginPage)
\ No newline at end of file
diff --git a/guides/EXPORT_IMPORT_FIREWALL_RULES.md b/guides/EXPORT_IMPORT_FIREWALL_RULES.md
new file mode 100644
index 000000000..9fb19b983
--- /dev/null
+++ b/guides/EXPORT_IMPORT_FIREWALL_RULES.md
@@ -0,0 +1,121 @@
+# Firewall Rules Export/Import Feature
+
+## Overview
+
+This feature allows CyberPanel administrators to export and import firewall rules between servers, making it easy to replicate security configurations across multiple servers.
+
+## Features
+
+### Export Functionality
+- Exports all custom firewall rules to a JSON file
+- Excludes default CyberPanel rules (CyberPanel Admin, SSHCustom) to prevent conflicts
+- Includes metadata such as export timestamp and rule count
+- Downloads file directly to the user's browser
+
+### Import Functionality
+- Imports firewall rules from a previously exported JSON file
+- Validates file format before processing
+- Skips duplicate rules (same name, protocol, port, and IP address)
+- Excludes default CyberPanel rules from import
+- Provides detailed import summary (imported, skipped, error counts)
+- Shows specific error messages for failed imports
+
+## Usage
+
+### Exporting Rules
+1. Navigate to the Firewall section in CyberPanel
+2. Click the "Export Rules" button in the Firewall Rules panel header
+3. The system will generate and download a JSON file containing your custom rules
+
+### Importing Rules
+1. Navigate to the Firewall section in CyberPanel
+2. Click the "Import Rules" button in the Firewall Rules panel header
+3. Select a previously exported JSON file
+4. The system will process the import and show a summary of results
+
+## File Format
+
+The exported JSON file has the following structure:
+
+```json
+{
+ "version": "1.0",
+ "exported_at": "2024-01-15 14:30:25",
+ "total_rules": 5,
+ "rules": [
+ {
+ "name": "Custom Web Server",
+ "proto": "tcp",
+ "port": "8080",
+ "ipAddress": "0.0.0.0/0"
+ },
+ {
+ "name": "Database Access",
+ "proto": "tcp",
+ "port": "3306",
+ "ipAddress": "192.168.1.0/24"
+ }
+ ]
+}
+```
+
+## Security Considerations
+
+- Only administrators can export/import firewall rules
+- Default CyberPanel rules are excluded to prevent system conflicts
+- Import process validates file format and rule data
+- Failed imports are logged for troubleshooting
+- Duplicate rules are automatically skipped
+
+## Error Handling
+
+The system provides comprehensive error handling:
+- Invalid file format detection
+- Missing required fields validation
+- Individual rule import error tracking
+- Detailed error messages for troubleshooting
+- Import summary with counts of successful, skipped, and failed imports
+
+## Technical Implementation
+
+### Backend Components
+- `exportFirewallRules()` method in `FirewallManager`
+- `importFirewallRules()` method in `FirewallManager`
+- New URL patterns for export/import endpoints
+- File upload handling for import functionality
+
+### Frontend Components
+- Export/Import buttons in firewall UI
+- File download handling for exports
+- File upload dialog for imports
+- Progress indicators and error messaging
+- Import summary display
+
+### Database Integration
+- Uses existing `FirewallRules` model
+- Maintains referential integrity
+- Preserves rule relationships and constraints
+
+## Benefits
+
+1. **Time Efficiency**: Significantly reduces time to replicate firewall rules across servers
+2. **Error Reduction**: Minimizes human error in manual rule creation
+3. **Consistency**: Ensures identical security policies across multiple servers
+4. **Backup**: Provides a way to backup and restore firewall configurations
+5. **Migration**: Simplifies server migration and setup processes
+
+## Compatibility
+
+- Compatible with CyberPanel's existing firewall system
+- Works with both TCP and UDP protocols
+- Supports all IP address formats (single IPs, CIDR ranges)
+- Maintains compatibility with existing firewall utilities
+
+## Future Enhancements
+
+Potential future improvements could include:
+- Rule conflict detection and resolution
+- Selective rule import (choose specific rules)
+- Rule templates and presets
+- Bulk rule management
+- Integration with configuration management tools
diff --git a/guides/SECURITY_INSTALLATION.md b/guides/SECURITY_INSTALLATION.md
new file mode 100644
index 000000000..60cd302c3
--- /dev/null
+++ b/guides/SECURITY_INSTALLATION.md
@@ -0,0 +1,193 @@
+# CyberPanel Secure Installation Guide
+
+## Overview
+
+This document describes the secure installation process for CyberPanel that eliminates hardcoded passwords and implements environment-based configuration.
+
+## Security Improvements
+
+### ✅ **Fixed Security Vulnerabilities**
+
+1. **Hardcoded Database Passwords** - Now generated securely during installation
+2. **Hardcoded Django Secret Key** - Now generated using cryptographically secure random generation
+3. **Environment Variables** - All sensitive configuration moved to `.env` file
+4. **File Permissions** - `.env` file set to 600 (owner read/write only)
+
+### 🔐 **Security Features**
+
+- **Cryptographically Secure Passwords**: Uses Python's `secrets` module for password generation
+- **Environment-based Configuration**: Sensitive data stored in `.env` file, not in code
+- **Secure File Permissions**: Environment files protected with 600 permissions
+- **Credential Backup**: Automatic backup of credentials for recovery
+- **Fallback Security**: Maintains backward compatibility with fallback method
+
+## Installation Process
+
+### 1. **Automatic Secure Installation**
+
+The installation script now automatically:
+
+1. Generates secure random passwords for:
+ - MySQL root user
+ - CyberPanel database user
+ - Django secret key
+
+2. Creates `.env` file with secure configuration:
+ ```bash
+ # Generated during installation
+ SECRET_KEY=your_64_character_secure_key
+ DB_PASSWORD=your_24_character_secure_password
+ ROOT_DB_PASSWORD=your_24_character_secure_password
+ ```
+
+3. Creates `.env.backup` file for credential recovery
+4. Sets secure file permissions (600) on all environment files
+
+### 2. **Manual Installation** (if needed)
+
+If you need to manually generate environment configuration:
+
+```bash
+cd /usr/local/CyberCP
+python install/env_generator.py /usr/local/CyberCP
+```
+
+## File Structure
+
+```
+/usr/local/CyberCP/
+├── .env # Main environment configuration (600 permissions)
+├── .env.backup # Credential backup (600 permissions)
+├── .env.template # Template for manual configuration
+├── .gitignore # Prevents .env files from being committed
+└── CyberCP/
+ └── settings.py # Updated to use environment variables
+```
+
+## Security Best Practices
+
+### ✅ **Do's**
+
+- Keep `.env` and `.env.backup` files secure
+- Record credentials from `.env.backup` and delete the file after installation
+- Use strong, unique passwords for production deployments
+- Regularly rotate database passwords
+- Monitor access to environment files
+
+### ❌ **Don'ts**
+
+- Never commit `.env` files to version control
+- Don't share `.env` files via insecure channels
+- Don't use default passwords in production
+- Don't leave `.env.backup` files on the system after recording credentials
+
+## Recovery
+
+### **Lost Credentials**
+
+If you lose your database credentials:
+
+1. Check if `.env.backup` file exists:
+ ```bash
+ sudo cat /usr/local/CyberCP/.env.backup
+ ```
+
+2. If backup doesn't exist, you'll need to reset MySQL passwords using MySQL recovery procedures
+
+### **Regenerate Environment**
+
+To regenerate environment configuration:
+
+```bash
+cd /usr/local/CyberCP
+sudo python install/env_generator.py /usr/local/CyberCP
+```
+
+## Configuration Options
+
+### **Environment Variables**
+
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `SECRET_KEY` | Django secret key | Generated (64 chars) |
+| `DB_PASSWORD` | CyberPanel DB password | Generated (24 chars) |
+| `ROOT_DB_PASSWORD` | MySQL root password | Generated (24 chars) |
+| `DEBUG` | Debug mode | False |
+| `ALLOWED_HOSTS` | Allowed hosts | localhost,127.0.0.1,hostname |
+
+### **Custom Configuration**
+
+To use custom passwords during installation:
+
+```bash
+python install/env_generator.py /usr/local/CyberCP "your_root_password" "your_db_password"
+```
+
+## Troubleshooting
+
+### **Installation Fails**
+
+If the new secure installation fails:
+
+1. Check installation logs for error messages
+2. The system will automatically fallback to the original installation method
+3. Verify Python dependencies are installed:
+ ```bash
+ pip install python-dotenv
+ ```
+
+### **Environment Loading Issues**
+
+If Django can't load environment variables:
+
+1. Ensure `.env` file exists and has correct permissions:
+ ```bash
+ ls -la /usr/local/CyberCP/.env
+ # Should show: -rw------- 1 root root
+ ```
+
+2. Install python-dotenv if missing:
+ ```bash
+ pip install python-dotenv
+ ```
+
+## Migration from Old Installation
+
+### **Existing Installations**
+
+For existing CyberPanel installations with hardcoded passwords:
+
+1. **Backup current configuration**:
+ ```bash
+ cp /usr/local/CyberCP/CyberCP/settings.py /usr/local/CyberCP/CyberCP/settings.py.backup
+ ```
+
+2. **Generate new environment configuration**:
+ ```bash
+ cd /usr/local/CyberCP
+ python install/env_generator.py /usr/local/CyberCP
+ ```
+
+3. **Update settings.py** (already done in new installations):
+ - The settings.py file now supports environment variables
+ - It will fallback to hardcoded values if .env is not available
+
+4. **Test the configuration**:
+ ```bash
+ cd /usr/local/CyberCP
+ python manage.py check
+ ```
+
+## Support
+
+For issues with the secure installation:
+
+1. Check the installation logs
+2. Verify file permissions
+3. Ensure all dependencies are installed
+4. Review the fallback installation method if needed
+
+---
+
+**Security Notice**: This installation method significantly improves security by eliminating hardcoded credentials. Always ensure proper file permissions and secure handling of environment files.
+