diff --git a/.env.template b/.env.template new file mode 100644 index 000000000..71c488291 --- /dev/null +++ b/.env.template @@ -0,0 +1,36 @@ +# CyberPanel Environment Configuration Template +# Copy this file to .env and update with your actual values +# NEVER commit .env to version control! + +# Django Configuration +SECRET_KEY=your_very_long_random_secret_key_here_minimum_50_characters +DEBUG=False +ALLOWED_HOSTS=localhost,127.0.0.1,yourdomain.com + +# Database Configuration - CyberPanel Database +DB_NAME=cyberpanel +DB_USER=cyberpanel +DB_PASSWORD=your_secure_cyberpanel_db_password_here +DB_HOST=localhost +DB_PORT=3306 + +# Root Database Configuration - MySQL Root Access +ROOT_DB_NAME=mysql +ROOT_DB_USER=root +ROOT_DB_PASSWORD=your_secure_mysql_root_password_here +ROOT_DB_HOST=localhost +ROOT_DB_PORT=3306 + +# Security Settings +SECURE_SSL_REDIRECT=False +SECURE_HSTS_SECONDS=0 +SECURE_HSTS_INCLUDE_SUBDOMAINS=False +SECURE_HSTS_PRELOAD=False +SESSION_COOKIE_SECURE=False +CSRF_COOKIE_SECURE=False + +# File Upload Settings +DATA_UPLOAD_MAX_MEMORY_SIZE=2147483648 + +# Logging Configuration +LOG_LEVEL=INFO diff --git a/.gitignore b/.gitignore index dffc08ca3..2812413b9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,120 @@ +# CyberPanel .gitignore + +# Environment variables (CRITICAL - Contains sensitive data) +.env +.env.backup +.env.local +.env.production +.env.staging + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Virtual environments +venv/ +env/ +ENV/ +env.bak/ +venv.bak/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS .DS_Store -.AppleDouble -.LSOverride -*.pyc -.idea -venv -/.venv/ +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Logs +*.log +logs/ +log/ + +# Database +*.db +*.sqlite3 + +# Temporary files +tmp/ +temp/ +*.tmp + +# Backup files +*.bak +*.backup + +# Node modules (if any frontend build tools are used) +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Coverage reports +htmlcov/ +.coverage +.coverage.* +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# CyberPanel specific +/usr/local/CyberCP/ +/etc/cyberpanel/ +cyberpanel_password.txt +mysql_password.txt \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..04834c690 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,249 @@ +# Contributing to CyberPanel + +Thank you for your interest in contributing to CyberPanel! This document provides guidelines and information for contributors. + +## ๐ฟ Branch Structure + +CyberPanel uses a structured branching strategy to manage development and releases: + +### **Branch Types** + +1. **`stable`** - Production-ready stable branch +2. **`vX.X.X`** - Version-specific stable branch (e.g., `v2.4.3`) +3. **`vX.X.X-dev`** - Development branch for specific version (e.g., `v2.4.3-dev`) + +## ๐ Development Lifecycle + +### **Development Process** + +1. **Default Branch**: The latest `vX.X.X-dev` branch serves as the default (master) branch +2. **Contributions**: All contributors must push to the latest `vX.X.X-dev` branch +3. **Stability Check**: Once development is complete and believed to be stable, a new `vX.X.X` stable branch is created from the dev branch +4. **Merge Process**: The `vX.X.X` stable branch is then merged into the main `stable` branch +5. **New Development**: A new `vX.X.X-dev` branch is created and becomes the default branch +6. **Cleanup**: Old dev branches are deleted to save space + +### **Important Rules** + +- โ **DO**: Create pull requests only for the latest dev branch +- โ **DON'T**: Create pull requests for any other branches (stable, old dev branches, etc.) +- ๐ **Development**: All development happens only in the latest dev branch +- ๐๏ธ **Cleanup**: Old dev branches are deleted after merging to stable + +## ๐ Getting Started + +### **Prerequisites** + +- Python 3.6+ (see supported versions in README.md) +- Django framework knowledge +- Basic understanding of web hosting control panels +- Git version control + +### **Setup Development Environment** + +1. **Fork the Repository** + ```bash + # Fork the repository on GitHub, then clone your fork + git clone https://github.com/YOUR_USERNAME/cyberpanel.git + cd cyberpanel + ``` + +2. **Add Upstream Remote** + ```bash + git remote add upstream https://github.com/usmannasir/cyberpanel.git + ``` + +3. **Create Development Branch** + ```bash + # Checkout the latest dev branch + git checkout vX.X.X-dev + git pull upstream vX.X.X-dev + ``` + +4. **Install Dependencies** + ```bash + # Install Python dependencies + pip install -r requirements.txt + ``` + +## ๐ Making Contributions + +### **Code Style Guidelines** + +- Follow PEP 8 for Python code +- Use meaningful variable and function names +- Add comments for complex logic +- Write comprehensive docstrings for functions and classes +- Ensure all code is properly tested + +### **Commit Message Format** + +Use clear, descriptive commit messages: + +``` +type(scope): brief description + +Detailed description of changes made. +- List specific changes +- Explain why changes were made +- Reference any related issues + +Fixes #123 +``` + +**Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore` + +### **Pull Request Process** + +1. **Create Feature Branch** + ```bash + git checkout -b feature/your-feature-name + ``` + +2. **Make Changes** + - Write your code + - Add tests if applicable + - Update documentation if needed + +3. **Test Your Changes** + ```bash + # Run tests + python manage.py test + + # Check for linting issues + flake8 . + ``` + +4. **Commit Changes** + ```bash + git add . + git commit -m "feat(module): add new feature" + ``` + +5. **Push and Create PR** + ```bash + git push origin feature/your-feature-name + ``` + Then create a pull request on GitHub targeting the latest dev branch. + +## ๐งช Testing + +### **Test Requirements** + +- All new features must include tests +- Bug fixes must include regression tests +- Ensure all existing tests pass +- Maintain or improve test coverage + +### **Running Tests** + +```bash +# Run all tests +python manage.py test + +# Run specific test module +python manage.py test module_name.tests + +# Run with coverage +coverage run --source='.' manage.py test +coverage report +``` + +## ๐ Issue Reporting + +### **Before Reporting** + +- Check existing issues to avoid duplicates +- Ensure you're using the latest version +- Verify the issue exists in the latest dev branch + +### **Issue Template** + +When creating an issue, include: + +- **OS and Version**: Your operating system and CyberPanel version +- **Steps to Reproduce**: Clear, numbered steps +- **Expected Behavior**: What should happen +- **Actual Behavior**: What actually happens +- **Screenshots**: If applicable +- **Logs**: Relevant error logs from `/usr/local/lscp/logs/` + +## ๐ Security + +### **Security Issues** + +For security-related issues: + +- **DO NOT** create public issues +- Email security concerns to: security@cyberpanel.net +- Include detailed information about the vulnerability +- Allow time for the team to address before public disclosure + +## ๐ Documentation + +### **Documentation Guidelines** + +- Update relevant documentation when adding features +- Use clear, concise language +- Include code examples where helpful +- Follow the existing documentation style +- Update README.md if adding new features or changing installation process + +## ๐ค Code Review Process + +### **Review Criteria** + +- Code quality and style +- Test coverage +- Documentation updates +- Security considerations +- Performance impact +- Backward compatibility + +### **Review Timeline** + +- Initial review: Within 48 hours +- Follow-up reviews: Within 24 hours +- Merge decision: Within 1 week (for approved PRs) + +## ๐ท๏ธ Release Process + +### **Version Numbering** + +CyberPanel follows semantic versioning (MAJOR.MINOR.PATCH): + +- **MAJOR**: Breaking changes +- **MINOR**: New features (backward compatible) +- **PATCH**: Bug fixes (backward compatible) + +### **Release Schedule** + +- **Stable Releases**: Monthly or as needed +- **Hotfixes**: As critical issues arise +- **Development**: Continuous integration + +## ๐ฌ Community + +### **Getting Help** + +- ๐ [Documentation](https://cyberpanel.net/KnowledgeBase/) +- ๐ฌ [Discord](https://discord.gg/g8k8Db3) +- ๐ข [Forums](https://community.cyberpanel.net) +- ๐ต [Facebook Group](https://www.facebook.com/groups/cyberpanel) + +### **Contributing Guidelines** + +- Be respectful and constructive +- Help others learn and grow +- Follow the code of conduct +- Ask questions when unsure + +## ๐ License + +By contributing to CyberPanel, you agree that your contributions will be licensed under the same license as the project (GPL-3.0). + +--- + +**Thank you for contributing to CyberPanel!** ๐ + +Your contributions help make web hosting management easier for thousands of users worldwide. diff --git a/CyberCP/settings.py b/CyberCP/settings.py index ac1579490..242636410 100644 --- a/CyberCP/settings.py +++ b/CyberCP/settings.py @@ -13,6 +13,14 @@ https://docs.djangoproject.com/en/1.11/ref/settings/ import os from django.utils.translation import gettext_lazy as _ +# Load environment variables from .env file +try: + from dotenv import load_dotenv + load_dotenv() +except ImportError: + # dotenv not available, continue without it + pass + # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -20,12 +28,13 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'xr%j*p!*$0d%(-(e%@-*hyoz4$f%y77coq0u)6pwmjg4)q&19f' +SECRET_KEY = os.getenv('SECRET_KEY', 'xr%j*p!*$0d%(-(e%@-*hyoz4$f%y77coq0u)6pwmjg4)q&19f') # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = False +DEBUG = os.getenv('DEBUG', 'False').lower() == 'true' -ALLOWED_HOSTS = ['*'] +# Allow configuration via environment variable, fallback to wildcard for backward compatibility +ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '*').split(',') # Application definition @@ -96,6 +105,7 @@ TEMPLATES = [ 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'baseTemplate.context_processors.version_context', + 'baseTemplate.context_processors.cosmetic_context', ], }, }, @@ -110,19 +120,19 @@ WSGI_APPLICATION = 'CyberCP.wsgi.application' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'cyberpanel', - 'USER': 'cyberpanel', - 'PASSWORD': 'SLTUIUxqhulwsh', - 'HOST': 'localhost', - 'PORT':'' + 'NAME': os.getenv('DB_NAME', 'cyberpanel'), + 'USER': os.getenv('DB_USER', 'cyberpanel'), + 'PASSWORD': os.getenv('DB_PASSWORD', 'SLTUIUxqhulwsh'), + 'HOST': os.getenv('DB_HOST', 'localhost'), + 'PORT': os.getenv('DB_PORT', '3306'), }, 'rootdb': { 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'mysql', - 'USER': 'root', - 'PASSWORD': 'SLTUIUxqhulwsh', - 'HOST': 'localhost', - 'PORT': '', + 'NAME': os.getenv('ROOT_DB_NAME', 'mysql'), + 'USER': os.getenv('ROOT_DB_USER', 'root'), + 'PASSWORD': os.getenv('ROOT_DB_PASSWORD', 'SLTUIUxqhulwsh'), + 'HOST': os.getenv('ROOT_DB_HOST', 'localhost'), + 'PORT': os.getenv('ROOT_DB_PORT', '3306'), }, } DATABASE_ROUTERS = ['backup.backupRouter.backupRouter'] diff --git a/README.md b/README.md index 98284c029..9ff9ab30c 100755 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr - ๐ [Docs (Old)](https://community.cyberpanel.net/docs) - ๐ [Additional Guides](guides/INDEX.md) - Detailed guides for Docker, AI Scanner, Mautic, and more - ๐ [Local Documentation](guides/) - All guides available in this repository +- ๐ค [Contributing Guide](CONTRIBUTING.md) - How to contribute to CyberPanel development - โ [Changelog](https://community.cyberpanel.net/t/change-logs/161) - ๐ฌ [Forums](https://community.cyberpanel.net) - ๐ข [Discord](https://discord.gg/g8k8Db3) diff --git a/SECURITY_INSTALLATION.md b/SECURITY_INSTALLATION.md new file mode 100644 index 000000000..60cd302c3 --- /dev/null +++ b/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. + diff --git a/backup/static/backup/backup.js b/backup/static/backup/backup.js index 1760c54fc..7323e8e49 100644 --- a/backup/static/backup/backup.js +++ b/backup/static/backup/backup.js @@ -2074,6 +2074,8 @@ app.controller('scheduleBackup', function ($scope, $http, $window) { $scope.allSites = response.data.allSites; $scope.lastRun = response.data.lastRun; $scope.currentStatus = response.data.currentStatus; + $scope.backupFrequency = response.data.currently; + $scope.backupRetention = response.data.retention; } else { new PNotify({ diff --git a/baseTemplate/context_processors.py b/baseTemplate/context_processors.py index c63e23902..13d871c10 100644 --- a/baseTemplate/context_processors.py +++ b/baseTemplate/context_processors.py @@ -7,4 +7,20 @@ def version_context(request): 'CYBERPANEL_VERSION': VERSION, 'CYBERPANEL_BUILD': BUILD, 'CYBERPANEL_FULL_VERSION': f"{VERSION}.{BUILD}" - } \ No newline at end of file + } + +def cosmetic_context(request): + """Add cosmetic data (custom CSS) to all templates""" + try: + from .models import CyberPanelCosmetic + cosmetic = CyberPanelCosmetic.objects.get(pk=1) + return { + 'cosmetic': cosmetic + } + except: + from .models import CyberPanelCosmetic + cosmetic = CyberPanelCosmetic() + cosmetic.save() + return { + 'cosmetic': cosmetic + } \ No newline at end of file diff --git a/baseTemplate/templates/baseTemplate/index.html b/baseTemplate/templates/baseTemplate/index.html index 82a55f350..3a6986ddd 100644 --- a/baseTemplate/templates/baseTemplate/index.html +++ b/baseTemplate/templates/baseTemplate/index.html @@ -15,6 +15,11 @@ + + + diff --git a/guides/CONTRIBUTING.md b/guides/CONTRIBUTING.md deleted file mode 100644 index 3aa9c212e..000000000 --- a/guides/CONTRIBUTING.md +++ /dev/null @@ -1,14 +0,0 @@ -Branches - - 1.Stable-> Stable branch - 2.vX.X.X-> vX.X.X Stable branch - 3.vX.X.X-dev-> v.X.X.X Dev branch - -Development Lifecycle - - vX.X.X-dev will be default(master) branch. All contributors must push to latest vX.X.X-dev branch. Once development - is complete(believed to be stable) new vX.X.X Stable branch will be created from Dev branch. Then vX.X.X Stable will - be merged into Stable branch. After that a new vX.X.X-dev branch will be created and it will be default(master) - branch. Old dev branch will be deleted at this stage(to save space) and no development will happen on old stable or - dev(if not deleted) branch. All development will only take place in latest dev branch. You must not create pull - request for any other branches other than latest dev branch. diff --git a/install/env_generator.py b/install/env_generator.py new file mode 100644 index 000000000..47f6cac64 --- /dev/null +++ b/install/env_generator.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +""" +CyberPanel Environment Configuration Generator +Generates secure .env file with random passwords during installation +""" + +import os +import sys +import secrets +import string +from pathlib import Path + +def generate_secure_password(length=24): + """ + Generate a cryptographically secure password + + Args: + length: Length of the password to generate (default 24) + + Returns: + str: Random password containing uppercase, lowercase, digits and safe special chars + """ + # Use safe characters that don't require escaping in most contexts + safe_chars = string.ascii_letters + string.digits + '!@#$%^&*' + return ''.join(secrets.choice(safe_chars) for _ in range(length)) + +def generate_secret_key(length=64): + """ + Generate a cryptographically secure Django secret key + + Args: + length: Length of the secret key to generate (default 64) + + Returns: + str: Random secret key + """ + chars = string.ascii_letters + string.digits + '!@#$%^&*(-_=+)' + return ''.join(secrets.choice(chars) for _ in range(length)) + +def create_env_file(cyberpanel_path, mysql_root_password=None, cyberpanel_db_password=None): + """ + Create .env file with generated secure credentials + + Args: + cyberpanel_path: Path to CyberPanel installation directory + mysql_root_password: Optional MySQL root password (will generate if None) + cyberpanel_db_password: Optional CyberPanel DB password (will generate if None) + """ + + # Generate secure passwords if not provided + if not mysql_root_password: + mysql_root_password = generate_secure_password(24) + + if not cyberpanel_db_password: + cyberpanel_db_password = generate_secure_password(24) + + secret_key = generate_secret_key(64) + + # Get hostname for ALLOWED_HOSTS + import socket + try: + hostname = socket.gethostname() + local_ip = socket.gethostbyname(hostname) + except: + hostname = 'localhost' + local_ip = '127.0.0.1' + + # Create .env content + env_content = f"""# CyberPanel Environment Configuration +# Generated automatically during installation - DO NOT EDIT MANUALLY +# Generated on: {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')} + +# Django Configuration +SECRET_KEY={secret_key} +DEBUG=False +ALLOWED_HOSTS=localhost,127.0.0.1,{hostname},{local_ip} + +# Database Configuration - CyberPanel Database +DB_NAME=cyberpanel +DB_USER=cyberpanel +DB_PASSWORD={cyberpanel_db_password} +DB_HOST=localhost +DB_PORT=3306 + +# Root Database Configuration - MySQL Root Access +ROOT_DB_NAME=mysql +ROOT_DB_USER=root +ROOT_DB_PASSWORD={mysql_root_password} +ROOT_DB_HOST=localhost +ROOT_DB_PORT=3306 + +# Security Settings +SECURE_SSL_REDIRECT=False +SECURE_HSTS_SECONDS=0 +SECURE_HSTS_INCLUDE_SUBDOMAINS=False +SECURE_HSTS_PRELOAD=False +SESSION_COOKIE_SECURE=False +CSRF_COOKIE_SECURE=False + +# File Upload Settings +DATA_UPLOAD_MAX_MEMORY_SIZE=2147483648 + +# Logging Configuration +LOG_LEVEL=INFO +""" + + # Write .env file + env_file_path = os.path.join(cyberpanel_path, '.env') + with open(env_file_path, 'w') as f: + f.write(env_content) + + # Set secure permissions (owner read/write only) + os.chmod(env_file_path, 0o600) + + print(f"โ Generated secure .env file at: {env_file_path}") + print(f"โ MySQL Root Password: {mysql_root_password}") + print(f"โ CyberPanel DB Password: {cyberpanel_db_password}") + print(f"โ Django Secret Key: {secret_key[:20]}...") + + return { + 'mysql_root_password': mysql_root_password, + 'cyberpanel_db_password': cyberpanel_db_password, + 'secret_key': secret_key + } + +def create_env_backup(cyberpanel_path, credentials): + """ + Create a secure backup of credentials for recovery purposes + + Args: + cyberpanel_path: Path to CyberPanel installation directory + credentials: Dictionary containing generated credentials + """ + backup_content = f"""# CyberPanel Credentials Backup +# Generated: {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')} +# +# IMPORTANT: Store this file securely and delete it after recording credentials +# These are your database passwords and should be kept confidential + +MySQL Root Password: {credentials['mysql_root_password']} +CyberPanel Database Password: {credentials['cyberpanel_db_password']} +Django Secret Key: {credentials['secret_key']} + +# To restore these credentials, copy them to your .env file +""" + + backup_file_path = os.path.join(cyberpanel_path, '.env.backup') + with open(backup_file_path, 'w') as f: + f.write(backup_content) + + # Set secure permissions (owner read/write only) + os.chmod(backup_file_path, 0o600) + + print(f"โ Created credentials backup at: {backup_file_path}") + print("โ ๏ธ IMPORTANT: Record these credentials and delete the backup file for security") + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python env_generator.py [mysql_root_password] [cyberpanel_db_password]") + sys.exit(1) + + cyberpanel_path = sys.argv[1] + mysql_root_password = sys.argv[2] if len(sys.argv) > 2 else None + cyberpanel_db_password = sys.argv[3] if len(sys.argv) > 3 else None + + if not os.path.exists(cyberpanel_path): + print(f"Error: CyberPanel path does not exist: {cyberpanel_path}") + sys.exit(1) + + try: + credentials = create_env_file(cyberpanel_path, mysql_root_password, cyberpanel_db_password) + create_env_backup(cyberpanel_path, credentials) + print("\nโ Environment configuration generated successfully!") + print("โ Remember to delete .env.backup after recording credentials") + except Exception as e: + print(f"Error generating environment configuration: {e}") + sys.exit(1) diff --git a/install/install.py b/install/install.py index 862e5de83..31267817b 100644 --- a/install/install.py +++ b/install/install.py @@ -504,6 +504,73 @@ class preFlightsChecks: self.stdOut("Install psmisc") self.install_package("psmisc") + def generate_secure_env_file(self, mysql_root_password, cyberpanel_db_password): + """ + Generate secure .env file with random passwords during installation + """ + try: + import sys + import socket + + # Import the environment generator + sys.path.append(os.path.join(self.cyberPanelPath, 'install')) + from env_generator import create_env_file, create_env_backup + + # Generate secure credentials + credentials = create_env_file( + self.cyberPanelPath, + mysql_root_password, + cyberpanel_db_password + ) + + # Create backup for recovery + create_env_backup(self.cyberPanelPath, credentials) + + logging.InstallLog.writeToFile("โ Secure .env file generated successfully") + logging.InstallLog.writeToFile("โ Credentials backup created for recovery") + + return credentials + + except Exception as e: + logging.InstallLog.writeToFile(f"[ERROR] Failed to generate secure environment file: {str(e)}") + # Fallback to original method if environment generation fails + self.fallback_settings_update(mysql_root_password, cyberpanel_db_password) + + def fallback_settings_update(self, mysqlPassword, password): + """ + Fallback method to update settings.py directly if environment generation fails + """ + logging.InstallLog.writeToFile("Using fallback method for settings.py update") + + path = self.cyberPanelPath + "/CyberCP/settings.py" + data = open(path, "r").readlines() + writeDataToFile = open(path, "w") + counter = 0 + + for items in data: + if items.find('SECRET_KEY') > -1: + SK = "SECRET_KEY = '%s'\n" % (generate_pass(50)) + writeDataToFile.writelines(SK) + continue + + if items.find("'PASSWORD':") > -1: + if counter == 0: + writeDataToFile.writelines(" 'PASSWORD': '" + mysqlPassword + "'," + "\n") + counter = counter + 1 + else: + writeDataToFile.writelines(" 'PASSWORD': '" + password + "'," + "\n") + elif items.find('127.0.0.1') > -1: + writeDataToFile.writelines(" 'HOST': 'localhost',\n") + elif items.find("'PORT':'3307'") > -1: + writeDataToFile.writelines(" 'PORT': '',\n") + else: + writeDataToFile.writelines(items) + + if self.distro == ubuntu: + os.fchmod(writeDataToFile.fileno(), stat.S_IRUSR | stat.S_IWUSR) + + writeDataToFile.close() + def download_install_CyberPanel(self, mysqlPassword, mysql): ## @@ -549,50 +616,12 @@ password="%s" logging.InstallLog.writeToFile("Updating /root/.my.cnf!") - logging.InstallLog.writeToFile("Updating settings.py!") + logging.InstallLog.writeToFile("Generating secure environment configuration!") - path = self.cyberPanelPath + "/CyberCP/settings.py" + # Generate secure environment file instead of hardcoding passwords + self.generate_secure_env_file(mysqlPassword, password) - data = open(path, "r").readlines() - - writeDataToFile = open(path, "w") - - counter = 0 - - for items in data: - if items.find('SECRET_KEY') > -1: - SK = "SECRET_KEY = '%s'\n" % (generate_pass(50)) - writeDataToFile.writelines(SK) - continue - - if mysql == 'Two': - if items.find("'PASSWORD':") > -1: - if counter == 0: - writeDataToFile.writelines(" 'PASSWORD': '" + mysqlPassword + "'," + "\n") - counter = counter + 1 - else: - writeDataToFile.writelines(" 'PASSWORD': '" + password + "'," + "\n") - - else: - writeDataToFile.writelines(items) - else: - if items.find("'PASSWORD':") > -1: - if counter == 0: - writeDataToFile.writelines(" 'PASSWORD': '" + mysqlPassword + "'," + "\n") - counter = counter + 1 - else: - writeDataToFile.writelines(" 'PASSWORD': '" + password + "'," + "\n") - elif items.find('127.0.0.1') > -1: - writeDataToFile.writelines(" 'HOST': 'localhost',\n") - elif items.find("'PORT':'3307'") > -1: - writeDataToFile.writelines(" 'PORT': '',\n") - else: - writeDataToFile.writelines(items) - - if self.distro == ubuntu: - os.fchmod(writeDataToFile.fileno(), stat.S_IRUSR | stat.S_IWUSR) - - writeDataToFile.close() + logging.InstallLog.writeToFile("Environment configuration generated successfully!") if self.remotemysql == 'ON': command = "sed -i 's|localhost|%s|g' %s" % (self.mysqlhost, path) diff --git a/mailServer/templates/mailServer/changeEmailPassword.html b/mailServer/templates/mailServer/changeEmailPassword.html index a0e79c185..363f7e93d 100644 --- a/mailServer/templates/mailServer/changeEmailPassword.html +++ b/mailServer/templates/mailServer/changeEmailPassword.html @@ -242,6 +242,16 @@ margin-right: 0.5rem; } + .password-hint { + color: var(--text-secondary, #64748b); + margin-top: 0.5rem; + display: block; + } + + .action-section { + margin-top: 2rem; + } + @keyframes spin { to { transform: rotate(360deg); } } @@ -324,7 +334,7 @@ {% trans "Select Website" %} - + {% trans "Choose a website..." %} {% for items in websiteList %} {{ items }} @@ -345,7 +355,7 @@ {% trans "Select Email Account" %} - + {% trans "Choose an email account..." %} {$ email.email $} @@ -374,19 +384,19 @@ {% trans "Generated Password" %} + ng-model="emailPassword" readonly aria-label="{% trans 'Generated Password' %}"> {% trans "Use This Password" %} - + {% trans "Make sure to save this password in a secure location" %} - + {% trans "Change Password" %} diff --git a/mailServer/templates/mailServer/createEmailAccount.html b/mailServer/templates/mailServer/createEmailAccount.html index b4cfbddf7..5f381679d 100644 --- a/mailServer/templates/mailServer/createEmailAccount.html +++ b/mailServer/templates/mailServer/createEmailAccount.html @@ -135,6 +135,7 @@ background: var(--bg-hover, #f8f9ff); border: 1px solid var(--border-color, #e8e9ff); border-radius: 8px; + min-width: -webkit-fill-available; min-width: fit-content; } @@ -304,6 +305,16 @@ width: 100%; background: var(--success-color, #10b981); } + + .password-hint { + color: var(--text-secondary, #64748b); + margin-top: 0.5rem; + display: block; + } + + .action-section { + margin-top: 2rem; + } @@ -320,7 +331,7 @@ {% trans "New Email Account" %} - {% trans "Test Email Delivery" %} @@ -346,7 +357,7 @@ {% trans "Select Website" %} - + {% trans "Choose a website..." %} {% for items in websiteList %} {{ items }} @@ -397,19 +408,19 @@ {% trans "Generated Password" %} + ng-model="emailPassword" readonly aria-label="{% trans 'Generated Password' %}"> {% trans "Use This Password" %} - + {% trans "Make sure to save this password in a secure location" %} - + {% trans "Create Email Account" %} diff --git a/mailServer/templates/mailServer/deleteEmailAccount.html b/mailServer/templates/mailServer/deleteEmailAccount.html index 3f52e36b3..f6ccf26ea 100644 --- a/mailServer/templates/mailServer/deleteEmailAccount.html +++ b/mailServer/templates/mailServer/deleteEmailAccount.html @@ -323,7 +323,7 @@ {% trans "Select Website" %} - + {% trans "Choose a website..." %} {% for items in websiteList %} {{ items }} @@ -339,7 +339,7 @@ {% trans "Select Email Account" %} - + {% trans "Choose an email account..." %} {$ email.email $} diff --git a/mailServer/templates/mailServer/dkimManager.html b/mailServer/templates/mailServer/dkimManager.html index a3e554444..4ed9b1c11 100644 --- a/mailServer/templates/mailServer/dkimManager.html +++ b/mailServer/templates/mailServer/dkimManager.html @@ -307,6 +307,50 @@ margin-right: 0.5rem; } + .install-icon { + color: #5b5fcf; + } + + .progress-title { + color: var(--text-primary, #1e293b); + font-size: 1.125rem; + font-weight: 600; + margin-bottom: 1rem; + } + + .terminal-pre { + margin: 0; + white-space: pre-wrap; + word-wrap: break-word; + } + + .table-domain { + width: 20%; + } + + .table-private-key { + width: 40%; + } + + .table-public-key { + width: 40%; + } + + .domain-badge { + padding: 0.5rem 1rem; + background: var(--accent-light, #e0e7ff); + color: #5b5fcf; + border-radius: 6px; + font-weight: 600; + display: inline-block; + } + + .key-hint { + color: var(--text-secondary, #64748b); + margin-top: 0.5rem; + display: block; + } + @keyframes spin { to { transform: rotate(360deg); } } @@ -359,7 +403,7 @@ {% trans "DKIM Manager" %} - + {% trans "DKIM Docs" %} @@ -378,7 +422,7 @@ - + {% trans "OpenDKIM is not installed" %} {% trans "OpenDKIM is required to manage DKIM keys for your domains" %} @@ -407,12 +451,12 @@ - + {% trans "Installation Progress" %} - + @@ -438,7 +482,7 @@ {% trans "Select Website" %} - + {% trans "Choose a website..." %} {% for items in websiteList %} {{ items }} @@ -465,29 +509,29 @@ - {% trans "Domain" %} - {% trans "Private Key" %} - {% trans "Public Key" %} + {% trans "Domain" %} + {% trans "Private Key" %} + {% trans "Public Key" %} - + - - + + {% trans "Keep this private key secure" %} - - + + {% trans "Add this as a TXT record in your DNS" %} diff --git a/mailServer/templates/mailServer/emailForwarding.html b/mailServer/templates/mailServer/emailForwarding.html index e65c2a0cd..1941724ec 100644 --- a/mailServer/templates/mailServer/emailForwarding.html +++ b/mailServer/templates/mailServer/emailForwarding.html @@ -277,6 +277,40 @@ display: inline-block; } + .warning-icon { + color: var(--warning-color, #ffa000); + } + + .table-id { + width: 10%; + } + + .table-source { + width: 35%; + } + + .table-destination { + width: 40%; + } + + .table-actions { + width: 15%; + text-align: center; + } + + .email-icon { + color: var(--accent-color, #5b5fcf); + margin-right: 0.5rem; + } + + .destination-text { + margin-left: 0.5rem; + } + + .full-width-btn { + width: 100%; + } + @keyframes spin { to { transform: rotate(360deg); } } @@ -329,7 +363,7 @@ {% trans "Setup Email Forwarding" %} - + {% trans "Forwarding Docs" %} @@ -348,7 +382,7 @@ {% if not status %} - + {% trans "Postfix is disabled" %} {% trans "You need to enable Postfix to setup email forwarding" %} @@ -363,7 +397,7 @@ {% trans "Select Website" %} - + {% trans "Choose a website..." %} {% for items in websiteList %} {{ items }} @@ -379,7 +413,7 @@ {% trans "Select Email Account" %} - + {% trans "Choose an email account..." %} {$ email.email $} @@ -387,7 +421,7 @@ {% trans "Forwarding Options" %} - + {% trans "Forward to email" %} {% trans "Pipe to program" %} @@ -402,21 +436,21 @@ {% trans "Source Email" %} - + {% trans "Destination" %} {% trans "(Path to program)" %} - + - + {% trans "Add Forwarding" %} @@ -427,17 +461,17 @@ - {% trans "ID" %} - {% trans "Source" %} - {% trans "Destination" %} - {% trans "Actions" %} + {% trans "ID" %} + {% trans "Source" %} + {% trans "Destination" %} + {% trans "Actions" %} - + @@ -447,9 +481,9 @@ Program - + - + {% trans "Delete" %} diff --git a/mailServer/templates/mailServer/listEmails.html b/mailServer/templates/mailServer/listEmails.html index 00397b9fe..34743b5f0 100644 --- a/mailServer/templates/mailServer/listEmails.html +++ b/mailServer/templates/mailServer/listEmails.html @@ -164,11 +164,9 @@ } /* Fix for Firefox on Windows */ - @-moz-document url-prefix() { - select.form-control { - color: var(--text-dark, #2f3640) !important; - text-shadow: none; - } + select.form-control { + color: var(--text-dark, #2f3640) !important; + text-shadow: none; } /* Ensure selected option is always visible */ @@ -397,6 +395,30 @@ flex-wrap: wrap; } + .email-icon { + color: var(--accent-color, #5b5fcf); + margin-right: 0.5rem; + } + + .email-address { + font-size: 0.9375rem; + color: var(--text-primary, #1e293b); + } + + .disk-usage-badge { + padding: 0.375rem 0.875rem; + background: var(--bg-gradient, #f0f1ff); + color: var(--accent-color, #5b5fcf); + border-radius: 4px; + font-weight: 600; + font-size: 0.875rem; + } + + .email-to-delete { + color: var(--danger-color, #ef4444); + font-weight: 600; + } + /* Modern Modal Styles */ .modal-content { border-radius: 12px; @@ -593,7 +615,7 @@ {% trans "Select Domain" %} - + {% trans "Choose a domain..." %} {% for items in websiteList %} {{ items }} @@ -613,7 +635,7 @@ {% trans "Learn more" %} - + {% trans "Fix SSL Now" %} @@ -731,11 +753,11 @@ - - + + - + {{ record.DiskUsage }} @@ -767,7 +789,7 @@ × Change Password - + @@ -775,7 +797,7 @@ {% trans "Email" %} - + @@ -784,7 +806,7 @@ {% trans "Password" %} - + @@ -837,7 +859,7 @@ {% trans "This will permanently delete the email account and all associated data including emails, folders, and settings." %} - {% trans "Email to delete:" %} {$ emailToDelete $} + {% trans "Email to delete:" %} {$ emailToDelete $} {% trans "Are you sure you want to continue?" %}
{% trans "OpenDKIM is required to manage DKIM keys for your domains" %}
{% trans "You need to enable Postfix to setup email forwarding" %}
{% trans "This will permanently delete the email account and all associated data including emails, folders, and settings." %}
{% trans "Email to delete:" %} {$ emailToDelete $}
{% trans "Are you sure you want to continue?" %}