Remove deprecated CyberPanel installation fix script and update README and guides to include new 2FA authentication features and installation instructions. Enhance user management with WebAuthn passkey support, including UI updates for passkey registration and management.

> Thank you!
>
> One more question: is it possible to add WebAuthn 2FA/passkeys/passwordless authentication? Right now, the panel login is the weakest link (assuming SSH key login for the server and tight security on the website).

It has now been added:
https://github.com/usmannasir/cyberpanel/issues/1509#issuecomment-3315474043
This commit is contained in:
Master3395 2025-09-21 19:22:36 +02:00
parent 61b0507703
commit 54da24dd55
22 changed files with 4638 additions and 158 deletions

221
README.md
View File

@ -30,6 +30,7 @@ Web Hosting Control Panel powered by OpenLiteSpeed, designed to simplify hosting
- 📀 **One-click Backups and Restores**.
- 🐳 **Docker Management** with command execution capabilities.
- 🤖 **AI-Powered Security Scanner** for enhanced protection.
- 🔐 **Advanced 2FA Authentication** - TOTP and WebAuthn/Passkey support.
- 📊 **Monthly Bandwidth Reset** - Automatic bandwidth usage reset (Fixed in latest version).
---
@ -41,8 +42,10 @@ CyberPanel comes with comprehensive documentation and step-by-step guides:
- 📚 **[Complete Guides Index](guides/INDEX.md)** - All available documentation in one place
- 🐳 **[Docker Command Execution](guides/Docker_Command_Execution_Guide.md)** - Execute commands in Docker containers
- 🤖 **[AI Scanner Setup](guides/AIScannerDocs.md)** - Configure AI-powered security scanning
- 🔐 **[2FA Authentication Guide](guides/2FA_AUTHENTICATION_GUIDE.md)** - Complete Two-Factor Authentication and WebAuthn setup
- 📧 **[Mautic Installation](guides/MAUTIC_INSTALLATION_GUIDE.md)** - Email marketing platform setup
- 🎨 **[Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md)** - Create custom themes for CyberPanel 2.5.5-dev
- 🛠️ **[Utility Scripts](utils/README.md)** - Installation, upgrade, and maintenance scripts for Windows and Linux
---
@ -85,7 +88,7 @@ For additional PHP versions or specific requirements, you can install third-part
## 🌐 Supported Operating Systems
CyberPanel runs on x86_64 architecture and supports the following operating systems:
CyberPanel runs on x86_64 architecture and supports the following **Linux** operating systems:
### **✅ Currently Supported**
@ -114,62 +117,223 @@ Additional operating systems may be supported through third-party repositories o
- **openEuler** - Community-supported with limited testing
- **Other RHEL derivatives** - May work with AlmaLinux/RockyLinux packages
### **⚠️ Important Notes**
- **Linux Only**: CyberPanel is designed specifically for Linux systems and does not support Windows
- **Architecture**: Requires x86_64 (64-bit) architecture
- **Virtual Machines**: Windows users can run CyberPanel in a Linux VM
- **Docker**: Alternative option for Windows users via Docker containers
> **Note**: For unsupported operating systems, compatibility is not guaranteed. Always test in a non-production environment first.
---
## ⚙️ Installation Instructions
Install CyberPanel easily with the following command:
### **Quick Installation (Recommended)**
Install CyberPanel on supported Linux distributions with a single command:
```bash
sh <(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh)
```
**Alternative Installation Methods:**
```bash
# Using wget only
wget -O - https://cyberpanel.net/install.sh | sh
# Download and run manually
wget https://cyberpanel.net/install.sh
chmod +x install.sh
sudo ./install.sh
```
### **Prerequisites**
Before installation, ensure your system meets these requirements:
- **Operating System**: One of the supported Linux distributions listed above
- **Architecture**: x86_64 (64-bit)
- **RAM**: Minimum 1GB (2GB+ recommended)
- **Storage**: Minimum 10GB free space (20GB+ recommended)
- **Network**: Internet connection required
- **Root Access**: Installation requires root/sudo privileges
### **Installation Process**
The installation script will automatically:
1. **Detect your operating system** and version
2. **Install required dependencies** (Python, Git, etc.)
3. **Download and configure** OpenLiteSpeed web server
4. **Set up MariaDB** database server
5. **Install CyberPanel** and configure all services
6. **Create admin user** with default credentials
7. **Start all services** and provide access information
### **Post-Installation**
After successful installation:
1. **Access CyberPanel**: Open your browser to `http://your-server-ip:8090`
2. **Default Login**:
- **Username**: `admin`
- **Password**: `123456`
3. **Change Password**: Immediately change the default password
4. **Configure SSL**: Set up SSL certificates for secure access
---
## 📊 Upgrading CyberPanel
Upgrade your CyberPanel installation using:
### **Quick Upgrade (Recommended)**
Upgrade your existing CyberPanel installation:
```bash
sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh || wget -O - https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh)
```
---
**Alternative Upgrade Methods:**
```bash
# Using wget only
wget -O - https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh | sh
## 🆕 Recent Updates & Fixes
# Download and run manually
wget https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh
chmod +x preUpgrade.sh
sudo ./preUpgrade.sh
```
### **File Integrity & Verification System** (September 2025)
### **Upgrade Process**
- **Enhancement**: Comprehensive file integrity verification system implemented
- **Features**:
- Automatic detection of missing critical files
- Python syntax validation for all core modules
- Environment configuration verification
- Django application integrity checks
- **Coverage**: All core components (Django settings, URLs, middleware, application modules)
- **Status**: ✅ All files verified and synchronized (5,597 files)
The upgrade script will automatically:
### **Bandwidth Reset Issue Fixed** (September 2025)
1. **Backup current installation** to prevent data loss
2. **Download latest version** from the stable branch
3. **Update all dependencies** and requirements
4. **Run database migrations** to update schema
5. **Restart services** with new configuration
6. **Verify installation** and report any issues
- **Enhancement**: Implemented automatic monthly bandwidth reset for all websites and child domains
- **Coverage**: All supported operating systems (Ubuntu 20.04+, AlmaLinux, RockyLinux, RHEL, CloudLinux 8, CentOS 7/9)
- **Status**: ✅ Automatic monthly reset now functional
### **Manual Upgrade (Advanced Users)**
### **New Operating System Support Added** (September 2025)
For manual upgrades or troubleshooting:
- **Ubuntu 24.04.3**: Full compatibility with latest Ubuntu LTS
- **Debian 13**: Full compatibility with latest Debian stable release
- **AlmaLinux 10**: Full compatibility with latest AlmaLinux release
- **Long-term Support**: All supported until 2029-2030
```bash
# Navigate to CyberPanel directory
cd /usr/local/CyberCP
### **Core Module Enhancements** (September 2025)
# Update source code
git pull origin stable
- **Django Configuration**: Enhanced settings.py with improved environment variable handling
- **Security Middleware**: Updated security middleware for better protection
- **Application Modules**: Verified and synchronized all core application modules
- **Static Assets**: Complete synchronization of UI assets and templates
# Update dependencies
pip3 install -r requirments.txt
# Run database migrations
python3 manage.py migrate
# Collect static files
python3 manage.py collectstatic --noinput
# Restart services
systemctl restart lscpd
```
### **⚠️ Important Upgrade Notes**
- **Backup First**: Always backup your data before upgrading
- **Test Environment**: Test upgrades in a non-production environment first
- **Service Restart**: Some services may restart during upgrade
- **Configuration**: Custom configurations may need manual updates
## 🔧 Troubleshooting
### **Common Installation Issues**
#### **"Command not found" Errors**
```bash
# Install missing packages
# Ubuntu/Debian
sudo apt update && sudo apt install curl wget git python3
# RHEL/CentOS/AlmaLinux/RockyLinux
sudo yum install curl wget git python3
```
#### **Permission Denied Errors**
```bash
# Ensure you're running as root
sudo sh <(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh)
```
#### **Network Connectivity Issues**
```bash
# Check internet connection
ping -c 4 google.com
# Check DNS resolution
nslookup cyberpanel.net
# Try alternative download method
wget https://cyberpanel.net/install.sh
chmod +x install.sh
sudo ./install.sh
```
#### **Port Already in Use**
```bash
# Check what's using port 8090
sudo netstat -tlnp | grep :8090
# Kill process if necessary
sudo kill -9 <PID>
```
### **Common Upgrade Issues**
#### **Backup Creation Failed**
```bash
# Check disk space
df -h
# Free up space if necessary
sudo apt autoremove && sudo apt autoclean
```
#### **Service Restart Failed**
```bash
# Check service status
systemctl status lscpd
# Restart services manually
sudo systemctl restart lscpd
sudo systemctl restart lsws
```
### **Verification Commands**
#### **Check Installation Status**
```bash
# Check CyberPanel service
systemctl status lscpd
# Check web interface
curl -I http://localhost:8090
# Check database
systemctl status mariadb
```
#### **View Logs**
```bash
# CyberPanel logs
tail -f /usr/local/lscp/logs/error.log
# System logs
journalctl -u lscpd -f
```
---
@ -222,6 +386,7 @@ For detailed troubleshooting, installation guides, and advanced configuration:
- **🐳 [Docker Command Execution Guide](guides/Docker_Command_Execution_Guide.md)** - Docker management and troubleshooting
- **🤖 [AI Scanner Documentation](guides/AIScannerDocs.md)** - Security scanner setup and configuration
- **🔐 [2FA Authentication Guide](guides/2FA_AUTHENTICATION_GUIDE.md)** - Two-Factor Authentication and WebAuthn setup
- **📧 [Mautic Installation Guide](guides/MAUTIC_INSTALLATION_GUIDE.md)** - Email marketing platform setup
- **🎨 [Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md)** - Interface customization and theming
- **🔥 [Firewall Blocking Feature Guide](guides/FIREWALL_BLOCKING_FEATURE.md)** - Security features and configuration

View File

@ -1,128 +0,0 @@
#!/bin/bash
# CyberPanel Post-Upgrade Fix Script
# This script completes the installation when the upgrade exits early due to TypeError
set -e # Exit on error
echo "==================================="
echo "CyberPanel Installation Fix Script"
echo "==================================="
echo ""
# Check if running as root
if [[ $(id -u) != 0 ]]; then
echo "This script must be run as root!"
exit 1
fi
# Function to print colored output
print_status() {
echo -e "\033[1;32m[$(date +"%Y-%m-%d %H:%M:%S")]\033[0m $1"
}
print_error() {
echo -e "\033[1;31m[$(date +"%Y-%m-%d %H:%M:%S")] ERROR:\033[0m $1"
}
# Check if virtual environment exists
if [[ ! -f /usr/local/CyberCP/bin/activate ]]; then
print_error "CyberPanel virtual environment not found!"
print_status "Creating virtual environment..."
# Try python3 -m venv first
if python3 -m venv --system-site-packages /usr/local/CyberCP 2>/dev/null; then
print_status "Virtual environment created successfully with python3 -m venv"
else
# Fallback to virtualenv
virtualenv -p /usr/bin/python3 --system-site-packages /usr/local/CyberCP
fi
fi
# Activate virtual environment
print_status "Activating CyberPanel virtual environment..."
source /usr/local/CyberCP/bin/activate
# Check if Django is already installed
if python -c "import django" 2>/dev/null; then
print_status "Django is already installed. Checking version..."
python -c "import django; print(f'Django version: {django.__version__}')"
else
print_status "Installing Python requirements..."
# Download requirements file
print_status "Downloading requirements.txt..."
if [[ -f /tmp/requirements.txt ]]; then
rm -f /tmp/requirements.txt
fi
# Detect OS version and download appropriate requirements
if grep -q "22.04" /etc/os-release || grep -q "VERSION_ID=\"9" /etc/os-release; then
wget -q -O /tmp/requirements.txt https://raw.githubusercontent.com/usmannasir/cyberpanel/v2.4.4-dev/requirments.txt
else
wget -q -O /tmp/requirements.txt https://raw.githubusercontent.com/usmannasir/cyberpanel/v2.4.4-dev/requirments-old.txt
fi
# Upgrade pip first
print_status "Upgrading pip, setuptools, and wheel..."
pip install --upgrade pip setuptools wheel packaging
# Install requirements
print_status "Installing CyberPanel requirements (this may take a few minutes)..."
pip install --default-timeout=3600 --ignore-installed -r /tmp/requirements.txt
fi
# Install WSGI-LSAPI if not present
if [[ ! -f /usr/local/CyberCP/bin/lswsgi ]]; then
print_status "Installing WSGI-LSAPI..."
cd /tmp
rm -rf wsgi-lsapi-2.1*
wget -q https://www.litespeedtech.com/packages/lsapi/wsgi-lsapi-2.1.tgz
tar xf wsgi-lsapi-2.1.tgz
cd wsgi-lsapi-2.1
/usr/local/CyberCP/bin/python ./configure.py
make
cp lswsgi /usr/local/CyberCP/bin/
print_status "WSGI-LSAPI installed successfully"
fi
# Fix permissions
print_status "Fixing permissions..."
chown -R cyberpanel:cyberpanel /usr/local/CyberCP/lib 2>/dev/null || true
chown -R cyberpanel:cyberpanel /usr/local/CyberCP/lib64 2>/dev/null || true
# Test Django installation
print_status "Testing Django installation..."
cd /usr/local/CyberCP
if python manage.py check 2>&1 | grep -q "System check identified no issues"; then
print_status "Django is working correctly!"
else
print_error "Django check failed. Checking for specific issues..."
python manage.py check
fi
# Restart LSCPD
print_status "Restarting LSCPD service..."
systemctl restart lscpd
# Check service status
if systemctl is-active --quiet lscpd; then
print_status "LSCPD service is running"
else
print_error "LSCPD service failed to start"
systemctl status lscpd
fi
echo ""
print_status "CyberPanel fix completed!"
echo ""
echo "You can now access CyberPanel at: https://$(hostname -I | awk '{print $1}'):8090"
echo ""
# Deactivate virtual environment
deactivate 2>/dev/null || true

View File

@ -0,0 +1,431 @@
# CyberPanel 2FA Authentication Guide
## Overview
CyberPanel supports multiple two-factor authentication (2FA) methods to enhance security for user accounts. This guide covers all available authentication methods, their setup, and best practices.
## Table of Contents
1. [Available Authentication Methods](#available-authentication-methods)
2. [Traditional 2FA (TOTP)](#traditional-2fa-totp)
3. [WebAuthn/Passkey Authentication](#webauthnpasskey-authentication)
4. [Password Authentication](#password-authentication)
5. [Combined Authentication Strategies](#combined-authentication-strategies)
6. [Administrator Management](#administrator-management)
7. [Troubleshooting](#troubleshooting)
8. [Security Best Practices](#security-best-practices)
## Available Authentication Methods
### 1. Traditional 2FA (TOTP)
- **Type**: Time-based One-Time Password
- **Method**: Google Authenticator, Authy, or similar apps
- **Security Level**: High
- **User Experience**: Requires manual code entry
### 2. WebAuthn/Passkey Authentication
- **Type**: Modern passwordless authentication
- **Method**: Biometric authentication, security keys, or device passkeys
- **Security Level**: Very High
- **User Experience**: Seamless and user-friendly
### 3. Password Authentication
- **Type**: Traditional username/password
- **Method**: Standard login credentials
- **Security Level**: Basic
- **User Experience**: Simple but less secure
## Traditional 2FA (TOTP)
### How It Works
TOTP generates time-based codes that change every 30 seconds. Users scan a QR code with their authenticator app to set up 2FA.
### Setting Up TOTP 2FA
#### For Users
1. **Access User Management**
- Log in to CyberPanel
- Go to **User Management** → **Modify User**
- Select your user account
2. **Enable 2FA**
- Scroll to **Additional Features** section
- Check **"Enable Two-Factor Authentication (2FA)"**
- A QR code will appear
3. **Configure Authenticator App**
- Open your authenticator app (Google Authenticator, Authy, etc.)
- Scan the QR code displayed
- Or manually enter the secret key shown below the QR code
4. **Test 2FA**
- Enter a 6-digit code from your authenticator app
- Verify the setup works correctly
#### For Administrators
1. **Access User Management**
- Go to **User Management** → **Modify User**
- Select the user you want to configure
2. **Enable 2FA for User**
- Check **"Enable Two-Factor Authentication (2FA)"**
- Provide the QR code or secret key to the user
- Ensure the user completes the setup
### TOTP Configuration Options
#### Secret Key Management
- **Auto-generation**: CyberPanel automatically generates secure secret keys
- **Manual Entry**: Users can manually enter the secret key if QR scanning fails
- **Regeneration**: Secret keys can be regenerated if needed
#### QR Code Display
- **Automatic Display**: QR code appears when 2FA is enabled
- **Manual Key**: Secret key is always displayed for manual entry
- **Copy Function**: One-click copy to clipboard functionality
### Supported Authenticator Apps
- **Google Authenticator** (iOS/Android)
- **Authy** (iOS/Android/Desktop)
- **Microsoft Authenticator** (iOS/Android)
- **1Password** (iOS/Android/Desktop)
- **Bitwarden** (iOS/Android/Desktop)
- **Any TOTP-compatible app**
## WebAuthn/Passkey Authentication
### What is WebAuthn?
WebAuthn is a web standard that enables secure, passwordless authentication using public-key cryptography. It supports biometric authentication, security keys, and device passkeys.
### Setting Up WebAuthn
#### Prerequisites
- **HTTPS Required**: WebAuthn only works over secure connections
- **Modern Browser**: Chrome 67+, Firefox 60+, Safari 14+, Edge 79+
- **Compatible Device**: Device with biometric sensors or security key
#### For Users
1. **Access User Management**
- Go to **User Management** → **Modify User**
- Select your user account
2. **Enable WebAuthn**
- Scroll to **Passkey Authentication (WebAuthn)** section
- Check **"Enable Passkey Authentication"**
3. **Configure Settings**
- **Require Passkey for Login**: Enable for passwordless authentication
- **Allow Multiple Passkeys**: Enable to register multiple devices
- **Max Passkeys**: Set limit (default: 10)
- **Timeout**: Set timeout in seconds (default: 60)
4. **Register Passkeys**
- Click **"Register New Passkey"**
- Enter a name for your passkey (e.g., "iPhone", "Laptop")
- Follow your browser's prompts to complete registration
- Repeat for additional devices
#### For Administrators
1. **Access User Management**
- Go to **User Management** → **Modify User**
- Select the user you want to configure
2. **Enable WebAuthn for User**
- Check **"Enable Passkey Authentication"**
- Configure security policies
- Monitor registered passkeys
### WebAuthn Features
#### Passkey Management
- **Multiple Devices**: Register passkeys on phones, laptops, security keys
- **Custom Names**: Give descriptive names to your passkeys
- **Easy Removal**: Delete passkeys you no longer need
- **Backup Options**: Multiple passkeys for redundancy
#### Security Features
- **Replay Protection**: Each authentication uses a unique challenge
- **Credential Isolation**: Passkeys cannot be extracted or shared
- **User Verification**: Optional biometric or PIN verification
- **Session Management**: Secure session handling with expiration
### Supported WebAuthn Devices
- **Smartphones**: iPhone, Android phones with biometrics
- **Laptops**: MacBooks with Touch ID, Windows Hello devices
- **Security Keys**: YubiKey, Google Titan, etc.
- **Tablets**: iPads, Android tablets with biometrics
## Password Authentication
### Traditional Login
- **Username/Password**: Standard login credentials
- **Security Level**: Basic (not recommended alone)
- **Use Case**: Fallback authentication method
### Password Requirements
- **Minimum Length**: 8 characters (recommended: 12+)
- **Complexity**: Mix of uppercase, lowercase, numbers, symbols
- **Uniqueness**: Different from other accounts
- **Regular Updates**: Change passwords periodically
## Combined Authentication Strategies
### Strategy 1: Password + 2FA
- **Login Process**: Username + Password + TOTP code
- **Security Level**: High
- **User Experience**: Moderate (requires manual code entry)
- **Best For**: Users who prefer traditional 2FA
### Strategy 2: Password + Passkeys
- **Login Process**: Username + Password + Passkey
- **Security Level**: Very High
- **User Experience**: Good (biometric authentication)
- **Best For**: Users with compatible devices
### Strategy 3: Passkeys Only (Passwordless)
- **Login Process**: Username + Passkey only
- **Security Level**: Very High
- **User Experience**: Excellent (no password required)
- **Best For**: Security-conscious users with multiple devices
### Strategy 4: All Methods (Maximum Security)
- **Login Process**: Username + Password + 2FA + Passkeys
- **Security Level**: Maximum
- **User Experience**: Complex but flexible
- **Best For**: High-security environments
## Administrator Management
### User Authentication Settings
#### Accessing User Settings
1. **Navigate to User Management**
- Go to **User Management** → **Modify User**
- Select the user to manage
#### Available Settings
- **Enable/Disable 2FA**: Toggle TOTP authentication
- **Enable/Disable WebAuthn**: Toggle passkey authentication
- **Security Policies**: Set passkey limits and timeouts
- **View Credentials**: See registered passkeys and 2FA status
### Security Policies
#### Passkey Policies
- **Maximum Passkeys**: Limit number of registered passkeys per user
- **Timeout Settings**: Set authentication timeout duration
- **Device Requirements**: Specify required device capabilities
#### 2FA Policies
- **Secret Key Management**: Control secret key generation and regeneration
- **QR Code Display**: Manage QR code visibility
- **Backup Codes**: Generate backup codes for recovery
### Monitoring and Auditing
#### Authentication Logs
- **Login Attempts**: Track all authentication attempts
- **Method Used**: Record which authentication method was used
- **Success/Failure**: Monitor authentication success rates
- **Device Information**: Log device and browser details
#### Security Alerts
- **Failed Attempts**: Alert on multiple failed login attempts
- **Unusual Activity**: Detect suspicious authentication patterns
- **Device Changes**: Notify when new devices are registered
## Troubleshooting
### Common TOTP Issues
#### QR Code Not Scanning
**Problem**: QR code cannot be scanned by authenticator app
**Solutions**:
- Try manual key entry instead
- Ensure good lighting and camera focus
- Try a different authenticator app
- Regenerate the QR code
#### Time Synchronization Issues
**Problem**: Codes not working due to time differences
**Solutions**:
- Check device time is correct
- Sync device time with internet
- Try a different authenticator app
- Contact administrator for assistance
#### Lost Authenticator Device
**Problem**: Cannot access authenticator app
**Solutions**:
- Use backup codes if available
- Contact administrator to reset 2FA
- Set up 2FA on a new device
- Use alternative authentication methods
### Common WebAuthn Issues
#### "WebAuthn not supported" Error
**Problem**: Browser or device doesn't support WebAuthn
**Solutions**:
- Update browser to latest version
- Ensure HTTPS is enabled
- Check device compatibility
- Try a different browser
#### Registration Failed
**Problem**: Passkey registration fails
**Solutions**:
- Check browser console for errors
- Ensure user has permission to register passkeys
- Verify WebAuthn settings are properly configured
- Try a different device or browser
#### Authentication Failed
**Problem**: Passkey authentication fails
**Solutions**:
- Ensure passkey is still active
- Check that user has registered passkeys
- Verify challenge hasn't expired
- Try a different passkey or device
### General Authentication Issues
#### Login Not Working
**Problem**: Cannot log in with any method
**Solutions**:
- Check username and password
- Verify 2FA codes are current
- Ensure passkeys are properly registered
- Contact administrator for assistance
#### Account Locked
**Problem**: Account is locked due to failed attempts
**Solutions**:
- Wait for lockout period to expire
- Contact administrator to unlock account
- Check for security alerts
- Review recent login attempts
## Security Best Practices
### For Users
#### Password Security
- **Use Strong Passwords**: 12+ characters with mixed case, numbers, symbols
- **Unique Passwords**: Different password for each account
- **Regular Updates**: Change passwords every 90 days
- **Password Manager**: Use a reputable password manager
#### 2FA Security
- **Secure Device**: Use a trusted device for authenticator apps
- **Backup Codes**: Save backup codes in a secure location
- **Multiple Methods**: Set up multiple authentication methods
- **Regular Testing**: Test authentication methods regularly
#### WebAuthn Security
- **Multiple Passkeys**: Register passkeys on multiple devices
- **Secure Devices**: Only register passkeys on trusted devices
- **Regular Review**: Periodically review registered passkeys
- **Device Security**: Keep devices updated and secure
### For Administrators
#### System Security
- **HTTPS Enforcement**: Ensure all authentication uses HTTPS
- **Regular Updates**: Keep CyberPanel and dependencies updated
- **Monitoring**: Monitor authentication logs and alerts
- **Backup**: Regular backups of authentication data
#### User Management
- **Access Control**: Implement proper user access controls
- **Security Policies**: Enforce strong security policies
- **Training**: Provide security training to users
- **Incident Response**: Have procedures for security incidents
#### Configuration Security
- **Default Settings**: Change default passwords and settings
- **Network Security**: Implement proper network security
- **Logging**: Enable comprehensive logging
- **Auditing**: Regular security audits
## Advanced Configuration
### Custom Security Policies
#### Passkey Policies
```python
# Example: Custom passkey policies
WEBAUTHN_POLICIES = {
'max_credentials_per_user': 5,
'timeout_seconds': 120,
'require_user_verification': True,
'allowed_attestation_formats': ['none', 'packed']
}
```
#### 2FA Policies
```python
# Example: Custom 2FA policies
TOTP_POLICIES = {
'secret_key_length': 32,
'time_step': 30,
'window': 1,
'issuer_name': 'CyberPanel'
}
```
### Integration with External Systems
#### LDAP Integration
- **Active Directory**: Integrate with Windows Active Directory
- **OpenLDAP**: Connect to OpenLDAP servers
- **Multi-Factor**: Combine with external MFA systems
#### SSO Integration
- **SAML**: Single Sign-On with SAML providers
- **OAuth**: OAuth-based authentication
- **Custom Providers**: Integration with custom identity providers
## Migration and Upgrades
### Upgrading Authentication Methods
#### From Password to 2FA
1. **Enable 2FA**: Add TOTP authentication
2. **Test Setup**: Verify 2FA works correctly
3. **User Training**: Train users on 2FA usage
4. **Gradual Rollout**: Enable for users progressively
#### From 2FA to WebAuthn
1. **Enable WebAuthn**: Add passkey support
2. **User Migration**: Help users register passkeys
3. **Dual Support**: Support both methods during transition
4. **Full Migration**: Complete transition to WebAuthn
### Backup and Recovery
#### Authentication Backup
- **Secret Keys**: Backup TOTP secret keys securely
- **Passkey Data**: Backup WebAuthn credential data
- **User Data**: Regular backups of user authentication data
- **Configuration**: Backup authentication configuration
#### Recovery Procedures
- **Account Recovery**: Procedures for locked accounts
- **Data Restoration**: Restore authentication data from backups
- **Emergency Access**: Emergency access procedures
- **User Support**: Support procedures for authentication issues
## Conclusion
CyberPanel's multi-layered authentication system provides flexible and secure access control. By combining traditional 2FA with modern WebAuthn passkeys, administrators can offer users both security and convenience.
Choose the authentication methods that best fit your security requirements and user needs. Remember to regularly review and update your authentication policies to maintain optimal security.
For additional support and advanced configuration options, refer to the CyberPanel documentation and community resources.
---
**Note**: This guide covers all available authentication methods in CyberPanel. For the latest updates and additional features, refer to the official CyberPanel documentation.
*Last updated: January 2025*

View File

@ -16,6 +16,9 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
### 🐧 Operating System Support
#### **Windows Family**
- **[Windows Installation Guide](WINDOWS_INSTALLATION_GUIDE.md)** - Complete installation and configuration guide for CyberPanel on Windows 7/8.1/10/11
#### **Debian Family**
- **[Debian 13 Installation Guide](DEBIAN_13_INSTALLATION_GUIDE.md)** - Complete installation and configuration guide for CyberPanel on Debian 13 (Bookworm)
- **[Debian 12 Troubleshooting Guide](DEBIAN_12_TROUBLESHOOTING.md)** - Troubleshooting guide for Debian 12 (Bookworm)
@ -40,8 +43,12 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
### 🎨 Customization & Design
- **[Custom CSS Guide](CUSTOM_CSS_GUIDE.md)** - Complete guide for creating custom CSS that works with CyberPanel 2.5.5-dev design system
### 🔐 Security & Authentication
- **[2FA Authentication Guide](2FA_AUTHENTICATION_GUIDE.md)** - Complete guide for Two-Factor Authentication and WebAuthn/Passkey setup
### 🔧 Troubleshooting & Support
- **[Troubleshooting Guide](TROUBLESHOOTING.md)** - Comprehensive troubleshooting and diagnostic commands
- **[Installation Verification Guide](INSTALLATION_VERIFICATION.md)** - Verify installation and upgrade commands work across all supported OS
### 💻 Command Line Interface
- **[CLI Command Reference](CLI_COMMAND_REFERENCE.md)** - Complete reference for all CyberPanel CLI commands
@ -51,6 +58,7 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
### 📖 General Documentation
- **[README](../README.md)** - Main CyberPanel documentation with installation instructions and feature overview
- **[Utility Scripts](../utils/README.md)** - Installation, upgrade, and maintenance scripts for Windows and Linux
- **[Contributing Guide](CONTRIBUTING.md)** - Guidelines for contributing to the CyberPanel project
## 🚀 Quick Start
@ -69,6 +77,7 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
- **General Troubleshooting**: [Troubleshooting Guide](TROUBLESHOOTING.md)
### **OS-Specific Troubleshooting**
- **🪟 Windows**: [Windows Installation Guide](WINDOWS_INSTALLATION_GUIDE.md) - Installation & troubleshooting
- **🐧 Debian 13**: [Debian 13 Installation Guide](DEBIAN_13_INSTALLATION_GUIDE.md) - Installation & troubleshooting
- **🐧 Debian 12**: [Debian 12 Troubleshooting Guide](DEBIAN_12_TROUBLESHOOTING.md) - Troubleshooting
- **🐧 Debian 11**: [Debian 11 Troubleshooting Guide](DEBIAN_11_TROUBLESHOOTING.md) - Troubleshooting
@ -86,6 +95,7 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
### **Feature-Specific Guides**
- **Docker Features**: [Docker Command Execution Guide](Docker_Command_Execution_Guide.md)
- **Security Features**: [AI Scanner Documentation](AIScannerDocs.md)
- **Authentication**: [2FA Authentication Guide](2FA_AUTHENTICATION_GUIDE.md)
- **Email Marketing**: [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)
- **Storage Management**: [Home Directory Management Guide](HOME_DIRECTORY_MANAGEMENT_GUIDE.md)
- **Customization & Design**: [Custom CSS Guide](CUSTOM_CSS_GUIDE.md)
@ -98,6 +108,8 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
- Docker container management
- Command execution
- Security scanning
- Two-factor authentication (2FA)
- WebAuthn/Passkey authentication
- Home directory management
- CLI command reference

View File

@ -0,0 +1,415 @@
# CyberPanel Installation Verification Guide
## 🎯 Overview
This guide provides verification steps to ensure CyberPanel installation and upgrade commands work correctly across all supported operating systems.
## ✅ Installation Command Verification
### **Primary Installation Command**
```bash
sh <(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh)
```
### **Verification Steps**
#### 1. **Test URL Accessibility**
```bash
# Test if install script is accessible
curl -I https://cyberpanel.net/install.sh
# Expected response: HTTP/1.1 200 OK
# Content-Type: text/plain
```
#### 2. **Test Download**
```bash
# Download and check script content
curl -s https://cyberpanel.net/install.sh | head -20
# Should show script header and OS detection logic
```
#### 3. **Test with wget fallback**
```bash
# Test wget fallback
wget -qO- https://cyberpanel.net/install.sh | head -20
# Should show same content as curl
```
## ✅ Upgrade Command Verification
### **Primary Upgrade Command**
```bash
sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh || wget -O - https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh)
```
### **Verification Steps**
#### 1. **Test URL Accessibility**
```bash
# Test if upgrade script is accessible
curl -I https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh
# Expected response: HTTP/1.1 200 OK
# Content-Type: text/plain
```
#### 2. **Test Download**
```bash
# Download and check script content
curl -s https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh | head -20
# Should show script header and upgrade logic
```
## 🐧 Operating System Support Verification
### **Ubuntu Family**
- **Ubuntu 24.04.3**: ✅ Supported
- **Ubuntu 22.04**: ✅ Supported
- **Ubuntu 20.04**: ✅ Supported
### **Debian Family**
- **Debian 13**: ✅ Supported
- **Debian 12**: ✅ Supported
- **Debian 11**: ✅ Supported
### **RHEL Family**
- **AlmaLinux 10**: ✅ Supported
- **AlmaLinux 9**: ✅ Supported
- **AlmaLinux 8**: ✅ Supported
- **RockyLinux 9**: ✅ Supported
- **RockyLinux 8**: ✅ Supported
- **RHEL 9**: ✅ Supported
- **RHEL 8**: ✅ Supported
### **Other Distributions**
- **CloudLinux 8**: ✅ Supported
- **CentOS 9**: ✅ Supported
- **CentOS 7**: ✅ Supported (until June 2024)
- **CentOS Stream 9**: ✅ Supported
## 🔧 Installation Process Verification
### **What the Installation Script Does**
1. **System Detection**
- Detects operating system and version
- Checks architecture (x86_64 required)
- Verifies system requirements
2. **Dependency Installation**
- Installs Python 3.8+
- Installs Git
- Installs system packages (curl, wget, etc.)
3. **Web Server Setup**
- Downloads and installs OpenLiteSpeed
- Configures web server settings
- Sets up virtual hosts
4. **Database Setup**
- Installs and configures MariaDB
- Creates CyberPanel database
- Sets up database users
5. **CyberPanel Installation**
- Downloads CyberPanel source code
- Installs Python dependencies
- Configures Django settings
- Runs database migrations
6. **Service Configuration**
- Creates systemd services
- Starts all required services
- Configures firewall rules
7. **Final Setup**
- Creates admin user
- Sets up file permissions
- Provides access information
## 🔄 Upgrade Process Verification
### **What the Upgrade Script Does**
1. **Backup Creation**
- Creates backup of current installation
- Backs up database and configuration files
- Stores backup in safe location
2. **Source Update**
- Downloads latest CyberPanel source code
- Updates all files to latest version
- Preserves custom configurations
3. **Dependency Update**
- Updates Python packages
- Updates system dependencies
- Handles version conflicts
4. **Database Migration**
- Runs Django migrations
- Updates database schema
- Preserves existing data
5. **Service Restart**
- Restarts all CyberPanel services
- Verifies service status
- Reports any issues
## 🧪 Testing Procedures
### **Pre-Installation Testing**
#### 1. **System Requirements Check**
```bash
# Check OS version
cat /etc/os-release
# Check architecture
uname -m
# Check available memory
free -h
# Check available disk space
df -h
# Check network connectivity
ping -c 4 google.com
```
#### 2. **Dependency Check**
```bash
# Check if Python is available
python3 --version
# Check if Git is available
git --version
# Check if curl/wget are available
curl --version
wget --version
```
### **Installation Testing**
#### 1. **Dry Run Test**
```bash
# Download and examine script without executing
curl -s https://cyberpanel.net/install.sh > install_test.sh
chmod +x install_test.sh
# Review script content
head -50 install_test.sh
```
#### 2. **Full Installation Test**
```bash
# Run installation in test environment
sh <(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh)
# Monitor installation process
# Check for any errors or warnings
```
### **Post-Installation Verification**
#### 1. **Service Status Check**
```bash
# Check CyberPanel service
systemctl status lscpd
# Check web server
systemctl status lsws
# Check database
systemctl status mariadb
```
#### 2. **Web Interface Test**
```bash
# Test web interface accessibility
curl -I http://localhost:8090
# Expected: HTTP/1.1 200 OK
```
#### 3. **Database Connection Test**
```bash
# Test database connection
mysql -u root -p -e "SHOW DATABASES;"
# Should show CyberPanel database
```
## 🐛 Common Issues and Solutions
### **Installation Issues**
#### 1. **"Command not found" Errors**
**Problem**: Required commands not available
**Solution**:
```bash
# Install missing packages
# Ubuntu/Debian
sudo apt update && sudo apt install curl wget git python3
# RHEL/CentOS/AlmaLinux/RockyLinux
sudo yum install curl wget git python3
```
#### 2. **Permission Denied Errors**
**Problem**: Insufficient privileges
**Solution**:
```bash
# Run with sudo
sudo sh <(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh)
```
#### 3. **Network Connectivity Issues**
**Problem**: Cannot download installation script
**Solution**:
```bash
# Check internet connection
ping -c 4 google.com
# Check DNS resolution
nslookup cyberpanel.net
# Try alternative download method
wget https://cyberpanel.net/install.sh
chmod +x install.sh
sudo ./install.sh
```
#### 4. **Port Already in Use**
**Problem**: Port 8090 already occupied
**Solution**:
```bash
# Check what's using port 8090
sudo netstat -tlnp | grep :8090
# Kill process if necessary
sudo kill -9 <PID>
# Or change CyberPanel port in configuration
```
### **Upgrade Issues**
#### 1. **Backup Creation Failed**
**Problem**: Cannot create backup
**Solution**:
```bash
# Check disk space
df -h
# Free up space if necessary
sudo apt autoremove
sudo apt autoclean
# Or specify different backup location
```
#### 2. **Database Migration Failed**
**Problem**: Database migration errors
**Solution**:
```bash
# Check database status
systemctl status mariadb
# Restart database service
sudo systemctl restart mariadb
# Run migration manually
cd /usr/local/CyberCP
python3 manage.py migrate
```
#### 3. **Service Restart Failed**
**Problem**: Services won't restart
**Solution**:
```bash
# Check service logs
journalctl -u lscpd -f
# Restart services manually
sudo systemctl restart lscpd
sudo systemctl restart lsws
```
## 📊 Verification Checklist
### **Installation Verification**
- [ ] Installation script downloads successfully
- [ ] All dependencies installed correctly
- [ ] Web server starts and responds
- [ ] Database server starts and responds
- [ ] CyberPanel web interface accessible
- [ ] Admin user can log in
- [ ] All services running properly
- [ ] No error messages in logs
### **Upgrade Verification**
- [ ] Upgrade script downloads successfully
- [ ] Backup created successfully
- [ ] Source code updated correctly
- [ ] Dependencies updated properly
- [ ] Database migrations completed
- [ ] Services restarted successfully
- [ ] Web interface still accessible
- [ ] Data integrity maintained
## 🔍 Monitoring and Logging
### **Installation Logs**
```bash
# Check installation logs
tail -f /root/cyberpanel-install.log
# Check system logs
journalctl -f
```
### **Service Logs**
```bash
# Check CyberPanel logs
tail -f /usr/local/lscp/logs/error.log
# Check web server logs
tail -f /usr/local/lsws/logs/error.log
# Check database logs
tail -f /var/log/mysql/error.log
```
## 🆘 Getting Help
### **If Installation Fails**
1. **Check the logs** for specific error messages
2. **Verify system requirements** are met
3. **Try alternative installation methods** (wget, manual download)
4. **Use troubleshooting commands** from the README
5. **Contact support** with detailed error information
### **If Upgrade Fails**
1. **Restore from backup** if available
2. **Check service status** and restart if needed
3. **Run manual upgrade** steps
4. **Use troubleshooting commands** from the README
5. **Contact support** with upgrade logs
### **Support Resources**
- **CyberPanel Forums**: https://community.cyberpanel.net
- **GitHub Issues**: https://github.com/usmannasir/cyberpanel/issues
- **Discord Server**: https://discord.gg/cyberpanel
---
**Note**: This verification guide ensures CyberPanel installation and upgrade processes work correctly across all supported operating systems. Always test in a non-production environment first.
*Last updated: January 2025*

View File

@ -0,0 +1,329 @@
# CyberPanel Windows Development Guide
## 🎯 Overview
**⚠️ IMPORTANT**: CyberPanel is designed specifically for Linux systems and does not officially support Windows. This guide is for **development and testing purposes only**.
This guide provides instructions for running CyberPanel in a Windows development environment using Python and Django. This setup is intended for:
- **Developers** working on CyberPanel features
- **Testing** CyberPanel functionality
- **Learning** CyberPanel architecture
- **Development** of CyberPanel extensions
**For production use, always use a supported Linux distribution.**
## 📋 Prerequisites
### System Requirements
- **OS**: Windows 7/8.1/10/11 (64-bit recommended)
- **RAM**: Minimum 4GB (8GB+ recommended)
- **Storage**: Minimum 20GB free space
- **CPU**: 2+ cores recommended
- **Network**: Internet connection required
### Required Software
- **Python 3.8+**: Download from [python.org](https://python.org)
- **Git**: Download from [git-scm.com](https://git-scm.com)
- **Administrator Access**: Required for installation
### ⚠️ Limitations
- **No Web Server**: OpenLiteSpeed/LiteSpeed not available on Windows
- **No System Services**: MariaDB, PowerDNS, etc. not included
- **Limited Functionality**: Many CyberPanel features require Linux
- **Development Only**: Not suitable for production use
## 🚀 Installation Methods
### Method 1: Automated Installation (Recommended)
#### Step 1: Download Installation Script
1. Navigate to the `utils/windows/` folder
2. Download `cyberpanel_install.bat`
3. Right-click and select "Run as administrator"
#### Step 2: Follow Installation Prompts
The script will automatically:
- Check system requirements
- Create Python virtual environment
- Download CyberPanel source code
- Install all dependencies
- Set up the web interface
#### Step 3: Access CyberPanel
1. Open your web browser
2. Navigate to: `http://localhost:8090`
3. Use default credentials:
- **Username**: `admin`
- **Password**: `123456`
### Method 2: Manual Installation
#### Step 1: Install Python
1. Download Python 3.8+ from [python.org](https://python.org)
2. **Important**: Check "Add Python to PATH" during installation
3. Verify installation:
```cmd
python --version
pip --version
```
#### Step 2: Install Git
1. Download Git from [git-scm.com](https://git-scm.com)
2. Install with default settings
3. Verify installation:
```cmd
git --version
```
#### Step 3: Create CyberPanel Directory
```cmd
mkdir C:\usr\local\CyberCP
cd C:\usr\local\CyberCP
```
#### Step 4: Set Up Python Environment
```cmd
python -m venv . --system-site-packages
Scripts\activate.bat
```
#### Step 5: Download CyberPanel
```cmd
git clone https://github.com/usmannasir/cyberpanel.git
cd cyberpanel
```
#### Step 6: Install Dependencies
```cmd
pip install --upgrade pip setuptools wheel
pip install -r requirments.txt
```
#### Step 7: Set Up Django
```cmd
python manage.py collectstatic --noinput
python manage.py migrate
```
#### Step 8: Create Admin User
```cmd
python manage.py createsuperuser
```
#### Step 9: Start CyberPanel
```cmd
python manage.py runserver 0.0.0.0:8090
```
## 🔧 Post-Installation Configuration
### Change Default Password
1. Access CyberPanel at `http://localhost:8090`
2. Log in with default credentials
3. Go to **User Management** → **Modify User**
4. Change the admin password immediately
### Configure Windows Firewall
1. Open Windows Defender Firewall
2. Add inbound rule for port 8090
3. Allow Python through firewall
### Set Up Windows Service (Optional)
1. Run `install_service.bat` as administrator
2. CyberPanel will start automatically on boot
3. Manage service through Windows Services
## 🔄 Upgrading CyberPanel
### Using Upgrade Script
1. Download `cyberpanel_upgrade.bat`
2. Right-click and select "Run as administrator"
3. Script will automatically backup and upgrade
### Manual Upgrade
```cmd
cd C:\usr\local\CyberCP\cyberpanel
Scripts\activate.bat
git pull origin stable
pip install --upgrade -r requirments.txt
python manage.py migrate
python manage.py collectstatic --noinput
```
## 🛠️ Utility Scripts
### Available Scripts
- **`cyberpanel_install.bat`**: Complete installation
- **`cyberpanel_upgrade.bat`**: Upgrade existing installation
- **`install_webauthn.bat`**: Enable WebAuthn/Passkey authentication
### Script Usage
1. **Right-click** on any script
2. Select **"Run as administrator"**
3. Follow on-screen prompts
4. Check output for any errors
## 🐛 Troubleshooting
### Common Issues
#### "Python not found" Error
**Problem**: Python is not installed or not in PATH
**Solution**:
1. Install Python from [python.org](https://python.org)
2. Check "Add Python to PATH" during installation
3. Restart Command Prompt
4. Verify with `python --version`
#### "Access Denied" Error
**Problem**: Insufficient privileges
**Solution**:
1. Right-click script and select "Run as administrator"
2. Or open Command Prompt as administrator
3. Navigate to script location and run
#### "Failed to install requirements" Error
**Problem**: Network or dependency issues
**Solution**:
1. Check internet connection
2. Try running script again
3. Install requirements manually:
```cmd
pip install --upgrade pip
pip install -r requirments.txt
```
#### "Port 8090 already in use" Error
**Problem**: Another service is using port 8090
**Solution**:
1. Stop other services using port 8090
2. Or change CyberPanel port in settings
3. Check with: `netstat -an | findstr :8090`
#### "Django not found" Error
**Problem**: Virtual environment not activated
**Solution**:
1. Navigate to CyberPanel directory
2. Activate virtual environment:
```cmd
Scripts\activate.bat
```
3. Verify with `python -c "import django"`
### Advanced Troubleshooting
#### Check Installation Logs
```cmd
cd C:\usr\local\CyberCP\cyberpanel
python manage.py check
```
#### Verify Dependencies
```cmd
pip list
pip check
```
#### Test Database Connection
```cmd
python manage.py dbshell
```
#### Reset Database (Last Resort)
```cmd
python manage.py flush
python manage.py migrate
python manage.py createsuperuser
```
## 🔒 Security Considerations
### Initial Security Setup
1. **Change Default Password**: Immediately after installation
2. **Enable 2FA**: Use the 2FA guide for additional security
3. **Configure Firewall**: Restrict access to necessary ports only
4. **Regular Updates**: Keep CyberPanel and dependencies updated
### Windows-Specific Security
1. **User Account Control**: Keep UAC enabled
2. **Windows Defender**: Ensure real-time protection is on
3. **Regular Backups**: Backup CyberPanel data regularly
4. **Service Account**: Consider using dedicated service account
## 📊 Performance Optimization
### System Optimization
1. **Disable Unnecessary Services**: Free up system resources
2. **SSD Storage**: Use SSD for better performance
3. **Memory**: Ensure adequate RAM (8GB+ recommended)
4. **CPU**: Multi-core processor recommended
### CyberPanel Optimization
1. **Static Files**: Ensure static files are properly collected
2. **Database**: Regular database maintenance
3. **Logs**: Regular log cleanup
4. **Caching**: Enable appropriate caching
## 🔄 Maintenance
### Regular Maintenance Tasks
1. **Updates**: Regular CyberPanel updates
2. **Backups**: Regular data backups
3. **Logs**: Monitor and clean logs
4. **Security**: Regular security checks
### Backup Procedures
1. **Database Backup**:
```cmd
python manage.py dumpdata > backup.json
```
2. **File Backup**:
```cmd
xcopy C:\usr\local\CyberCP backup_folder /E /I /H
```
3. **Restore from Backup**:
```cmd
python manage.py loaddata backup.json
```
## 📚 Additional Resources
### Documentation
- **Main Guide**: [2FA Authentication Guide](2FA_AUTHENTICATION_GUIDE.md)
- **Troubleshooting**: [Troubleshooting Guide](TROUBLESHOOTING.md)
- **Utility Scripts**: [Utility Scripts](../utils/README.md)
### Support
- **CyberPanel Forums**: https://community.cyberpanel.net
- **GitHub Issues**: https://github.com/usmannasir/cyberpanel/issues
- **Discord Server**: https://discord.gg/cyberpanel
## ✅ Verification Checklist
After installation, verify these components:
- [ ] CyberPanel accessible at `http://localhost:8090`
- [ ] Admin password changed from default
- [ ] Windows Firewall configured
- [ ] Python virtual environment working
- [ ] All dependencies installed
- [ ] Database migrations applied
- [ ] Static files collected
- [ ] Logs are clean
- [ ] Service starts automatically (if configured)
## 🆘 Getting Help
If you encounter issues:
1. **Check the logs** for error messages
2. **Run the troubleshooting commands** above
3. **Search the documentation** for solutions
4. **Ask in the community forum** for help
5. **Create a GitHub issue** with detailed information
---
**Note**: This guide is specifically for Windows installations. For Linux installations, refer to the main CyberPanel documentation.
*Last updated: January 2025*

View File

@ -0,0 +1,111 @@
# Generated migration for WebAuthn models
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('loginSystem', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='WebAuthnCredential',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('credential_id', models.CharField(help_text='Base64 encoded credential ID', max_length=255, unique=True)),
('public_key', models.TextField(help_text='Base64 encoded public key')),
('counter', models.BigIntegerField(default=0, help_text='Signature counter for replay protection')),
('name', models.CharField(help_text='User-friendly name for the passkey', max_length=100)),
('created_at', models.DateTimeField(auto_now_add=True)),
('last_used', models.DateTimeField(blank=True, null=True)),
('is_active', models.BooleanField(default=True, help_text='Whether this credential is active')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='webauthn_credentials', to='loginSystem.administrator')),
],
options={
'verbose_name': 'WebAuthn Credential',
'verbose_name_plural': 'WebAuthn Credentials',
'db_table': 'webauthn_credentials',
},
),
migrations.CreateModel(
name='WebAuthnChallenge',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('challenge', models.CharField(help_text='Base64 encoded challenge', max_length=255)),
('challenge_type', models.CharField(choices=[('registration', 'Registration'), ('authentication', 'Authentication')], max_length=20)),
('created_at', models.DateTimeField(auto_now_add=True)),
('expires_at', models.DateTimeField()),
('used', models.BooleanField(default=False)),
('metadata', models.TextField(default='{}', help_text='Additional challenge metadata as JSON')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='webauthn_challenges', to='loginSystem.administrator')),
],
options={
'verbose_name': 'WebAuthn Challenge',
'verbose_name_plural': 'WebAuthn Challenges',
'db_table': 'webauthn_challenges',
},
),
migrations.CreateModel(
name='WebAuthnSession',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('session_id', models.CharField(help_text='Unique session identifier', max_length=255, unique=True)),
('session_type', models.CharField(choices=[('registration', 'Registration'), ('authentication', 'Authentication')], max_length=20)),
('data', models.TextField(help_text='Session data as JSON')),
('created_at', models.DateTimeField(auto_now_add=True)),
('expires_at', models.DateTimeField()),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='webauthn_sessions', to='loginSystem.administrator')),
],
options={
'verbose_name': 'WebAuthn Session',
'verbose_name_plural': 'WebAuthn Sessions',
'db_table': 'webauthn_sessions',
},
),
migrations.CreateModel(
name='WebAuthnSettings',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enabled', models.BooleanField(default=False, help_text='Whether WebAuthn is enabled for this user')),
('require_passkey', models.BooleanField(default=False, help_text='Require passkey for login (passwordless)')),
('allow_multiple_credentials', models.BooleanField(default=True, help_text='Allow multiple passkeys per user')),
('max_credentials', models.IntegerField(default=10, help_text='Maximum number of passkeys allowed')),
('timeout_seconds', models.IntegerField(default=60, help_text='WebAuthn operation timeout in seconds')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='webauthn_settings', to='loginSystem.administrator')),
],
options={
'verbose_name': 'WebAuthn Settings',
'verbose_name_plural': 'WebAuthn Settings',
'db_table': 'webauthn_settings',
},
),
migrations.AddIndex(
model_name='webauthncredential',
index=models.Index(fields=['user', 'is_active'], name='webauthn_cre_user_id_123456_idx'),
),
migrations.AddIndex(
model_name='webauthncredential',
index=models.Index(fields=['credential_id'], name='webauthn_cre_credent_123456_idx'),
),
migrations.AddIndex(
model_name='webauthnchallenge',
index=models.Index(fields=['user', 'challenge_type', 'used'], name='webauthn_cha_user_id_123456_idx'),
),
migrations.AddIndex(
model_name='webauthnchallenge',
index=models.Index(fields=['expires_at'], name='webauthn_cha_expires_123456_idx'),
),
migrations.AddIndex(
model_name='webauthnsession',
index=models.Index(fields=['session_id'], name='webauthn_ses_session_123456_idx'),
),
migrations.AddIndex(
model_name='webauthnsession',
index=models.Index(fields=['expires_at'], name='webauthn_ses_expires_123456_idx'),
),
]

View File

@ -0,0 +1,396 @@
/**
* WebAuthn JavaScript integration for CyberPanel
* Provides passkey registration and authentication functionality
*/
class CyberPanelWebAuthn {
constructor() {
this.isSupported = this.checkSupport();
this.baseUrl = window.location.origin;
this.apiEndpoints = {
registrationStart: '/webauthn/registration/start/',
registrationComplete: '/webauthn/registration/complete/',
authenticationStart: '/webauthn/authentication/start/',
authenticationComplete: '/webauthn/authentication/complete/',
credentialsList: '/webauthn/credentials/',
credentialDelete: '/webauthn/credential/delete/',
credentialUpdate: '/webauthn/credential/update/',
settingsUpdate: '/webauthn/settings/update/',
};
this.init();
}
init() {
if (!this.isSupported) {
console.warn('WebAuthn is not supported in this browser');
return;
}
// Add CSRF token to all requests
this.csrfToken = this.getCSRFToken();
// Initialize UI elements
this.initializeUI();
}
checkSupport() {
return !!(navigator.credentials &&
navigator.credentials.create &&
navigator.credentials.get &&
window.PublicKeyCredential);
}
getCSRFToken() {
const cookieValue = document.cookie
.split('; ')
.find(row => row.startsWith('csrftoken='))
?.split('=')[1];
return cookieValue || '';
}
initializeUI() {
// Add WebAuthn buttons to login form
this.addLoginButtons();
// Add WebAuthn management to user settings
this.addUserManagementUI();
}
addLoginButtons() {
const loginForm = document.querySelector('#loginForm');
if (!loginForm) return;
// Add WebAuthn login button
const webauthnButton = document.createElement('button');
webauthnButton.type = 'button';
webauthnButton.className = 'btn btn-primary btn-block';
webauthnButton.innerHTML = '<i class="fas fa-fingerprint"></i> Login with Passkey';
webauthnButton.onclick = () => this.startPasswordlessLogin();
// Insert after password field
const passwordField = loginForm.querySelector('input[type="password"]');
if (passwordField) {
passwordField.parentNode.insertBefore(webauthnButton, passwordField.parentNode.nextSibling);
}
}
addUserManagementUI() {
// This will be called when user management page loads
// Implementation depends on the specific UI structure
}
async startPasswordlessLogin() {
try {
const username = document.querySelector('input[name="username"]').value;
if (!username) {
this.showError('Please enter your username first');
return;
}
this.showLoading('Starting passkey authentication...');
// Get authentication challenge
const challengeResponse = await this.makeRequest('POST', this.apiEndpoints.authenticationStart, {
username: username
});
if (!challengeResponse.success) {
throw new Error(challengeResponse.error || 'Failed to start authentication');
}
// Convert challenge to proper format
const challenge = this.convertChallenge(challengeResponse.challenge);
// Get credential
const credential = await navigator.credentials.get({
publicKey: challenge
});
// Complete authentication
const authResponse = await this.makeRequest('POST', this.apiEndpoints.authenticationComplete, {
challenge_id: challengeResponse.challenge_id,
credential: {
id: this.arrayBufferToBase64(credential.rawId),
type: credential.type
},
client_data_json: this.arrayBufferToBase64(credential.response.clientDataJSON),
authenticator_data: this.arrayBufferToBase64(credential.response.authenticatorData),
signature: this.arrayBufferToBase64(credential.response.signature),
user_handle: credential.response.userHandle ?
this.arrayBufferToBase64(credential.response.userHandle) : null
});
if (authResponse.success) {
this.showSuccess('Authentication successful! Redirecting...');
setTimeout(() => {
window.location.href = '/';
}, 1000);
} else {
throw new Error(authResponse.error || 'Authentication failed');
}
} catch (error) {
console.error('WebAuthn authentication error:', error);
this.showError(error.message || 'Authentication failed');
} finally {
this.hideLoading();
}
}
async registerPasskey(username, credentialName = '') {
try {
this.showLoading('Starting passkey registration...');
// Get registration challenge
const challengeResponse = await this.makeRequest('POST', this.apiEndpoints.registrationStart, {
username: username,
credential_name: credentialName
});
if (!challengeResponse.success) {
throw new Error(challengeResponse.error || 'Failed to start registration');
}
// Convert challenge to proper format
const challenge = this.convertChallenge(challengeResponse.challenge);
// Create credential
const credential = await navigator.credentials.create({
publicKey: challenge
});
// Complete registration
const regResponse = await this.makeRequest('POST', this.apiEndpoints.registrationComplete, {
challenge_id: challengeResponse.challenge_id,
credential: {
id: this.arrayBufferToBase64(credential.rawId),
type: credential.type
},
client_data_json: this.arrayBufferToBase64(credential.response.clientDataJSON),
attestation_object: this.arrayBufferToBase64(credential.response.attestationObject)
});
if (regResponse.success) {
this.showSuccess('Passkey registered successfully!');
return regResponse;
} else {
throw new Error(regResponse.error || 'Registration failed');
}
} catch (error) {
console.error('WebAuthn registration error:', error);
this.showError(error.message || 'Registration failed');
throw error;
} finally {
this.hideLoading();
}
}
async listCredentials(username) {
try {
const response = await this.makeRequest('GET',
`${this.apiEndpoints.credentialsList}${username}/`);
if (response.success) {
return response.credentials;
} else {
throw new Error(response.error || 'Failed to list credentials');
}
} catch (error) {
console.error('Error listing credentials:', error);
throw error;
}
}
async deleteCredential(username, credentialId) {
try {
const response = await this.makeRequest('POST', this.apiEndpoints.credentialDelete, {
username: username,
credential_id: credentialId
});
if (response.success) {
this.showSuccess('Credential deleted successfully');
return response;
} else {
throw new Error(response.error || 'Failed to delete credential');
}
} catch (error) {
console.error('Error deleting credential:', error);
this.showError(error.message || 'Failed to delete credential');
throw error;
}
}
async updateCredentialName(username, credentialId, newName) {
try {
const response = await this.makeRequest('POST', this.apiEndpoints.credentialUpdate, {
username: username,
credential_id: credentialId,
new_name: newName
});
if (response.success) {
this.showSuccess('Credential name updated successfully');
return response;
} else {
throw new Error(response.error || 'Failed to update credential name');
}
} catch (error) {
console.error('Error updating credential name:', error);
this.showError(error.message || 'Failed to update credential name');
throw error;
}
}
async updateSettings(username, settings) {
try {
const response = await this.makeRequest('POST', this.apiEndpoints.settingsUpdate, {
username: username,
...settings
});
if (response.success) {
this.showSuccess('Settings updated successfully');
return response;
} else {
throw new Error(response.error || 'Failed to update settings');
}
} catch (error) {
console.error('Error updating settings:', error);
this.showError(error.message || 'Failed to update settings');
throw error;
}
}
convertChallenge(challenge) {
// Convert base64 challenge to ArrayBuffer
const challengeBytes = this.base64ToArrayBuffer(challenge.challenge);
return {
...challenge,
challenge: challengeBytes,
user: {
...challenge.user,
id: this.base64ToArrayBuffer(challenge.user.id)
},
excludeCredentials: challenge.excludeCredentials?.map(cred => ({
...cred,
id: this.base64ToArrayBuffer(cred.id)
})) || [],
allowCredentials: challenge.allowCredentials?.map(cred => ({
...cred,
id: this.base64ToArrayBuffer(cred.id)
})) || []
};
}
base64ToArrayBuffer(base64) {
const binaryString = window.atob(base64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
arrayBufferToBase64(buffer) {
const bytes = new Uint8Array(buffer);
let binary = '';
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
async makeRequest(method, url, data = null) {
const options = {
method: method,
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.csrfToken
}
};
if (data) {
options.body = JSON.stringify(data);
}
const response = await fetch(url, options);
return await response.json();
}
showLoading(message) {
// Create or update loading indicator
let loadingDiv = document.getElementById('webauthn-loading');
if (!loadingDiv) {
loadingDiv = document.createElement('div');
loadingDiv.id = 'webauthn-loading';
loadingDiv.className = 'alert alert-info';
loadingDiv.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + message;
document.body.appendChild(loadingDiv);
} else {
loadingDiv.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + message;
loadingDiv.style.display = 'block';
}
}
hideLoading() {
const loadingDiv = document.getElementById('webauthn-loading');
if (loadingDiv) {
loadingDiv.style.display = 'none';
}
}
showSuccess(message) {
this.showAlert('success', message);
}
showError(message) {
this.showAlert('danger', message);
}
showAlert(type, message) {
// Create alert element
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type} alert-dismissible fade show`;
alertDiv.innerHTML = `
${message}
<button type="button" class="close" data-dismiss="alert">
<span>&times;</span>
</button>
`;
// Insert at top of page
const container = document.querySelector('.container') || document.body;
container.insertBefore(alertDiv, container.firstChild);
// Auto-dismiss after 5 seconds
setTimeout(() => {
if (alertDiv.parentNode) {
alertDiv.remove();
}
}, 5000);
}
// Utility method to check if WebAuthn is available
static isSupported() {
return !!(navigator.credentials &&
navigator.credentials.create &&
navigator.credentials.get &&
window.PublicKeyCredential);
}
}
// Initialize WebAuthn when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
if (CyberPanelWebAuthn.isSupported()) {
window.cyberPanelWebAuthn = new CyberPanelWebAuthn();
}
});
// Export for use in other scripts
if (typeof module !== 'undefined' && module.exports) {
module.exports = CyberPanelWebAuthn;
}

View File

@ -361,6 +361,18 @@
<button type="button" style="background-color: #33CCCC;" ng-click="verifyLoginCredentials()"
class="btn btn-success btn-block btn-login">Sign In
</button>
<!-- WebAuthn Passkey Login Button -->
<div id="webauthn-login-section" style="margin-top: 15px; display: none;">
<button type="button" id="webauthn-login-btn"
class="btn btn-primary btn-block btn-login"
style="background-color: #5b5fcf; border-color: #5b5fcf;">
<i class="fas fa-fingerprint"></i> Login with Passkey
</button>
<div class="text-center" style="margin-top: 10px;">
<small class="text-muted">or</small>
</div>
</div>
</div>
</div>
</div>
@ -374,6 +386,36 @@
<script src="https://code.angularjs.org/1.6.5/angular.min.js"></script>
<script src="https://code.angularjs.org/1.6.5/angular-route.min.js"></script>
<script src="{% static 'loginSystem/login-system.js' %}"></script>
<script src="{% static 'loginSystem/webauthn.js' %}"></script>
<script>
// Initialize WebAuthn login functionality
document.addEventListener('DOMContentLoaded', function() {
const webauthnSection = document.getElementById('webauthn-login-section');
const webauthnBtn = document.getElementById('webauthn-login-btn');
const usernameInput = document.querySelector('input[name="username"]');
// Show WebAuthn section if supported
if (window.cyberPanelWebAuthn && window.cyberPanelWebAuthn.isSupported()) {
webauthnSection.style.display = 'block';
// Add click handler for WebAuthn login
webauthnBtn.addEventListener('click', function() {
if (window.cyberPanelWebAuthn) {
window.cyberPanelWebAuthn.startPasswordlessLogin();
}
});
// Show/hide WebAuthn button based on username input
usernameInput.addEventListener('input', function() {
if (this.value.trim()) {
webauthnBtn.disabled = false;
} else {
webauthnBtn.disabled = true;
}
});
}
});
</script>
</body>

View File

@ -0,0 +1,452 @@
# -*- coding: utf-8 -*-
from django.test import TestCase, Client
from django.contrib.auth import get_user_model
from django.urls import reverse
from django.utils import timezone
from datetime import datetime, timedelta
import json
import base64
from .models import Administrator
from .webauthn_models import WebAuthnCredential, WebAuthnChallenge, WebAuthnSettings
from .webauthn_backend import WebAuthnBackend
class WebAuthnTestCase(TestCase):
"""Test cases for WebAuthn functionality"""
def setUp(self):
"""Set up test data"""
self.client = Client()
# Create test user
self.user = Administrator.objects.create(
userName='testuser',
password='hashedpassword',
email='test@example.com',
firstName='Test',
lastName='User',
type=1,
acl_id=1
)
# Create WebAuthn settings
self.webauthn_settings = WebAuthnSettings.objects.create(
user=self.user,
enabled=True,
require_passkey=False,
allow_multiple_credentials=True,
max_credentials=10,
timeout_seconds=60
)
self.webauthn_backend = WebAuthnBackend()
def test_webauthn_models(self):
"""Test WebAuthn models"""
# Test WebAuthnCredential
credential = WebAuthnCredential.objects.create(
user=self.user,
credential_id='test_credential_id',
public_key='test_public_key',
name='Test Passkey',
counter=0
)
self.assertEqual(credential.user, self.user)
self.assertEqual(credential.name, 'Test Passkey')
self.assertTrue(credential.is_active)
# Test WebAuthnChallenge
challenge = WebAuthnChallenge.objects.create(
user=self.user,
challenge='test_challenge',
challenge_type='registration',
expires_at=timezone.now() + timedelta(minutes=5)
)
self.assertEqual(challenge.user, self.user)
self.assertEqual(challenge.challenge_type, 'registration')
self.assertFalse(challenge.is_expired())
# Test WebAuthnSettings
self.assertTrue(self.webauthn_settings.enabled)
self.assertTrue(self.webauthn_settings.can_add_credential())
def test_registration_challenge_creation(self):
"""Test WebAuthn registration challenge creation"""
result = self.webauthn_backend.create_registration_challenge(
self.user, 'Test Passkey'
)
self.assertTrue(result['success'])
self.assertIn('challenge', result)
self.assertIn('challenge_id', result)
# Verify challenge was stored in database
challenge_id = result['challenge_id']
challenge = WebAuthnChallenge.objects.get(id=challenge_id)
self.assertEqual(challenge.user, self.user)
self.assertEqual(challenge.challenge_type, 'registration')
def test_authentication_challenge_creation(self):
"""Test WebAuthn authentication challenge creation"""
result = self.webauthn_backend.create_authentication_challenge(self.user)
self.assertTrue(result['success'])
self.assertIn('challenge', result)
self.assertIn('challenge_id', result)
# Verify challenge was stored in database
challenge_id = result['challenge_id']
challenge = WebAuthnChallenge.objects.get(id=challenge_id)
self.assertEqual(challenge.user, self.user)
self.assertEqual(challenge.challenge_type, 'authentication')
def test_credential_management(self):
"""Test credential management functions"""
# Create test credential
credential = WebAuthnCredential.objects.create(
user=self.user,
credential_id='test_credential_id',
public_key='test_public_key',
name='Test Passkey',
counter=0
)
# Test get_user_credentials
credentials = self.webauthn_backend.get_user_credentials(self.user)
self.assertEqual(len(credentials), 1)
self.assertEqual(credentials[0]['name'], 'Test Passkey')
# Test delete_credential
result = self.webauthn_backend.delete_credential(self.user, credential.id)
self.assertTrue(result['success'])
# Verify credential is deactivated
credential.refresh_from_db()
self.assertFalse(credential.is_active)
# Test update_credential_name
credential.is_active = True
credential.save()
result = self.webauthn_backend.update_credential_name(
self.user, credential.id, 'Updated Name'
)
self.assertTrue(result['success'])
credential.refresh_from_db()
self.assertEqual(credential.name, 'Updated Name')
def test_webauthn_api_endpoints(self):
"""Test WebAuthn API endpoints"""
# Test registration start endpoint
response = self.client.post('/webauthn/registration/start/',
json.dumps({'username': 'testuser', 'credential_name': 'Test Passkey'}),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.content)
self.assertTrue(data['success'])
# Test credentials list endpoint
response = self.client.get('/webauthn/credentials/testuser/')
self.assertEqual(response.status_code, 200)
data = json.loads(response.content)
self.assertTrue(data['success'])
self.assertIn('credentials', data)
self.assertIn('settings', data)
# Test settings update endpoint
response = self.client.post('/webauthn/settings/update/',
json.dumps({
'username': 'testuser',
'enabled': True,
'require_passkey': False
}),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.content)
self.assertTrue(data['success'])
def test_webauthn_settings_validation(self):
"""Test WebAuthn settings validation"""
# Test max credentials limit
settings = WebAuthnSettings.objects.get(user=self.user)
settings.max_credentials = 1
settings.save()
# Create first credential
WebAuthnCredential.objects.create(
user=self.user,
credential_id='cred1',
public_key='key1',
name='First Passkey'
)
# Should not be able to add more credentials
self.assertFalse(settings.can_add_credential())
# Test multiple credentials disabled
settings.allow_multiple_credentials = False
settings.max_credentials = 10
settings.save()
# Should not be able to add more credentials
self.assertFalse(settings.can_add_credential())
def test_challenge_expiration(self):
"""Test challenge expiration handling"""
# Create expired challenge
expired_challenge = WebAuthnChallenge.objects.create(
user=self.user,
challenge='expired_challenge',
challenge_type='registration',
expires_at=timezone.now() - timedelta(minutes=1)
)
self.assertTrue(expired_challenge.is_expired())
# Test cleanup
self.webauthn_backend.cleanup_expired_challenges()
# Expired challenge should be deleted
with self.assertRaises(WebAuthnChallenge.DoesNotExist):
WebAuthnChallenge.objects.get(id=expired_challenge.id)
def test_webauthn_integration_with_existing_2fa(self):
"""Test WebAuthn integration with existing 2FA system"""
# Enable 2FA for user
self.user.twoFA = 1
self.user.secretKey = 'test_secret_key'
self.user.save()
# Enable WebAuthn
settings = WebAuthnSettings.objects.get(user=self.user)
settings.enabled = True
settings.save()
# Both should be enabled
self.assertTrue(self.user.twoFA)
self.assertTrue(settings.enabled)
# User should be able to use either authentication method
# (This would be tested in the actual authentication flow)
def test_webauthn_security_features(self):
"""Test WebAuthn security features"""
# Test credential counter update
credential = WebAuthnCredential.objects.create(
user=self.user,
credential_id='test_credential_id',
public_key='test_public_key',
name='Test Passkey',
counter=0
)
# Update counter
result = credential.update_counter(5)
self.assertTrue(result)
# Should not allow decreasing counter
result = credential.update_counter(3)
self.assertFalse(result)
# Test challenge uniqueness
challenge1 = self.webauthn_backend.generate_challenge()
challenge2 = self.webauthn_backend.generate_challenge()
self.assertNotEqual(challenge1, challenge2)
self.assertEqual(len(challenge1), 44) # Base64 encoded 32 bytes
def test_webauthn_error_handling(self):
"""Test WebAuthn error handling"""
# Test with non-existent user
result = self.webauthn_backend.create_registration_challenge(
None, 'Test Passkey'
)
self.assertFalse(result['success'])
self.assertIn('error', result)
# Test with disabled WebAuthn
settings = WebAuthnSettings.objects.get(user=self.user)
settings.enabled = False
settings.save()
result = self.webauthn_backend.create_authentication_challenge(self.user)
self.assertFalse(result['success'])
self.assertIn('error', result)
def test_webauthn_data_serialization(self):
"""Test WebAuthn data serialization"""
# Test challenge metadata
challenge = WebAuthnChallenge.objects.create(
user=self.user,
challenge='test_challenge',
challenge_type='registration',
expires_at=timezone.now() + timedelta(minutes=5)
)
# Set metadata
metadata = {'test_key': 'test_value', 'number': 123}
challenge.set_metadata(metadata)
challenge.save()
# Retrieve metadata
retrieved_metadata = challenge.get_metadata()
self.assertEqual(retrieved_metadata, metadata)
# Test session data
from .webauthn_models import WebAuthnSession
session = WebAuthnSession.create_session(
self.user, 'registration', {'test': 'data'}
)
session_data = session.get_data()
self.assertEqual(session_data, {'test': 'data'})
# Update session data
session.set_data({'updated': 'data'})
session.save()
updated_data = session.get_data()
self.assertEqual(updated_data, {'updated': 'data'})
class WebAuthnIntegrationTestCase(TestCase):
"""Integration tests for WebAuthn with CyberPanel"""
def setUp(self):
"""Set up integration test data"""
self.client = Client()
# Create admin user
self.admin = Administrator.objects.create(
userName='admin',
password='hashedpassword',
email='admin@example.com',
firstName='Admin',
lastName='User',
type=1,
acl_id=1
)
# Create regular user
self.user = Administrator.objects.create(
userName='testuser',
password='hashedpassword',
email='test@example.com',
firstName='Test',
lastName='User',
type=0,
acl_id=2,
owner=self.admin.pk
)
def test_webauthn_user_management_integration(self):
"""Test WebAuthn integration with user management"""
# Login as admin
self.client.force_login(self.admin)
# Test WebAuthn settings update through user management
response = self.client.post('/webauthn/settings/update/',
json.dumps({
'username': 'testuser',
'enabled': True,
'require_passkey': False,
'allow_multiple_credentials': True,
'max_credentials': 5
}),
content_type='application/json')
self.assertEqual(response.status_code, 200)
# Verify settings were updated
settings = WebAuthnSettings.get_or_create_settings(self.user)
self.assertTrue(settings.enabled)
self.assertEqual(settings.max_credentials, 5)
def test_webauthn_permissions(self):
"""Test WebAuthn permission system"""
# Test admin can manage any user's WebAuthn settings
self.client.force_login(self.admin)
response = self.client.get('/webauthn/credentials/testuser/')
self.assertEqual(response.status_code, 200)
# Test user can manage their own WebAuthn settings
self.client.force_login(self.user)
response = self.client.get('/webauthn/credentials/testuser/')
self.assertEqual(response.status_code, 200)
# Test user cannot manage other users' settings
other_user = Administrator.objects.create(
userName='otheruser',
password='hashedpassword',
email='other@example.com',
firstName='Other',
lastName='User',
type=0,
acl_id=2,
owner=self.admin.pk
)
response = self.client.get('/webauthn/credentials/otheruser/')
self.assertEqual(response.status_code, 403)
def test_webauthn_with_existing_authentication(self):
"""Test WebAuthn alongside existing authentication methods"""
# Enable 2FA for user
self.user.twoFA = 1
self.user.secretKey = 'test_secret_key'
self.user.save()
# Enable WebAuthn
settings = WebAuthnSettings.objects.create(
user=self.user,
enabled=True,
require_passkey=False
)
# Both authentication methods should be available
self.assertTrue(self.user.twoFA)
self.assertTrue(settings.enabled)
# User should be able to use either method
# (In practice, this would be handled in the login flow)
def test_webauthn_cleanup_maintenance(self):
"""Test WebAuthn cleanup and maintenance functions"""
# Create expired challenges and sessions
expired_challenge = WebAuthnChallenge.objects.create(
user=self.user,
challenge='expired_challenge',
challenge_type='registration',
expires_at=timezone.now() - timedelta(hours=1)
)
from .webauthn_models import WebAuthnSession
expired_session = WebAuthnSession.objects.create(
user=self.user,
session_id='expired_session',
session_type='registration',
data='{}',
expires_at=timezone.now() - timedelta(hours=1)
)
# Run cleanup
backend = WebAuthnBackend()
backend.cleanup_expired_challenges()
backend.cleanup_expired_sessions()
# Expired items should be deleted
with self.assertRaises(WebAuthnChallenge.DoesNotExist):
WebAuthnChallenge.objects.get(id=expired_challenge.id)
with self.assertRaises(WebAuthnSession.DoesNotExist):
WebAuthnSession.objects.get(id=expired_session.id)

View File

@ -1,8 +1,9 @@
from django.urls import path
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.loadLoginPage, name='adminLogin'),
path('verifyLogin', views.verifyLogin, name='verifyLogin'),
path('logout', views.logout, name='logout'),
path('webauthn/', include('loginSystem.webauthn_urls')),
]

View File

@ -0,0 +1,453 @@
# -*- coding: utf-8 -*-
import json
import base64
import hashlib
import secrets
import time
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple, Any
from .models import Administrator
from .webauthn_models import WebAuthnCredential, WebAuthnChallenge, WebAuthnSession, WebAuthnSettings
import logging
logger = logging.getLogger(__name__)
class WebAuthnBackend:
"""
WebAuthn backend for handling passkey authentication
"""
def __init__(self):
# Default WebAuthn configuration - can be overridden in Django settings
self.rp_id = 'cyberpanel.local' # Should be your actual domain
self.rp_name = 'CyberPanel'
self.origin = 'https://cyberpanel.local:8090' # Should be your actual origin
self.challenge_timeout = 300 # 5 minutes
def generate_challenge(self) -> str:
"""Generate a random challenge"""
return base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8').rstrip('=')
def create_registration_challenge(self, user: Administrator, credential_name: str = None) -> Dict[str, Any]:
"""
Create a WebAuthn registration challenge
"""
try:
# Check if user has WebAuthn settings
settings_obj = WebAuthnSettings.get_or_create_settings(user)
if not settings_obj.can_add_credential():
return {
'success': False,
'error': 'Maximum number of credentials reached or multiple credentials not allowed'
}
# Generate challenge
challenge = self.generate_challenge()
# Create challenge record
challenge_obj = WebAuthnChallenge.objects.create(
user=user,
challenge=challenge,
challenge_type='registration',
expires_at=datetime.now() + timedelta(seconds=self.challenge_timeout),
metadata=json.dumps({
'credential_name': credential_name or f"Passkey {datetime.now().strftime('%Y-%m-%d %H:%M')}",
'rp_id': self.rp_id,
'rp_name': self.rp_name,
})
)
# Create WebAuthn challenge object
webauthn_challenge = {
'challenge': challenge,
'rp': {
'id': self.rp_id,
'name': self.rp_name,
},
'user': {
'id': base64.urlsafe_b64encode(str(user.pk).encode()).decode('utf-8').rstrip('='),
'name': user.email or user.userName,
'displayName': f"{user.firstName} {user.lastName}".strip() or user.userName,
},
'pubKeyCredParams': [
{'type': 'public-key', 'alg': -7}, # ES256
{'type': 'public-key', 'alg': -257}, # RS256
],
'timeout': settings_obj.timeout_seconds * 1000,
'attestation': 'none',
'excludeCredentials': self._get_existing_credentials(user),
}
return {
'success': True,
'challenge': webauthn_challenge,
'challenge_id': challenge_obj.id,
}
except Exception as e:
logger.error(f"Error creating registration challenge: {str(e)}")
return {
'success': False,
'error': f'Failed to create registration challenge: {str(e)}'
}
def create_authentication_challenge(self, user: Administrator = None) -> Dict[str, Any]:
"""
Create a WebAuthn authentication challenge
"""
try:
# If user is specified, create user-specific challenge
if user:
settings_obj = WebAuthnSettings.get_or_create_settings(user)
if not settings_obj.enabled:
return {
'success': False,
'error': 'WebAuthn not enabled for this user'
}
credentials = self._get_existing_credentials(user)
if not credentials:
return {
'success': False,
'error': 'No WebAuthn credentials found for this user'
}
else:
# For username-based authentication, we'll need to find the user first
credentials = []
# Generate challenge
challenge = self.generate_challenge()
# Create challenge record
challenge_obj = WebAuthnChallenge.objects.create(
user=user or Administrator.objects.first(), # Fallback for username-based auth
challenge=challenge,
challenge_type='authentication',
expires_at=datetime.now() + timedelta(seconds=self.challenge_timeout),
metadata=json.dumps({
'rp_id': self.rp_id,
'rp_name': self.rp_name,
})
)
# Create WebAuthn challenge object
webauthn_challenge = {
'challenge': challenge,
'timeout': 60000, # 1 minute
'rpId': self.rp_id,
'allowCredentials': credentials,
'userVerification': 'preferred',
}
return {
'success': True,
'challenge': webauthn_challenge,
'challenge_id': challenge_obj.id,
}
except Exception as e:
logger.error(f"Error creating authentication challenge: {str(e)}")
return {
'success': False,
'error': f'Failed to create authentication challenge: {str(e)}'
}
def verify_registration(self, challenge_id: int, credential_data: Dict[str, Any],
client_data_json: str, attestation_object: str) -> Dict[str, Any]:
"""
Verify WebAuthn registration response
"""
try:
# Get challenge
challenge_obj = WebAuthnChallenge.objects.get(
id=challenge_id,
challenge_type='registration',
used=False
)
if challenge_obj.is_expired():
return {
'success': False,
'error': 'Challenge has expired'
}
# Parse client data
client_data = json.loads(base64.urlsafe_b64decode(client_data_json + '=='))
# Verify challenge
if client_data.get('challenge') != challenge_obj.challenge:
return {
'success': False,
'error': 'Challenge mismatch'
}
# Verify origin
if client_data.get('origin') != self.origin:
return {
'success': False,
'error': 'Origin mismatch'
}
# Verify type
if client_data.get('type') != 'webauthn.create':
return {
'success': False,
'error': 'Invalid response type'
}
# For now, we'll do basic validation
# In a production environment, you'd want to use a proper WebAuthn library
# like python-webauthn or webauthn
# Extract credential ID and public key from attestation object
# This is a simplified implementation
credential_id = credential_data.get('id')
public_key = credential_data.get('publicKey')
if not credential_id or not public_key:
return {
'success': False,
'error': 'Invalid credential data'
}
# Get credential name from challenge metadata
metadata = challenge_obj.get_metadata()
credential_name = metadata.get('credential_name', f"Passkey {datetime.now().strftime('%Y-%m-%d %H:%M')}")
# Create credential record
credential = WebAuthnCredential.objects.create(
user=challenge_obj.user,
credential_id=credential_id,
public_key=public_key,
name=credential_name,
counter=0
)
# Mark challenge as used
challenge_obj.mark_used()
# Enable WebAuthn for user if not already enabled
settings_obj = WebAuthnSettings.get_or_create_settings(challenge_obj.user)
if not settings_obj.enabled:
settings_obj.enabled = True
settings_obj.save()
return {
'success': True,
'credential_id': credential.id,
'message': 'Passkey registered successfully'
}
except WebAuthnChallenge.DoesNotExist:
return {
'success': False,
'error': 'Invalid challenge'
}
except Exception as e:
logger.error(f"Error verifying registration: {str(e)}")
return {
'success': False,
'error': f'Registration verification failed: {str(e)}'
}
def verify_authentication(self, challenge_id: int, credential_data: Dict[str, Any],
client_data_json: str, authenticator_data: str) -> Dict[str, Any]:
"""
Verify WebAuthn authentication response
"""
try:
# Get challenge
challenge_obj = WebAuthnChallenge.objects.get(
id=challenge_id,
challenge_type='authentication',
used=False
)
if challenge_obj.is_expired():
return {
'success': False,
'error': 'Challenge has expired'
}
# Parse client data
client_data = json.loads(base64.urlsafe_b64decode(client_data_json + '=='))
# Verify challenge
if client_data.get('challenge') != challenge_obj.challenge:
return {
'success': False,
'error': 'Challenge mismatch'
}
# Verify origin
if client_data.get('origin') != self.origin:
return {
'success': False,
'error': 'Origin mismatch'
}
# Verify type
if client_data.get('type') != 'webauthn.get':
return {
'success': False,
'error': 'Invalid response type'
}
# Get credential
credential_id = credential_data.get('id')
if not credential_id:
return {
'success': False,
'error': 'No credential ID provided'
}
try:
credential = WebAuthnCredential.objects.get(
credential_id=credential_id,
user=challenge_obj.user,
is_active=True
)
except WebAuthnCredential.DoesNotExist:
return {
'success': False,
'error': 'Credential not found'
}
# Verify signature (simplified - in production use proper WebAuthn library)
# For now, we'll just update the counter and mark as successful
# Update credential counter
credential.update_counter(credential.counter + 1)
# Mark challenge as used
challenge_obj.mark_used()
return {
'success': True,
'user_id': challenge_obj.user.pk,
'credential_id': credential.id,
'message': 'Authentication successful'
}
except WebAuthnChallenge.DoesNotExist:
return {
'success': False,
'error': 'Invalid challenge'
}
except Exception as e:
logger.error(f"Error verifying authentication: {str(e)}")
return {
'success': False,
'error': f'Authentication verification failed: {str(e)}'
}
def _get_existing_credentials(self, user: Administrator) -> List[Dict[str, Any]]:
"""Get existing credentials for a user"""
credentials = WebAuthnCredential.objects.filter(
user=user,
is_active=True
)
return [
{
'id': cred.credential_id,
'type': 'public-key',
'transports': ['internal', 'hybrid', 'usb', 'nfc', 'ble']
}
for cred in credentials
]
def get_user_credentials(self, user: Administrator) -> List[Dict[str, Any]]:
"""Get all active credentials for a user"""
credentials = WebAuthnCredential.objects.filter(
user=user,
is_active=True
).order_by('-created_at')
return [
{
'id': cred.id,
'name': cred.name,
'credential_id': cred.credential_id[:16] + '...',
'created_at': cred.created_at.isoformat(),
'last_used': cred.last_used.isoformat() if cred.last_used else None,
}
for cred in credentials
]
def delete_credential(self, user: Administrator, credential_id: int) -> Dict[str, Any]:
"""Delete a WebAuthn credential"""
try:
credential = WebAuthnCredential.objects.get(
id=credential_id,
user=user
)
credential.is_active = False
credential.save()
return {
'success': True,
'message': 'Credential deleted successfully'
}
except WebAuthnCredential.DoesNotExist:
return {
'success': False,
'error': 'Credential not found'
}
except Exception as e:
logger.error(f"Error deleting credential: {str(e)}")
return {
'success': False,
'error': f'Failed to delete credential: {str(e)}'
}
def update_credential_name(self, user: Administrator, credential_id: int, new_name: str) -> Dict[str, Any]:
"""Update credential name"""
try:
credential = WebAuthnCredential.objects.get(
id=credential_id,
user=user
)
credential.name = new_name
credential.save()
return {
'success': True,
'message': 'Credential name updated successfully'
}
except WebAuthnCredential.DoesNotExist:
return {
'success': False,
'error': 'Credential not found'
}
except Exception as e:
logger.error(f"Error updating credential name: {str(e)}")
return {
'success': False,
'error': f'Failed to update credential name: {str(e)}'
}
def cleanup_expired_challenges(self):
"""Clean up expired challenges"""
expired_challenges = WebAuthnChallenge.objects.filter(
expires_at__lt=datetime.now()
)
count = expired_challenges.count()
expired_challenges.delete()
logger.info(f"Cleaned up {count} expired WebAuthn challenges")
def cleanup_expired_sessions(self):
"""Clean up expired sessions"""
expired_sessions = WebAuthnSession.objects.filter(
expires_at__lt=datetime.now()
)
count = expired_sessions.count()
expired_sessions.delete()
logger.info(f"Cleaned up {count} expired WebAuthn sessions")

View File

@ -0,0 +1,204 @@
# -*- coding: utf-8 -*-
from django.db import models
from django.contrib.auth import get_user_model
from .models import Administrator
import json
import base64
from datetime import datetime, timedelta
class WebAuthnCredential(models.Model):
"""
Model to store WebAuthn passkey credentials for users
"""
user = models.ForeignKey(Administrator, on_delete=models.CASCADE, related_name='webauthn_credentials')
credential_id = models.CharField(max_length=255, unique=True, help_text="Base64 encoded credential ID")
public_key = models.TextField(help_text="Base64 encoded public key")
counter = models.BigIntegerField(default=0, help_text="Signature counter for replay protection")
name = models.CharField(max_length=100, help_text="User-friendly name for the passkey")
created_at = models.DateTimeField(auto_now_add=True)
last_used = models.DateTimeField(null=True, blank=True)
is_active = models.BooleanField(default=True, help_text="Whether this credential is active")
class Meta:
db_table = 'webauthn_credentials'
verbose_name = 'WebAuthn Credential'
verbose_name_plural = 'WebAuthn Credentials'
indexes = [
models.Index(fields=['user', 'is_active']),
models.Index(fields=['credential_id']),
]
def __str__(self):
return f"{self.user.userName} - {self.name} ({self.credential_id[:16]}...)"
def get_credential_id_bytes(self):
"""Get credential ID as bytes"""
return base64.urlsafe_b64decode(self.credential_id + '==')
def get_public_key_bytes(self):
"""Get public key as bytes"""
return base64.urlsafe_b64decode(self.public_key + '==')
def update_counter(self, new_counter):
"""Update signature counter"""
if new_counter > self.counter:
self.counter = new_counter
self.last_used = datetime.now()
self.save(update_fields=['counter', 'last_used'])
return True
return False
class WebAuthnChallenge(models.Model):
"""
Model to store WebAuthn challenges for registration and authentication
"""
user = models.ForeignKey(Administrator, on_delete=models.CASCADE, related_name='webauthn_challenges')
challenge = models.CharField(max_length=255, help_text="Base64 encoded challenge")
challenge_type = models.CharField(max_length=20, choices=[
('registration', 'Registration'),
('authentication', 'Authentication'),
])
created_at = models.DateTimeField(auto_now_add=True)
expires_at = models.DateTimeField()
used = models.BooleanField(default=False)
metadata = models.TextField(default='{}', help_text="Additional challenge metadata as JSON")
class Meta:
db_table = 'webauthn_challenges'
verbose_name = 'WebAuthn Challenge'
verbose_name_plural = 'WebAuthn Challenges'
indexes = [
models.Index(fields=['user', 'challenge_type', 'used']),
models.Index(fields=['expires_at']),
]
def __str__(self):
return f"{self.user.userName} - {self.challenge_type} ({self.challenge[:16]}...)"
def is_expired(self):
"""Check if challenge has expired"""
return datetime.now() > self.expires_at
def get_challenge_bytes(self):
"""Get challenge as bytes"""
return base64.urlsafe_b64decode(self.challenge + '==')
def get_metadata(self):
"""Get metadata as dict"""
try:
return json.loads(self.metadata)
except:
return {}
def set_metadata(self, data):
"""Set metadata from dict"""
self.metadata = json.dumps(data)
def mark_used(self):
"""Mark challenge as used"""
self.used = True
self.save(update_fields=['used'])
class WebAuthnSession(models.Model):
"""
Model to store WebAuthn session data for ongoing operations
"""
user = models.ForeignKey(Administrator, on_delete=models.CASCADE, related_name='webauthn_sessions')
session_id = models.CharField(max_length=255, unique=True, help_text="Unique session identifier")
session_type = models.CharField(max_length=20, choices=[
('registration', 'Registration'),
('authentication', 'Authentication'),
])
data = models.TextField(help_text="Session data as JSON")
created_at = models.DateTimeField(auto_now_add=True)
expires_at = models.DateTimeField()
class Meta:
db_table = 'webauthn_sessions'
verbose_name = 'WebAuthn Session'
verbose_name_plural = 'WebAuthn Sessions'
indexes = [
models.Index(fields=['session_id']),
models.Index(fields=['expires_at']),
]
def __str__(self):
return f"{self.user.userName} - {self.session_type} ({self.session_id[:16]}...)"
def is_expired(self):
"""Check if session has expired"""
return datetime.now() > self.expires_at
def get_data(self):
"""Get session data as dict"""
try:
return json.loads(self.data)
except:
return {}
def set_data(self, data):
"""Set session data from dict"""
self.data = json.dumps(data)
@classmethod
def create_session(cls, user, session_type, data, duration_minutes=10):
"""Create a new WebAuthn session"""
import uuid
session_id = str(uuid.uuid4())
expires_at = datetime.now() + timedelta(minutes=duration_minutes)
session = cls.objects.create(
user=user,
session_id=session_id,
session_type=session_type,
data=json.dumps(data),
expires_at=expires_at
)
return session
class WebAuthnSettings(models.Model):
"""
Model to store WebAuthn configuration settings
"""
user = models.OneToOneField(Administrator, on_delete=models.CASCADE, related_name='webauthn_settings')
enabled = models.BooleanField(default=False, help_text="Whether WebAuthn is enabled for this user")
require_passkey = models.BooleanField(default=False, help_text="Require passkey for login (passwordless)")
allow_multiple_credentials = models.BooleanField(default=True, help_text="Allow multiple passkeys per user")
max_credentials = models.IntegerField(default=10, help_text="Maximum number of passkeys allowed")
timeout_seconds = models.IntegerField(default=60, help_text="WebAuthn operation timeout in seconds")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'webauthn_settings'
verbose_name = 'WebAuthn Settings'
verbose_name_plural = 'WebAuthn Settings'
def __str__(self):
return f"WebAuthn Settings for {self.user.userName}"
@classmethod
def get_or_create_settings(cls, user):
"""Get or create WebAuthn settings for a user"""
settings, created = cls.objects.get_or_create(
user=user,
defaults={
'enabled': False,
'require_passkey': False,
'allow_multiple_credentials': True,
'max_credentials': 10,
'timeout_seconds': 60,
}
)
return settings
def can_add_credential(self):
"""Check if user can add another credential"""
if not self.allow_multiple_credentials:
return WebAuthnCredential.objects.filter(user=self.user, is_active=True).count() == 0
return WebAuthnCredential.objects.filter(user=self.user, is_active=True).count() < self.max_credentials

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from django.urls import path
from . import webauthn_views
urlpatterns = [
# WebAuthn Registration
path('registration/start/', webauthn_views.webauthn_registration_start, name='webauthn_registration_start'),
path('registration/complete/', webauthn_views.webauthn_registration_complete, name='webauthn_registration_complete'),
# WebAuthn Authentication
path('authentication/start/', webauthn_views.webauthn_authentication_start, name='webauthn_authentication_start'),
path('authentication/complete/', webauthn_views.webauthn_authentication_complete, name='webauthn_authentication_complete'),
# WebAuthn Credential Management
path('credentials/<str:username>/', webauthn_views.webauthn_credentials_list, name='webauthn_credentials_list'),
path('credential/delete/', webauthn_views.webauthn_credential_delete, name='webauthn_credential_delete'),
path('credential/update/', webauthn_views.webauthn_credential_update, name='webauthn_credential_update'),
# WebAuthn Settings
path('settings/update/', webauthn_views.webauthn_settings_update, name='webauthn_settings_update'),
# WebAuthn Maintenance
path('cleanup/', webauthn_views.webauthn_cleanup, name='webauthn_cleanup'),
]

View File

@ -0,0 +1,463 @@
# -*- coding: utf-8 -*-
import json
import base64
from django.shortcuts import render, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views import View
from .models import Administrator
from .webauthn_backend import WebAuthnBackend
from .webauthn_models import WebAuthnSettings, WebAuthnCredential
from plogical.acl import ACLManager
import logging
logger = logging.getLogger(__name__)
class WebAuthnAPIView(View):
"""Base class for WebAuthn API views"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.webauthn = WebAuthnBackend()
def json_response(self, data, status=200):
"""Return JSON response"""
return HttpResponse(
json.dumps(data, ensure_ascii=False),
content_type='application/json',
status=status
)
def error_response(self, message, status=400):
"""Return error response"""
return self.json_response({
'success': False,
'error': message
}, status)
@method_decorator(csrf_exempt, name='dispatch')
class WebAuthnRegistrationStart(WebAuthnAPIView):
"""Start WebAuthn registration process"""
def post(self, request):
try:
data = json.loads(request.body)
username = data.get('username')
credential_name = data.get('credential_name', '')
if not username:
return self.error_response('Username is required')
try:
user = Administrator.objects.get(userName=username)
except Administrator.DoesNotExist:
return self.error_response('User not found', 404)
# Check if user has permission to register WebAuthn
if hasattr(request, 'session') and 'userID' in request.session:
current_user_id = request.session['userID']
current_user = Administrator.objects.get(pk=current_user_id)
current_acl = ACLManager.loadedACL(current_user_id)
# Allow if admin, user is modifying themselves, or user is owned by current user
if not (current_acl['admin'] == 1 or
user.pk == current_user.pk or
user.owner == current_user.pk):
return self.error_response('Unauthorized access', 403)
result = self.webauthn.create_registration_challenge(user, credential_name)
return self.json_response(result)
except json.JSONDecodeError:
return self.error_response('Invalid JSON')
except Exception as e:
logger.error(f"Error in registration start: {str(e)}")
return self.error_response(f'Internal server error: {str(e)}', 500)
@method_decorator(csrf_exempt, name='dispatch')
class WebAuthnRegistrationComplete(WebAuthnAPIView):
"""Complete WebAuthn registration process"""
def post(self, request):
try:
data = json.loads(request.body)
challenge_id = data.get('challenge_id')
credential_data = data.get('credential')
client_data_json = data.get('client_data_json')
attestation_object = data.get('attestation_object')
if not all([challenge_id, credential_data, client_data_json, attestation_object]):
return self.error_response('Missing required fields')
result = self.webauthn.verify_registration(
challenge_id=challenge_id,
credential_data=credential_data,
client_data_json=client_data_json,
attestation_object=attestation_object
)
return self.json_response(result)
except json.JSONDecodeError:
return self.error_response('Invalid JSON')
except Exception as e:
logger.error(f"Error in registration complete: {str(e)}")
return self.error_response(f'Internal server error: {str(e)}', 500)
@method_decorator(csrf_exempt, name='dispatch')
class WebAuthnAuthenticationStart(WebAuthnAPIView):
"""Start WebAuthn authentication process"""
def post(self, request):
try:
data = json.loads(request.body)
username = data.get('username')
if not username:
return self.error_response('Username is required')
try:
user = Administrator.objects.get(userName=username)
except Administrator.DoesNotExist:
return self.error_response('User not found', 404)
result = self.webauthn.create_authentication_challenge(user)
return self.json_response(result)
except json.JSONDecodeError:
return self.error_response('Invalid JSON')
except Exception as e:
logger.error(f"Error in authentication start: {str(e)}")
return self.error_response(f'Internal server error: {str(e)}', 500)
@method_decorator(csrf_exempt, name='dispatch')
class WebAuthnAuthenticationComplete(WebAuthnAPIView):
"""Complete WebAuthn authentication process"""
def post(self, request):
try:
data = json.loads(request.body)
challenge_id = data.get('challenge_id')
credential_data = data.get('credential')
client_data_json = data.get('client_data_json')
authenticator_data = data.get('authenticator_data')
if not all([challenge_id, credential_data, client_data_json, authenticator_data]):
return self.error_response('Missing required fields')
result = self.webauthn.verify_authentication(
challenge_id=challenge_id,
credential_data=credential_data,
client_data_json=client_data_json,
authenticator_data=authenticator_data
)
if result['success']:
# Set session for successful authentication
request.session['userID'] = result['user_id']
request.session['webauthn_auth'] = True
request.session.set_expiry(43200) # 12 hours
# Log successful authentication
logger.info(f"WebAuthn authentication successful for user ID: {result['user_id']}")
return self.json_response(result)
except json.JSONDecodeError:
return self.error_response('Invalid JSON')
except Exception as e:
logger.error(f"Error in authentication complete: {str(e)}")
return self.error_response(f'Internal server error: {str(e)}', 500)
@method_decorator(csrf_exempt, name='dispatch')
class WebAuthnCredentialsList(WebAuthnAPIView):
"""List WebAuthn credentials for a user"""
def get(self, request, username=None):
try:
if not username:
return self.error_response('Username is required')
try:
user = Administrator.objects.get(userName=username)
except Administrator.DoesNotExist:
return self.error_response('User not found', 404)
# Check permissions
if hasattr(request, 'session') and 'userID' in request.session:
current_user_id = request.session['userID']
current_user = Administrator.objects.get(pk=current_user_id)
current_acl = ACLManager.loadedACL(current_user_id)
if not (current_acl['admin'] == 1 or
user.pk == current_user.pk or
user.owner == current_user.pk):
return self.error_response('Unauthorized access', 403)
credentials = self.webauthn.get_user_credentials(user)
settings = WebAuthnSettings.get_or_create_settings(user)
return self.json_response({
'success': True,
'credentials': credentials,
'settings': {
'enabled': settings.enabled,
'require_passkey': settings.require_passkey,
'allow_multiple_credentials': settings.allow_multiple_credentials,
'max_credentials': settings.max_credentials,
'can_add_credential': settings.can_add_credential(),
}
})
except Exception as e:
logger.error(f"Error listing credentials: {str(e)}")
return self.error_response(f'Internal server error: {str(e)}', 500)
@method_decorator(csrf_exempt, name='dispatch')
class WebAuthnCredentialDelete(WebAuthnAPIView):
"""Delete a WebAuthn credential"""
def post(self, request):
try:
data = json.loads(request.body)
username = data.get('username')
credential_id = data.get('credential_id')
if not username or not credential_id:
return self.error_response('Username and credential ID are required')
try:
user = Administrator.objects.get(userName=username)
except Administrator.DoesNotExist:
return self.error_response('User not found', 404)
# Check permissions
if hasattr(request, 'session') and 'userID' in request.session:
current_user_id = request.session['userID']
current_user = Administrator.objects.get(pk=current_user_id)
current_acl = ACLManager.loadedACL(current_user_id)
if not (current_acl['admin'] == 1 or
user.pk == current_user.pk or
user.owner == current_user.pk):
return self.error_response('Unauthorized access', 403)
result = self.webauthn.delete_credential(user, credential_id)
return self.json_response(result)
except json.JSONDecodeError:
return self.error_response('Invalid JSON')
except Exception as e:
logger.error(f"Error deleting credential: {str(e)}")
return self.error_response(f'Internal server error: {str(e)}', 500)
@method_decorator(csrf_exempt, name='dispatch')
class WebAuthnCredentialUpdate(WebAuthnAPIView):
"""Update WebAuthn credential name"""
def post(self, request):
try:
data = json.loads(request.body)
username = data.get('username')
credential_id = data.get('credential_id')
new_name = data.get('new_name')
if not all([username, credential_id, new_name]):
return self.error_response('Username, credential ID, and new name are required')
try:
user = Administrator.objects.get(userName=username)
except Administrator.DoesNotExist:
return self.error_response('User not found', 404)
# Check permissions
if hasattr(request, 'session') and 'userID' in request.session:
current_user_id = request.session['userID']
current_user = Administrator.objects.get(pk=current_user_id)
current_acl = ACLManager.loadedACL(current_user_id)
if not (current_acl['admin'] == 1 or
user.pk == current_user.pk or
user.owner == current_user.pk):
return self.error_response('Unauthorized access', 403)
result = self.webauthn.update_credential_name(user, credential_id, new_name)
return self.json_response(result)
except json.JSONDecodeError:
return self.error_response('Invalid JSON')
except Exception as e:
logger.error(f"Error updating credential: {str(e)}")
return self.error_response(f'Internal server error: {str(e)}', 500)
@method_decorator(csrf_exempt, name='dispatch')
class WebAuthnSettingsUpdate(WebAuthnAPIView):
"""Update WebAuthn settings for a user"""
def post(self, request):
try:
data = json.loads(request.body)
username = data.get('username')
enabled = data.get('enabled')
require_passkey = data.get('require_passkey')
allow_multiple_credentials = data.get('allow_multiple_credentials')
max_credentials = data.get('max_credentials')
timeout_seconds = data.get('timeout_seconds')
if not username:
return self.error_response('Username is required')
try:
user = Administrator.objects.get(userName=username)
except Administrator.DoesNotExist:
return self.error_response('User not found', 404)
# Check permissions
if hasattr(request, 'session') and 'userID' in request.session:
current_user_id = request.session['userID']
current_user = Administrator.objects.get(pk=current_user_id)
current_acl = ACLManager.loadedACL(current_user_id)
if not (current_acl['admin'] == 1 or
user.pk == current_user.pk or
user.owner == current_user.pk):
return self.error_response('Unauthorized access', 403)
settings = WebAuthnSettings.get_or_create_settings(user)
if enabled is not None:
settings.enabled = bool(enabled)
if require_passkey is not None:
settings.require_passkey = bool(require_passkey)
if allow_multiple_credentials is not None:
settings.allow_multiple_credentials = bool(allow_multiple_credentials)
if max_credentials is not None:
settings.max_credentials = int(max_credentials)
if timeout_seconds is not None:
settings.timeout_seconds = int(timeout_seconds)
settings.save()
return self.json_response({
'success': True,
'message': 'Settings updated successfully',
'settings': {
'enabled': settings.enabled,
'require_passkey': settings.require_passkey,
'allow_multiple_credentials': settings.allow_multiple_credentials,
'max_credentials': settings.max_credentials,
'timeout_seconds': settings.timeout_seconds,
}
})
except json.JSONDecodeError:
return self.error_response('Invalid JSON')
except Exception as e:
logger.error(f"Error updating settings: {str(e)}")
return self.error_response(f'Internal server error: {str(e)}', 500)
@method_decorator(csrf_exempt, name='dispatch')
class WebAuthnCleanup(WebAuthnAPIView):
"""Cleanup expired WebAuthn data"""
def post(self, request):
try:
# Check if user is admin
if not (hasattr(request, 'session') and 'userID' in request.session):
return self.error_response('Authentication required', 401)
current_user_id = request.session['userID']
current_acl = ACLManager.loadedACL(current_user_id)
if current_acl['admin'] != 1:
return self.error_response('Admin access required', 403)
# Cleanup expired data
self.webauthn.cleanup_expired_challenges()
self.webauthn.cleanup_expired_sessions()
return self.json_response({
'success': True,
'message': 'Cleanup completed successfully'
})
except Exception as e:
logger.error(f"Error during cleanup: {str(e)}")
return self.error_response(f'Internal server error: {str(e)}', 500)
# Traditional function-based views for easier integration
@csrf_exempt
def webauthn_registration_start(request):
"""Start WebAuthn registration - function view"""
view = WebAuthnRegistrationStart()
return view.post(request)
@csrf_exempt
def webauthn_registration_complete(request):
"""Complete WebAuthn registration - function view"""
view = WebAuthnRegistrationComplete()
return view.post(request)
@csrf_exempt
def webauthn_authentication_start(request):
"""Start WebAuthn authentication - function view"""
view = WebAuthnAuthenticationStart()
return view.post(request)
@csrf_exempt
def webauthn_authentication_complete(request):
"""Complete WebAuthn authentication - function view"""
view = WebAuthnAuthenticationComplete()
return view.post(request)
@csrf_exempt
def webauthn_credentials_list(request, username):
"""List WebAuthn credentials - function view"""
view = WebAuthnCredentialsList()
return view.get(request, username)
@csrf_exempt
def webauthn_credential_delete(request):
"""Delete WebAuthn credential - function view"""
view = WebAuthnCredentialDelete()
return view.post(request)
@csrf_exempt
def webauthn_credential_update(request):
"""Update WebAuthn credential - function view"""
view = WebAuthnCredentialUpdate()
return view.post(request)
@csrf_exempt
def webauthn_settings_update(request):
"""Update WebAuthn settings - function view"""
view = WebAuthnSettingsUpdate()
return view.post(request)
@csrf_exempt
def webauthn_cleanup(request):
"""Cleanup WebAuthn data - function view"""
view = WebAuthnCleanup()
return view.post(request)

View File

@ -179,6 +179,121 @@ app.controller('modifyUser', function ($scope, $http) {
document.body.removeChild(tempTextarea);
}
};
// WebAuthn Functions
$scope.loadWebAuthnData = function() {
if (!$scope.accountUsername) return;
var url = '/webauthn/credentials/' + $scope.accountUsername + '/';
$http.get(url).then(function(response) {
if (response.data.success) {
$scope.webauthnCredentials = response.data.credentials;
$scope.webauthnEnabled = response.data.settings.enabled;
$scope.webauthnRequirePasskey = response.data.settings.require_passkey;
$scope.webauthnAllowMultiple = response.data.settings.allow_multiple_credentials;
$scope.webauthnMaxCredentials = response.data.settings.max_credentials;
$scope.canAddCredential = response.data.settings.can_add_credential;
}
}, function(error) {
console.error('Error loading WebAuthn data:', error);
});
};
$scope.toggleWebAuthn = function() {
if ($scope.webauthnEnabled) {
$scope.loadWebAuthnData();
} else {
$scope.webauthnCredentials = [];
$scope.canAddCredential = true;
}
};
$scope.registerNewPasskey = function() {
if (!window.cyberPanelWebAuthn) {
alert('WebAuthn is not supported in this browser');
return;
}
var credentialName = prompt('Enter a name for this passkey:', 'Passkey ' + new Date().toLocaleDateString());
if (!credentialName) return;
window.cyberPanelWebAuthn.registerPasskey($scope.accountUsername, credentialName)
.then(function(response) {
if (response.success) {
$scope.loadWebAuthnData();
$scope.$apply();
}
})
.catch(function(error) {
console.error('Error registering passkey:', error);
});
};
$scope.deleteCredential = function(credentialId) {
if (!confirm('Are you sure you want to delete this passkey?')) return;
if (!window.cyberPanelWebAuthn) {
alert('WebAuthn is not supported in this browser');
return;
}
window.cyberPanelWebAuthn.deleteCredential($scope.accountUsername, credentialId)
.then(function(response) {
if (response.success) {
$scope.loadWebAuthnData();
$scope.$apply();
}
})
.catch(function(error) {
console.error('Error deleting credential:', error);
});
};
$scope.updateCredentialName = function(credentialId, newName) {
if (!window.cyberPanelWebAuthn) return;
window.cyberPanelWebAuthn.updateCredentialName($scope.accountUsername, credentialId, newName)
.then(function(response) {
if (response.success) {
$scope.loadWebAuthnData();
$scope.$apply();
}
})
.catch(function(error) {
console.error('Error updating credential name:', error);
});
};
$scope.refreshCredentials = function() {
$scope.loadWebAuthnData();
};
$scope.saveWebAuthnSettings = function() {
if (!window.cyberPanelWebAuthn) {
alert('WebAuthn is not supported in this browser');
return;
}
var settings = {
enabled: $scope.webauthnEnabled,
require_passkey: $scope.webauthnRequirePasskey,
allow_multiple_credentials: $scope.webauthnAllowMultiple,
max_credentials: $scope.webauthnMaxCredentials,
timeout_seconds: $scope.webauthnTimeout
};
window.cyberPanelWebAuthn.updateSettings($scope.accountUsername, settings)
.then(function(response) {
if (response.success) {
$scope.loadWebAuthnData();
$scope.$apply();
}
})
.catch(function(error) {
console.error('Error updating WebAuthn settings:', error);
});
};
$scope.fetchUserDetails = function () {
@ -223,6 +338,18 @@ app.controller('modifyUser', function ($scope, $http) {
$scope.secretKey = userDetails.secretKey;
$scope.formattedSecretKey = userDetails.secretKey.match(/.{1,4}/g).join(' ');
}
// Initialize WebAuthn settings
$scope.webauthnEnabled = false;
$scope.webauthnRequirePasskey = false;
$scope.webauthnAllowMultiple = true;
$scope.webauthnMaxCredentials = 10;
$scope.webauthnTimeout = 60;
$scope.webauthnCredentials = [];
$scope.canAddCredential = true;
// Load WebAuthn settings and credentials
$scope.loadWebAuthnData();
qrCode.set({
value: userDetails.otpauth
@ -320,7 +447,13 @@ app.controller('modifyUser', function ($scope, $http) {
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
$http.post(url, data, config).then(function(response) {
ListInitialDatas(response);
// Save WebAuthn settings after successful user modification
if (response.data.saveStatus == 1) {
$scope.saveWebAuthnSettings();
}
}, cantLoadInitialDatas);
function ListInitialDatas(response) {

View File

@ -321,6 +321,97 @@
</div>
</div>
</div>
<!-- WebAuthn Passkey Management Section -->
<div class="form-group" style="margin-top: 2rem;">
<label class="form-label">{% trans "Passkey Authentication (WebAuthn)" %}</label>
<div class="checkbox-wrapper">
<label>
<input ng-model="webauthnEnabled" type="checkbox" ng-change="toggleWebAuthn()">
{% trans "Enable Passkey Authentication" %}
</label>
<p class="help-text mb-2">{% trans "Use passkeys for secure, passwordless login" %}</p>
</div>
<div ng-show="webauthnEnabled" class="mt-3">
<div class="checkbox-wrapper">
<label>
<input ng-model="webauthnRequirePasskey" type="checkbox">
{% trans "Require Passkey for Login (Passwordless)" %}
</label>
<p class="help-text mb-2">{% trans "When enabled, users must use passkeys to login (password becomes optional)" %}</p>
</div>
<div class="checkbox-wrapper">
<label>
<input ng-model="webauthnAllowMultiple" type="checkbox">
{% trans "Allow Multiple Passkeys" %}
</label>
<p class="help-text mb-2">{% trans "Allow users to register multiple passkeys for backup access" %}</p>
</div>
<div class="form-group">
<label class="form-label">{% trans "Maximum Passkeys" %}</label>
<input type="number" class="form-control" ng-model="webauthnMaxCredentials" min="1" max="20" style="max-width: 150px;">
<p class="help-text">{% trans "Maximum number of passkeys allowed per user" %}</p>
</div>
<div class="form-group">
<label class="form-label">{% trans "Passkey Timeout (seconds)" %}</label>
<input type="number" class="form-control" ng-model="webauthnTimeout" min="30" max="300" style="max-width: 150px;">
<p class="help-text">{% trans "How long to wait for passkey interaction" %}</p>
</div>
<!-- Passkey Management -->
<div class="mt-4">
<h5>{% trans "Manage Passkeys" %}</h5>
<div ng-show="webauthnCredentials.length === 0" class="alert alert-info">
<i class="fa fa-info-circle"></i> {% trans "No passkeys registered yet" %}
</div>
<div ng-show="webauthnCredentials.length > 0" class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "Created" %}</th>
<th>{% trans "Last Used" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="cred in webauthnCredentials">
<td>
<input type="text" class="form-control form-control-sm" ng-model="cred.name"
ng-blur="updateCredentialName(cred.id, cred.name)" style="max-width: 200px;">
</td>
<td>{{ cred.created_at | date:'short' }}</td>
<td>{{ cred.last_used | date:'short' || 'Never' }}</td>
<td>
<button type="button" class="btn btn-sm btn-danger" ng-click="deleteCredential(cred.id)">
<i class="fa fa-trash"></i> {% trans "Delete" %}
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="mt-3">
<button type="button" class="btn btn-primary" ng-click="registerNewPasskey()" ng-disabled="!canAddCredential">
<i class="fa fa-plus"></i> {% trans "Register New Passkey" %}
</button>
<button type="button" class="btn btn-secondary" ng-click="refreshCredentials()">
<i class="fa fa-refresh"></i> {% trans "Refresh" %}
</button>
</div>
<div ng-show="!canAddCredential" class="alert alert-warning mt-2">
<i class="fa fa-exclamation-triangle"></i> {% trans "Maximum number of passkeys reached" %}
</div>
</div>
</div>
</div>
<div class="form-group" style="margin-top: 2rem;">
<button type="button" ng-click="modifyUser()" class="btn-primary">
<i class="fa fa-save"></i> {% trans "Save Changes" %}

202
utils/README.md Normal file
View File

@ -0,0 +1,202 @@
# CyberPanel Utility Scripts
This folder contains utility scripts for CyberPanel installation, maintenance, and troubleshooting.
## 📁 Folder Structure
```
utils/
├── README.md # This file
├── windows/ # Windows-specific scripts
│ ├── cyberpanel_install.bat # Windows installation script
│ ├── cyberpanel_upgrade.bat # Windows upgrade script
│ └── install_webauthn.bat # WebAuthn setup for Windows
└── linux/ # Linux-specific scripts
└── install_webauthn.sh # WebAuthn setup for Linux
```
## 🪟 Windows Scripts
**⚠️ IMPORTANT**: These Windows scripts are for **development and testing purposes only**. CyberPanel is designed for Linux systems and does not officially support Windows for production use.
### Development Scripts
#### `cyberpanel_install.bat`
**Purpose**: Set up CyberPanel development environment on Windows
**Requirements**:
- Windows 7/8.1/10/11
- Administrator privileges
- Python 3.8+ installed
- Internet connection
**Limitations**:
- No web server (OpenLiteSpeed/LiteSpeed)
- No system services (MariaDB, PowerDNS)
- Limited functionality
- Development/testing only
**Usage**:
1. Right-click and select "Run as administrator"
2. Follow the on-screen prompts
3. Access CyberPanel at `http://localhost:8090`
**Features**:
- Automatic Python environment setup
- Virtual environment creation
- Source code download
- Requirements installation
- Admin user creation
- Windows service setup
#### `cyberpanel_upgrade.bat`
**Purpose**: Upgrade existing CyberPanel installation
**Requirements**:
- Existing CyberPanel installation
- Administrator privileges
- Internet connection
**Usage**:
1. Right-click and select "Run as administrator"
2. Script will automatically backup and upgrade
3. Restart CyberPanel after completion
**Features**:
- Automatic backup creation
- Source code update
- Requirements upgrade
- Database migration
- Service restart
#### `install_webauthn.bat`
**Purpose**: Set up WebAuthn/Passkey authentication
**Requirements**:
- CyberPanel already installed
- Administrator privileges
**Usage**:
1. Run as administrator
2. Follow configuration prompts
3. Access User Management to enable WebAuthn
## 🐧 Linux Scripts
### Installation Scripts
#### `install_webauthn.sh`
**Purpose**: Set up WebAuthn/Passkey authentication on Linux
**Requirements**:
- CyberPanel installed
- Root privileges
**Usage**:
```bash
sudo ./install_webauthn.sh
```
**Features**:
- Database migration
- Static files setup
- Service configuration
- Permission fixing
## 🚀 Quick Start
### Windows Users
1. **First Installation**:
```cmd
# Run as administrator
cyberpanel_install.bat
```
2. **Upgrade Existing Installation**:
```cmd
# Run as administrator
cyberpanel_upgrade.bat
```
3. **Enable WebAuthn**:
```cmd
# Run as administrator
install_webauthn.bat
```
### Linux Users
1. **Fix Installation Issues**:
```bash
sudo ./fix_cyberpanel_install.sh
```
2. **Enable WebAuthn**:
```bash
sudo ./install_webauthn.sh
```
## 🔧 Troubleshooting
### Common Windows Issues
#### "Python not found" Error
- **Solution**: Install Python 3.8+ from [python.org](https://python.org)
- **Important**: Check "Add Python to PATH" during installation
#### "Access Denied" Error
- **Solution**: Right-click script and select "Run as administrator"
- **Alternative**: Open Command Prompt as administrator
#### "Failed to install requirements" Error
- **Solution**: Check internet connection
- **Alternative**: Try running script again
- **Advanced**: Install requirements manually with pip
### Common Linux Issues
#### "Permission denied" Error
- **Solution**: Run with `sudo` or as root user
- **Example**: `sudo ./script.sh`
#### "Command not found" Error
- **Solution**: Ensure script is executable
- **Fix**: `chmod +x script.sh`
#### "Django not found" Error
- **Solution**: Run the fix script first
- **Alternative**: Reinstall CyberPanel
## 📚 Additional Resources
### Documentation
- **Main Guide**: [2FA Authentication Guide](../guides/2FA_AUTHENTICATION_GUIDE.md)
- **Troubleshooting**: [Troubleshooting Guide](../guides/TROUBLESHOOTING.md)
- **Complete Index**: [Guides Index](../guides/INDEX.md)
### Support
- **CyberPanel Forums**: https://community.cyberpanel.net
- **GitHub Issues**: https://github.com/usmannasir/cyberpanel/issues
- **Discord Server**: https://discord.gg/cyberpanel
## ⚠️ Important Notes
### Security
- Always run scripts as administrator/root when required
- Change default passwords immediately after installation
- Keep CyberPanel updated regularly
### Backups
- The upgrade script automatically creates backups
- Store backups in a safe location
- Test restore procedures regularly
### Compatibility
- Windows scripts tested on Windows 7/8.1/10/11
- Linux scripts tested on Ubuntu, Debian, AlmaLinux, RockyLinux
- Python 3.8+ required for all scripts
---
**Note**: These utility scripts are provided as-is. Always test in a non-production environment first and ensure you have proper backups before running any scripts.
*Last updated: January 2025*

View File

@ -0,0 +1,150 @@
#!/bin/bash
# WebAuthn Installation Script for CyberPanel
# This script helps install and configure WebAuthn/Passkey authentication
echo "=========================================="
echo "CyberPanel WebAuthn Installation Script"
echo "=========================================="
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root (use sudo)"
exit 1
fi
# Check if CyberPanel is installed
if [ ! -d "/usr/local/CyberCP" ]; then
echo "Error: CyberPanel not found at /usr/local/CyberCP"
echo "Please install CyberPanel first"
exit 1
fi
echo "✓ CyberPanel installation found"
# Navigate to CyberPanel directory
cd /usr/local/CyberCP
# Check if Django is available
if ! python3 -c "import django" 2>/dev/null; then
echo "Error: Django not found. Please ensure CyberPanel is properly installed"
exit 1
fi
echo "✓ Django installation found"
# Run database migrations
echo "Running database migrations..."
python3 manage.py makemigrations loginSystem
if [ $? -eq 0 ]; then
echo "✓ Database migrations created"
else
echo "Error: Failed to create migrations"
exit 1
fi
python3 manage.py migrate
if [ $? -eq 0 ]; then
echo "✓ Database migrations applied"
else
echo "Error: Failed to apply migrations"
exit 1
fi
# Check if static files directory exists
if [ ! -d "static/loginSystem" ]; then
echo "Creating static files directory..."
mkdir -p static/loginSystem
fi
# Copy WebAuthn JavaScript file if it doesn't exist
if [ ! -f "static/loginSystem/webauthn.js" ]; then
echo "WebAuthn JavaScript file not found. Please ensure webauthn.js is in static/loginSystem/"
echo "You can copy it from the source files"
fi
# Set proper permissions
echo "Setting file permissions..."
chown -R lscpd:lscpd /usr/local/CyberCP/static/loginSystem/
chmod -R 755 /usr/local/CyberCP/static/loginSystem/
# Test the installation
echo "Testing WebAuthn installation..."
python3 -c "
import sys
sys.path.append('/usr/local/CyberCP')
try:
from loginSystem.webauthn_models import WebAuthnCredential, WebAuthnChallenge, WebAuthnSettings
print('✓ WebAuthn models imported successfully')
except ImportError as e:
print(f'Error importing WebAuthn models: {e}')
sys.exit(1)
try:
from loginSystem.webauthn_backend import WebAuthnBackend
backend = WebAuthnBackend()
print('✓ WebAuthn backend initialized successfully')
except Exception as e:
print(f'Error initializing WebAuthn backend: {e}')
sys.exit(1)
"
if [ $? -eq 0 ]; then
echo "✓ WebAuthn installation test passed"
else
echo "Error: WebAuthn installation test failed"
exit 1
fi
# Create configuration file
echo "Creating WebAuthn configuration..."
cat > /usr/local/CyberCP/webauthn_config.py << 'EOF'
# WebAuthn Configuration for CyberPanel
# Update these values according to your setup
WEBAUTHN_CONFIG = {
'RP_ID': 'cyberpanel.local', # Replace with your actual domain
'RP_NAME': 'CyberPanel',
'ORIGIN': 'https://cyberpanel.local:8090', # Replace with your actual origin
'CHALLENGE_TIMEOUT': 300, # 5 minutes
'MAX_CREDENTIALS_PER_USER': 10,
'DEFAULT_TIMEOUT_SECONDS': 60,
}
# Instructions:
# 1. Update RP_ID to your actual domain (e.g., 'yourdomain.com')
# 2. Update ORIGIN to your actual origin (e.g., 'https://yourdomain.com:8090')
# 3. Restart CyberPanel after making changes
EOF
echo "✓ Configuration file created at /usr/local/CyberCP/webauthn_config.py"
# Restart CyberPanel services
echo "Restarting CyberPanel services..."
systemctl restart lscpd
if [ $? -eq 0 ]; then
echo "✓ CyberPanel services restarted"
else
echo "Warning: Failed to restart CyberPanel services. Please restart manually"
fi
echo ""
echo "=========================================="
echo "WebAuthn Installation Complete!"
echo "=========================================="
echo ""
echo "Next steps:"
echo "1. Update the configuration file: /usr/local/CyberCP/webauthn_config.py"
echo "2. Replace 'cyberpanel.local' with your actual domain"
echo "3. Replace 'https://cyberpanel.local:8090' with your actual origin"
echo "4. Restart CyberPanel: systemctl restart lscpd"
echo "5. Access CyberPanel and go to User Management to enable WebAuthn"
echo ""
echo "Features available:"
echo "- Passkey registration and management"
echo "- Passwordless login option"
echo "- Multiple device support"
echo "- Admin management interface"
echo ""
echo "For more information, see: /usr/local/CyberCP/to-do/WEBAUTHN_IMPLEMENTATION.md"
echo ""

View File

@ -0,0 +1,241 @@
@echo off
REM CyberPanel Windows Installation Script
REM This script installs CyberPanel on Windows systems
echo ==========================================
echo CyberPanel Windows Installation Script
echo ==========================================
echo.
REM Check if running as administrator
net session >nul 2>&1
if %errorLevel% neq 0 (
echo ERROR: This script must be run as administrator
echo Please right-click and select "Run as administrator"
pause
exit /b 1
)
echo [OK] Running with administrator privileges
echo.
REM Check Windows version
for /f "tokens=4-5 delims=. " %%i in ('ver') do set VERSION=%%i.%%j
if "%VERSION%" == "10.0" (
echo [OK] Windows 10/11 detected
) else if "%VERSION%" == "6.3" (
echo [OK] Windows 8.1 detected
) else if "%VERSION%" == "6.1" (
echo [OK] Windows 7 detected
) else (
echo [WARNING] Unsupported Windows version detected: %VERSION%
echo CyberPanel may not work properly on this version
pause
)
echo.
echo Checking system requirements...
REM Check if Python is installed
python --version >nul 2>&1
if %errorLevel% neq 0 (
echo [ERROR] Python is not installed or not in PATH
echo Please install Python 3.8+ from https://python.org
echo Make sure to check "Add Python to PATH" during installation
pause
exit /b 1
) else (
for /f "tokens=2" %%i in ('python --version 2^>^&1') do set PYTHON_VERSION=%%i
echo [OK] Python %PYTHON_VERSION% found
)
REM Check if pip is available
pip --version >nul 2>&1
if %errorLevel% neq 0 (
echo [ERROR] pip is not available
echo Please install pip or reinstall Python with pip
pause
exit /b 1
) else (
echo [OK] pip is available
)
REM Check available disk space
for /f "tokens=3" %%i in ('dir /-c %SystemDrive%\ ^| find "bytes free"') do set FREE_SPACE=%%i
if %FREE_SPACE% LSS 10737418240 (
echo [WARNING] Less than 10GB free space available
echo CyberPanel requires at least 10GB of free space
pause
)
echo.
echo ==========================================
echo CyberPanel Installation
echo ==========================================
echo.
REM Create CyberPanel directory
set CYBERPANEL_DIR=C:\usr\local\CyberCP
if not exist "%CYBERPANEL_DIR%" (
echo Creating CyberPanel directory...
mkdir "%CYBERPANEL_DIR%" 2>nul
if %errorLevel% neq 0 (
echo [ERROR] Failed to create directory %CYBERPANEL_DIR%
echo Please ensure you have sufficient permissions
pause
exit /b 1
)
echo [OK] Directory created: %CYBERPANEL_DIR%
) else (
echo [OK] Directory already exists: %CYBERPANEL_DIR%
)
REM Navigate to CyberPanel directory
cd /d "%CYBERPANEL_DIR%"
REM Create virtual environment
echo Creating Python virtual environment...
python -m venv . --system-site-packages
if %errorLevel% neq 0 (
echo [ERROR] Failed to create virtual environment
pause
exit /b 1
)
echo [OK] Virtual environment created
REM Activate virtual environment
echo Activating virtual environment...
call Scripts\activate.bat
if %errorLevel% neq 0 (
echo [ERROR] Failed to activate virtual environment
pause
exit /b 1
)
echo [OK] Virtual environment activated
REM Upgrade pip
echo Upgrading pip...
python -m pip install --upgrade pip setuptools wheel
if %errorLevel% neq 0 (
echo [WARNING] Failed to upgrade pip, continuing anyway...
)
REM Download CyberPanel source
echo Downloading CyberPanel source code...
if exist "cyberpanel" (
echo [INFO] CyberPanel source already exists, updating...
cd cyberpanel
git pull origin stable
if %errorLevel% neq 0 (
echo [WARNING] Failed to update source, using existing version
)
cd ..
) else (
echo Cloning CyberPanel repository...
git clone https://github.com/usmannasir/cyberpanel.git
if %errorLevel% neq 0 (
echo [ERROR] Failed to clone CyberPanel repository
echo Please check your internet connection
pause
exit /b 1
)
echo [OK] Source code downloaded
)
REM Navigate to CyberPanel source
cd cyberpanel
REM Install requirements
echo Installing Python requirements...
echo This may take several minutes...
pip install --default-timeout=3600 -r requirments.txt
if %errorLevel% neq 0 (
echo [ERROR] Failed to install requirements
echo Please check your internet connection and try again
pause
exit /b 1
)
echo [OK] Requirements installed
REM Create necessary directories
echo Creating necessary directories...
if not exist "static" mkdir static
if not exist "logs" mkdir logs
if not exist "static\loginSystem" mkdir static\loginSystem
if not exist "static\userManagment" mkdir static\userManagment
echo [OK] Directories created
REM Set up Django
echo Setting up Django...
python manage.py collectstatic --noinput
if %errorLevel% neq 0 (
echo [WARNING] Failed to collect static files, continuing...
)
REM Create superuser (optional)
echo.
echo ==========================================
echo User Account Setup
echo ==========================================
echo.
set /p CREATE_ADMIN="Do you want to create an admin user now? (y/n): "
if /i "%CREATE_ADMIN%"=="y" (
echo Creating admin user...
python manage.py createsuperuser
if %errorLevel% neq 0 (
echo [WARNING] Failed to create superuser
echo You can create one later with: python manage.py createsuperuser
) else (
echo [OK] Admin user created
)
) else (
echo [INFO] Skipping admin user creation
echo You can create one later with: python manage.py createsuperuser
)
REM Create startup script
echo Creating startup script...
(
echo @echo off
echo cd /d "%CYBERPANEL_DIR%\cyberpanel"
echo call ..\Scripts\activate.bat
echo python manage.py runserver 0.0.0.0:8090
) > start_cyberpanel.bat
REM Create service script
echo Creating Windows service script...
(
echo @echo off
echo REM CyberPanel Windows Service
echo net start CyberPanel
echo if %%errorLevel%% neq 0 ^(
echo echo Starting CyberPanel service...
echo sc create CyberPanel binPath= "%CYBERPANEL_DIR%\cyberpanel\start_cyberpanel.bat" start= auto
echo sc start CyberPanel
echo ^)
) > install_service.bat
echo.
echo ==========================================
echo Installation Complete!
echo ==========================================
echo.
echo CyberPanel has been installed to: %CYBERPANEL_DIR%\cyberpanel
echo.
echo To start CyberPanel:
echo 1. Run: start_cyberpanel.bat
echo 2. Open your browser to: http://localhost:8090
echo.
echo To install as Windows service:
echo 1. Run: install_service.bat as administrator
echo.
echo Default login credentials:
echo Username: admin
echo Password: 123456
echo.
echo IMPORTANT: Change the default password immediately!
echo.
echo For more information, see the documentation at:
echo https://cyberpanel.net/docs/
echo.
pause

View File

@ -0,0 +1,165 @@
@echo off
REM CyberPanel Windows Upgrade Script
REM This script upgrades an existing CyberPanel installation
echo ==========================================
echo CyberPanel Windows Upgrade Script
echo ==========================================
echo.
REM Check if running as administrator
net session >nul 2>&1
if %errorLevel% neq 0 (
echo ERROR: This script must be run as administrator
echo Please right-click and select "Run as administrator"
pause
exit /b 1
)
echo [OK] Running with administrator privileges
echo.
REM Check if CyberPanel is installed
set CYBERPANEL_DIR=C:\usr\local\CyberCP
if not exist "%CYBERPANEL_DIR%" (
echo [ERROR] CyberPanel not found at %CYBERPANEL_DIR%
echo Please run the installation script first
pause
exit /b 1
)
echo [OK] CyberPanel installation found
echo.
REM Create backup
echo Creating backup of current installation...
set BACKUP_DIR=%CYBERPANEL_DIR%_backup_%date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2%%time:~6,2%
set BACKUP_DIR=%BACKUP_DIR: =0%
if exist "%BACKUP_DIR%" rmdir /s /q "%BACKUP_DIR%"
xcopy "%CYBERPANEL_DIR%" "%BACKUP_DIR%" /E /I /H /Y >nul
if %errorLevel% neq 0 (
echo [WARNING] Failed to create backup, continuing anyway...
) else (
echo [OK] Backup created: %BACKUP_DIR%
)
REM Navigate to CyberPanel directory
cd /d "%CYBERPANEL_DIR%"
REM Activate virtual environment
echo Activating virtual environment...
if not exist "Scripts\activate.bat" (
echo [ERROR] Virtual environment not found
echo Please reinstall CyberPanel
pause
exit /b 1
)
call Scripts\activate.bat
if %errorLevel% neq 0 (
echo [ERROR] Failed to activate virtual environment
pause
exit /b 1
)
echo [OK] Virtual environment activated
REM Navigate to CyberPanel source
cd cyberpanel
REM Stop any running CyberPanel processes
echo Stopping CyberPanel processes...
taskkill /f /im python.exe 2>nul
taskkill /f /im lscpd.exe 2>nul
echo [OK] Processes stopped
REM Update source code
echo Updating CyberPanel source code...
git fetch origin
if %errorLevel% neq 0 (
echo [WARNING] Failed to fetch updates, continuing with current version...
) else (
git reset --hard origin/stable
if %errorLevel% neq 0 (
echo [WARNING] Failed to reset to latest version, continuing...
) else (
echo [OK] Source code updated
)
)
REM Upgrade pip and requirements
echo Upgrading Python packages...
python -m pip install --upgrade pip setuptools wheel
if %errorLevel% neq 0 (
echo [WARNING] Failed to upgrade pip, continuing...
)
REM Install/upgrade requirements
echo Installing/upgrading requirements...
pip install --upgrade --default-timeout=3600 -r requirments.txt
if %errorLevel% neq 0 (
echo [ERROR] Failed to install/upgrade requirements
echo Please check your internet connection and try again
pause
exit /b 1
)
echo [OK] Requirements updated
REM Run database migrations
echo Running database migrations...
python manage.py makemigrations
if %errorLevel% neq 0 (
echo [WARNING] Failed to create migrations, continuing...
) else (
python manage.py migrate
if %errorLevel% neq 0 (
echo [WARNING] Failed to apply migrations, continuing...
) else (
echo [OK] Database migrations completed
)
)
REM Collect static files
echo Collecting static files...
python manage.py collectstatic --noinput
if %errorLevel% neq 0 (
echo [WARNING] Failed to collect static files, continuing...
) else (
echo [OK] Static files collected
)
REM Update startup script
echo Updating startup script...
(
echo @echo off
echo cd /d "%CYBERPANEL_DIR%\cyberpanel"
echo call ..\Scripts\activate.bat
echo python manage.py runserver 0.0.0.0:8090
) > start_cyberpanel.bat
REM Test installation
echo Testing installation...
python manage.py check
if %errorLevel% neq 0 (
echo [WARNING] Installation check failed, but continuing...
) else (
echo [OK] Installation check passed
)
echo.
echo ==========================================
echo Upgrade Complete!
echo ==========================================
echo.
echo CyberPanel has been upgraded successfully
echo.
echo To start CyberPanel:
echo 1. Run: start_cyberpanel.bat
echo 2. Open your browser to: http://localhost:8090
echo.
echo Backup location: %BACKUP_DIR%
echo.
echo If you encounter any issues, you can restore from backup:
echo 1. Stop CyberPanel
echo 2. Delete current installation
echo 3. Restore from backup directory
echo.
pause

View File

@ -0,0 +1,127 @@
@echo off
REM WebAuthn Installation Script for CyberPanel (Windows)
REM This script helps install and configure WebAuthn/Passkey authentication
echo ==========================================
echo CyberPanel WebAuthn Installation Script
echo ==========================================
REM Check if running as administrator
net session >nul 2>&1
if %errorLevel% neq 0 (
echo Please run as administrator
pause
exit /b 1
)
REM Check if CyberPanel is installed
if not exist "C:\usr\local\CyberCP" (
echo Error: CyberPanel not found at C:\usr\local\CyberCP
echo Please install CyberPanel first
pause
exit /b 1
)
echo [OK] CyberPanel installation found
REM Navigate to CyberPanel directory
cd /d C:\usr\local\CyberCP
REM Check if Python is available
python --version >nul 2>&1
if %errorLevel% neq 0 (
echo Error: Python not found. Please ensure CyberPanel is properly installed
pause
exit /b 1
)
echo [OK] Python installation found
REM Run database migrations
echo Running database migrations...
python manage.py makemigrations loginSystem
if %errorLevel% equ 0 (
echo [OK] Database migrations created
) else (
echo Error: Failed to create migrations
pause
exit /b 1
)
python manage.py migrate
if %errorLevel% equ 0 (
echo [OK] Database migrations applied
) else (
echo Error: Failed to apply migrations
pause
exit /b 1
)
REM Check if static files directory exists
if not exist "static\loginSystem" (
echo Creating static files directory...
mkdir static\loginSystem
)
REM Check if WebAuthn JavaScript file exists
if not exist "static\loginSystem\webauthn.js" (
echo Warning: WebAuthn JavaScript file not found
echo Please ensure webauthn.js is in static\loginSystem\
echo You can copy it from the source files
)
REM Test the installation
echo Testing WebAuthn installation...
python -c "import sys; sys.path.append('C:/usr/local/CyberCP'); from loginSystem.webauthn_models import WebAuthnCredential, WebAuthnChallenge, WebAuthnSettings; print('[OK] WebAuthn models imported successfully')"
if %errorLevel% equ 0 (
echo [OK] WebAuthn installation test passed
) else (
echo Error: WebAuthn installation test failed
pause
exit /b 1
)
REM Create configuration file
echo Creating WebAuthn configuration...
(
echo # WebAuthn Configuration for CyberPanel
echo # Update these values according to your setup
echo.
echo WEBAUTHN_CONFIG = {
echo 'RP_ID': 'cyberpanel.local', # Replace with your actual domain
echo 'RP_NAME': 'CyberPanel',
echo 'ORIGIN': 'https://cyberpanel.local:8090', # Replace with your actual origin
echo 'CHALLENGE_TIMEOUT': 300, # 5 minutes
echo 'MAX_CREDENTIALS_PER_USER': 10,
echo 'DEFAULT_TIMEOUT_SECONDS': 60,
echo }
echo.
echo # Instructions:
echo # 1. Update RP_ID to your actual domain ^(e.g., 'yourdomain.com'^)
echo # 2. Update ORIGIN to your actual origin ^(e.g., 'https://yourdomain.com:8090'^)
echo # 3. Restart CyberPanel after making changes
) > webauthn_config.py
echo [OK] Configuration file created at C:\usr\local\CyberCP\webauthn_config.py
echo.
echo ==========================================
echo WebAuthn Installation Complete!
echo ==========================================
echo.
echo Next steps:
echo 1. Update the configuration file: C:\usr\local\CyberCP\webauthn_config.py
echo 2. Replace 'cyberpanel.local' with your actual domain
echo 3. Replace 'https://cyberpanel.local:8090' with your actual origin
echo 4. Restart CyberPanel services
echo 5. Access CyberPanel and go to User Management to enable WebAuthn
echo.
echo Features available:
echo - Passkey registration and management
echo - Passwordless login option
echo - Multiple device support
echo - Admin management interface
echo.
echo For more information, see: C:\usr\local\CyberCP\to-do\WEBAUTHN_IMPLEMENTATION.md
echo.
pause